From 490618e75bd250c332aa68693bc5fbc779c55354 Mon Sep 17 00:00:00 2001 From: rodolfobacuinjr Date: Mon, 28 Nov 2022 10:26:36 +0800 Subject: [PATCH 01/86] add assets and configure assets directory in pubspec.yaml --- unit2/.gitignore | 44 ++ unit2/.metadata | 45 ++ unit2/README.md | 16 + unit2/analysis_options.yaml | 29 + unit2/android/.gitignore | 13 + unit2/android/app/build.gradle | 71 +++ .../android/app/src/debug/AndroidManifest.xml | 8 + .../android/app/src/main/AndroidManifest.xml | 34 ++ .../kotlin/com/example/unit2/MainActivity.kt | 6 + .../res/drawable-v21/launch_background.xml | 12 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 18 + .../app/src/main/res/values/styles.xml | 18 + .../app/src/profile/AndroidManifest.xml | 8 + unit2/android/build.gradle | 31 + unit2/android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + unit2/android/settings.gradle | 11 + unit2/assets/fonts/LexendDeca-Bold.ttf | Bin 0 -> 78128 bytes unit2/assets/fonts/LexendDeca-Light.ttf | Bin 0 -> 78200 bytes unit2/assets/fonts/LexendDeca-Medium.ttf | Bin 0 -> 78216 bytes unit2/assets/fonts/LexendDeca-Regular.ttf | Bin 0 -> 77904 bytes unit2/assets/fonts/LexendDeca-SemiBold.ttf | Bin 0 -> 78272 bytes .../fonts/LexendDeca-VariableFont_wght.ttf | Bin 0 -> 174048 bytes unit2/assets/pngs/qr-scan.png | Bin 0 -> 18267 bytes unit2/assets/svgs/assign.svg | 1 + unit2/assets/svgs/download.svg | 1 + unit2/assets/svgs/emergency.png | Bin 0 -> 163354 bytes unit2/assets/svgs/empty.svg | 1 + unit2/assets/svgs/error.svg | 1 + unit2/assets/svgs/female.svg | 1 + unit2/assets/svgs/logo.svg | 108 ++++ unit2/assets/svgs/male.svg | 1 + unit2/assets/svgs/settings.svg | 1 + unit2/assets/svgs/sos.svg | 1 + unit2/assets/svgs/timeout.svg | 1 + unit2/assets/svgs/welcome.svg | 1 + unit2/assets/svgs/workspace.svg | 1 + unit2/ios/.gitignore | 34 ++ unit2/ios/Flutter/AppFrameworkInfo.plist | 26 + unit2/ios/Flutter/Debug.xcconfig | 1 + unit2/ios/Flutter/Release.xcconfig | 1 + unit2/ios/Runner.xcodeproj/project.pbxproj | 481 +++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 87 +++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + unit2/ios/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 122 ++++ .../Icon-App-1024x1024@1x.png | Bin 0 -> 10932 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 564 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 1588 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 1025 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 1716 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 1920 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 1895 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 3831 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 1888 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 3294 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 3612 bytes .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/README.md | 5 + .../Runner/Base.lproj/LaunchScreen.storyboard | 37 ++ unit2/ios/Runner/Base.lproj/Main.storyboard | 26 + unit2/ios/Runner/Info.plist | 51 ++ unit2/ios/Runner/Runner-Bridging-Header.h | 1 + unit2/lib/main.dart | 115 ++++ unit2/linux/.gitignore | 1 + unit2/linux/CMakeLists.txt | 138 +++++ unit2/linux/flutter/CMakeLists.txt | 88 +++ .../flutter/generated_plugin_registrant.cc | 11 + .../flutter/generated_plugin_registrant.h | 15 + unit2/linux/flutter/generated_plugins.cmake | 23 + unit2/linux/main.cc | 6 + unit2/linux/my_application.cc | 104 ++++ unit2/linux/my_application.h | 18 + unit2/macos/.gitignore | 7 + unit2/macos/Flutter/Flutter-Debug.xcconfig | 1 + unit2/macos/Flutter/Flutter-Release.xcconfig | 1 + .../Flutter/GeneratedPluginRegistrant.swift | 10 + unit2/macos/Runner.xcodeproj/project.pbxproj | 572 ++++++++++++++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 87 +++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + unit2/macos/Runner/AppDelegate.swift | 9 + .../AppIcon.appiconset/Contents.json | 68 +++ .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 102994 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 5680 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 520 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 14142 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1066 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 36406 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 2218 bytes unit2/macos/Runner/Base.lproj/MainMenu.xib | 343 +++++++++++ unit2/macos/Runner/Configs/AppInfo.xcconfig | 14 + unit2/macos/Runner/Configs/Debug.xcconfig | 2 + unit2/macos/Runner/Configs/Release.xcconfig | 2 + unit2/macos/Runner/Configs/Warnings.xcconfig | 13 + unit2/macos/Runner/DebugProfile.entitlements | 12 + unit2/macos/Runner/Info.plist | 32 + unit2/macos/Runner/MainFlutterWindow.swift | 15 + unit2/macos/Runner/Release.entitlements | 8 + unit2/pubspec.lock | 160 +++++ unit2/pubspec.yaml | 93 +++ unit2/test/widget_test.dart | 30 + unit2/web/favicon.png | Bin 0 -> 917 bytes unit2/web/icons/Icon-192.png | Bin 0 -> 5292 bytes unit2/web/icons/Icon-512.png | Bin 0 -> 8252 bytes unit2/web/icons/Icon-maskable-192.png | Bin 0 -> 5594 bytes unit2/web/icons/Icon-maskable-512.png | Bin 0 -> 20998 bytes unit2/web/index.html | 58 ++ unit2/web/manifest.json | 35 ++ unit2/windows/.gitignore | 17 + unit2/windows/CMakeLists.txt | 101 ++++ unit2/windows/flutter/CMakeLists.txt | 104 ++++ .../flutter/generated_plugin_registrant.cc | 11 + .../flutter/generated_plugin_registrant.h | 15 + unit2/windows/flutter/generated_plugins.cmake | 23 + unit2/windows/runner/CMakeLists.txt | 39 ++ unit2/windows/runner/Runner.rc | 121 ++++ unit2/windows/runner/flutter_window.cpp | 61 ++ unit2/windows/runner/flutter_window.h | 33 + unit2/windows/runner/main.cpp | 43 ++ unit2/windows/runner/resource.h | 16 + unit2/windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes unit2/windows/runner/runner.exe.manifest | 20 + unit2/windows/runner/utils.cpp | 64 ++ unit2/windows/runner/utils.h | 19 + unit2/windows/runner/win32_window.cpp | 245 ++++++++ unit2/windows/runner/win32_window.h | 98 +++ 146 files changed, 4601 insertions(+) create mode 100644 unit2/.gitignore create mode 100644 unit2/.metadata create mode 100644 unit2/README.md create mode 100644 unit2/analysis_options.yaml create mode 100644 unit2/android/.gitignore create mode 100644 unit2/android/app/build.gradle create mode 100644 unit2/android/app/src/debug/AndroidManifest.xml create mode 100644 unit2/android/app/src/main/AndroidManifest.xml create mode 100644 unit2/android/app/src/main/kotlin/com/example/unit2/MainActivity.kt create mode 100644 unit2/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 unit2/android/app/src/main/res/drawable/launch_background.xml create mode 100644 unit2/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 unit2/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 unit2/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 unit2/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 unit2/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 unit2/android/app/src/main/res/values-night/styles.xml create mode 100644 unit2/android/app/src/main/res/values/styles.xml create mode 100644 unit2/android/app/src/profile/AndroidManifest.xml create mode 100644 unit2/android/build.gradle create mode 100644 unit2/android/gradle.properties create mode 100644 unit2/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 unit2/android/settings.gradle create mode 100644 unit2/assets/fonts/LexendDeca-Bold.ttf create mode 100644 unit2/assets/fonts/LexendDeca-Light.ttf create mode 100644 unit2/assets/fonts/LexendDeca-Medium.ttf create mode 100644 unit2/assets/fonts/LexendDeca-Regular.ttf create mode 100644 unit2/assets/fonts/LexendDeca-SemiBold.ttf create mode 100644 unit2/assets/fonts/LexendDeca-VariableFont_wght.ttf create mode 100644 unit2/assets/pngs/qr-scan.png create mode 100644 unit2/assets/svgs/assign.svg create mode 100644 unit2/assets/svgs/download.svg create mode 100644 unit2/assets/svgs/emergency.png create mode 100644 unit2/assets/svgs/empty.svg create mode 100644 unit2/assets/svgs/error.svg create mode 100644 unit2/assets/svgs/female.svg create mode 100644 unit2/assets/svgs/logo.svg create mode 100644 unit2/assets/svgs/male.svg create mode 100644 unit2/assets/svgs/settings.svg create mode 100644 unit2/assets/svgs/sos.svg create mode 100644 unit2/assets/svgs/timeout.svg create mode 100644 unit2/assets/svgs/welcome.svg create mode 100644 unit2/assets/svgs/workspace.svg create mode 100644 unit2/ios/.gitignore create mode 100644 unit2/ios/Flutter/AppFrameworkInfo.plist create mode 100644 unit2/ios/Flutter/Debug.xcconfig create mode 100644 unit2/ios/Flutter/Release.xcconfig create mode 100644 unit2/ios/Runner.xcodeproj/project.pbxproj create mode 100644 unit2/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 unit2/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 unit2/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 unit2/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 unit2/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 unit2/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 unit2/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 unit2/ios/Runner/AppDelegate.swift create mode 100644 unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 unit2/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 unit2/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 unit2/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 unit2/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 unit2/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 unit2/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 unit2/ios/Runner/Base.lproj/Main.storyboard create mode 100644 unit2/ios/Runner/Info.plist create mode 100644 unit2/ios/Runner/Runner-Bridging-Header.h create mode 100644 unit2/lib/main.dart create mode 100644 unit2/linux/.gitignore create mode 100644 unit2/linux/CMakeLists.txt create mode 100644 unit2/linux/flutter/CMakeLists.txt create mode 100644 unit2/linux/flutter/generated_plugin_registrant.cc create mode 100644 unit2/linux/flutter/generated_plugin_registrant.h create mode 100644 unit2/linux/flutter/generated_plugins.cmake create mode 100644 unit2/linux/main.cc create mode 100644 unit2/linux/my_application.cc create mode 100644 unit2/linux/my_application.h create mode 100644 unit2/macos/.gitignore create mode 100644 unit2/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 unit2/macos/Flutter/Flutter-Release.xcconfig create mode 100644 unit2/macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 unit2/macos/Runner.xcodeproj/project.pbxproj create mode 100644 unit2/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 unit2/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 unit2/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 unit2/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 unit2/macos/Runner/AppDelegate.swift create mode 100644 unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 unit2/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 unit2/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 unit2/macos/Runner/Configs/Debug.xcconfig create mode 100644 unit2/macos/Runner/Configs/Release.xcconfig create mode 100644 unit2/macos/Runner/Configs/Warnings.xcconfig create mode 100644 unit2/macos/Runner/DebugProfile.entitlements create mode 100644 unit2/macos/Runner/Info.plist create mode 100644 unit2/macos/Runner/MainFlutterWindow.swift create mode 100644 unit2/macos/Runner/Release.entitlements create mode 100644 unit2/pubspec.lock create mode 100644 unit2/pubspec.yaml create mode 100644 unit2/test/widget_test.dart create mode 100644 unit2/web/favicon.png create mode 100644 unit2/web/icons/Icon-192.png create mode 100644 unit2/web/icons/Icon-512.png create mode 100644 unit2/web/icons/Icon-maskable-192.png create mode 100644 unit2/web/icons/Icon-maskable-512.png create mode 100644 unit2/web/index.html create mode 100644 unit2/web/manifest.json create mode 100644 unit2/windows/.gitignore create mode 100644 unit2/windows/CMakeLists.txt create mode 100644 unit2/windows/flutter/CMakeLists.txt create mode 100644 unit2/windows/flutter/generated_plugin_registrant.cc create mode 100644 unit2/windows/flutter/generated_plugin_registrant.h create mode 100644 unit2/windows/flutter/generated_plugins.cmake create mode 100644 unit2/windows/runner/CMakeLists.txt create mode 100644 unit2/windows/runner/Runner.rc create mode 100644 unit2/windows/runner/flutter_window.cpp create mode 100644 unit2/windows/runner/flutter_window.h create mode 100644 unit2/windows/runner/main.cpp create mode 100644 unit2/windows/runner/resource.h create mode 100644 unit2/windows/runner/resources/app_icon.ico create mode 100644 unit2/windows/runner/runner.exe.manifest create mode 100644 unit2/windows/runner/utils.cpp create mode 100644 unit2/windows/runner/utils.h create mode 100644 unit2/windows/runner/win32_window.cpp create mode 100644 unit2/windows/runner/win32_window.h diff --git a/unit2/.gitignore b/unit2/.gitignore new file mode 100644 index 0000000..24476c5 --- /dev/null +++ b/unit2/.gitignore @@ -0,0 +1,44 @@ +# 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 +.packages +.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/unit2/.metadata b/unit2/.metadata new file mode 100644 index 0000000..3fdf532 --- /dev/null +++ b/unit2/.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. + +version: + revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 + channel: stable + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 + base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 + - platform: android + create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 + base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 + - platform: ios + create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 + base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 + - platform: linux + create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 + base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 + - platform: macos + create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 + base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 + - platform: web + create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 + base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 + - platform: windows + create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 + base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 + + # 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/unit2/README.md b/unit2/README.md new file mode 100644 index 0000000..9a223c4 --- /dev/null +++ b/unit2/README.md @@ -0,0 +1,16 @@ +# unit2 + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/unit2/analysis_options.yaml b/unit2/analysis_options.yaml new file mode 100644 index 0000000..61b6c4d --- /dev/null +++ b/unit2/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/unit2/android/.gitignore b/unit2/android/.gitignore new file mode 100644 index 0000000..6f56801 --- /dev/null +++ b/unit2/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/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/unit2/android/app/build.gradle b/unit2/android/app/build.gradle new file mode 100644 index 0000000..149c584 --- /dev/null +++ b/unit2/android/app/build.gradle @@ -0,0 +1,71 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion flutter.compileSdkVersion + ndkVersion flutter.ndkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.example.unit2" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. + minSdkVersion flutter.minSdkVersion + targetSdkVersion flutter.targetSdkVersion + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} diff --git a/unit2/android/app/src/debug/AndroidManifest.xml b/unit2/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..f26dc6f --- /dev/null +++ b/unit2/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + diff --git a/unit2/android/app/src/main/AndroidManifest.xml b/unit2/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..9e1ccf9 --- /dev/null +++ b/unit2/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + diff --git a/unit2/android/app/src/main/kotlin/com/example/unit2/MainActivity.kt b/unit2/android/app/src/main/kotlin/com/example/unit2/MainActivity.kt new file mode 100644 index 0000000..ecbd330 --- /dev/null +++ b/unit2/android/app/src/main/kotlin/com/example/unit2/MainActivity.kt @@ -0,0 +1,6 @@ +package com.example.unit2 + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/unit2/android/app/src/main/res/drawable-v21/launch_background.xml b/unit2/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/unit2/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/unit2/android/app/src/main/res/drawable/launch_background.xml b/unit2/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/unit2/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/unit2/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/unit2/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/unit2/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/unit2/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/unit2/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/unit2/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/unit2/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/unit2/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/unit2/android/app/src/main/res/values-night/styles.xml b/unit2/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..06952be --- /dev/null +++ b/unit2/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/unit2/android/app/src/main/res/values/styles.xml b/unit2/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..cb1ef88 --- /dev/null +++ b/unit2/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/unit2/android/app/src/profile/AndroidManifest.xml b/unit2/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..f26dc6f --- /dev/null +++ b/unit2/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + diff --git a/unit2/android/build.gradle b/unit2/android/build.gradle new file mode 100644 index 0000000..83ae220 --- /dev/null +++ b/unit2/android/build.gradle @@ -0,0 +1,31 @@ +buildscript { + ext.kotlin_version = '1.6.10' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.1.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/unit2/android/gradle.properties b/unit2/android/gradle.properties new file mode 100644 index 0000000..94adc3a --- /dev/null +++ b/unit2/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/unit2/android/gradle/wrapper/gradle-wrapper.properties b/unit2/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..cb24abd --- /dev/null +++ b/unit2/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.4-all.zip diff --git a/unit2/android/settings.gradle b/unit2/android/settings.gradle new file mode 100644 index 0000000..44e62bc --- /dev/null +++ b/unit2/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/unit2/assets/fonts/LexendDeca-Bold.ttf b/unit2/assets/fonts/LexendDeca-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..f373a368a1ae31487ddddd1c715823e1ae80c489 GIT binary patch literal 78128 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 literal 0 HcmV?d00001 diff --git a/unit2/assets/fonts/LexendDeca-Light.ttf b/unit2/assets/fonts/LexendDeca-Light.ttf new file mode 100644 index 0000000000000000000000000000000000000000..df16a79944ca046b97add2d7a97f5511d5840823 GIT binary patch literal 78200 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 M%G@{ue6P9xAN*Dhr2qf` literal 0 HcmV?d00001 diff --git a/unit2/assets/fonts/LexendDeca-Regular.ttf b/unit2/assets/fonts/LexendDeca-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..736bed8ecad7d4bd355e04601b38112b98f8ac46 GIT binary patch literal 77904 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

@1=gI_JCcw*Udn>>Q$*ZAIqIqkw>BwiS>RYBkA^ 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 )K^GrSG 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

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unit2/macos/Runner/Configs/AppInfo.xcconfig b/unit2/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 0000000..53a76c1 --- /dev/null +++ b/unit2/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 = unit2 + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.unit2 + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2022 com.example. All rights reserved. diff --git a/unit2/macos/Runner/Configs/Debug.xcconfig b/unit2/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 0000000..36b0fd9 --- /dev/null +++ b/unit2/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/unit2/macos/Runner/Configs/Release.xcconfig b/unit2/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 0000000..dff4f49 --- /dev/null +++ b/unit2/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/unit2/macos/Runner/Configs/Warnings.xcconfig b/unit2/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 0000000..42bcbf4 --- /dev/null +++ b/unit2/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/unit2/macos/Runner/DebugProfile.entitlements b/unit2/macos/Runner/DebugProfile.entitlements new file mode 100644 index 0000000..dddb8a3 --- /dev/null +++ b/unit2/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/unit2/macos/Runner/Info.plist b/unit2/macos/Runner/Info.plist new file mode 100644 index 0000000..4789daa --- /dev/null +++ b/unit2/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/unit2/macos/Runner/MainFlutterWindow.swift b/unit2/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 0000000..2722837 --- /dev/null +++ b/unit2/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController.init() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/unit2/macos/Runner/Release.entitlements b/unit2/macos/Runner/Release.entitlements new file mode 100644 index 0000000..852fa1a --- /dev/null +++ b/unit2/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/unit2/pubspec.lock b/unit2/pubspec.lock new file mode 100644 index 0000000..56c9d41 --- /dev/null +++ b/unit2/pubspec.lock @@ -0,0 +1,160 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.9.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.16.0" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.5" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + lints: + dependency: transitive + description: + name: lints + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.12" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.5" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.2" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.12" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" +sdks: + dart: ">=2.18.5 <3.0.0" diff --git a/unit2/pubspec.yaml b/unit2/pubspec.yaml new file mode 100644 index 0000000..543fda5 --- /dev/null +++ b/unit2/pubspec.yaml @@ -0,0 +1,93 @@ +name: unit2 +description: A new Flutter project. + +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# In Windows, build-name is used as the major, minor, and patch parts +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 + +environment: + sdk: '>=2.18.5 <3.0.0' + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + assets: + - assets/fonts + - assets/pngs + - assets/svgs + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/unit2/test/widget_test.dart b/unit2/test/widget_test.dart new file mode 100644 index 0000000..831a007 --- /dev/null +++ b/unit2/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:unit2/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/unit2/web/favicon.png b/unit2/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/unit2/web/icons/Icon-192.png b/unit2/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 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 literal 0 HcmV?d00001 diff --git a/unit2/web/icons/Icon-512.png b/unit2/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 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 literal 0 HcmV?d00001 diff --git a/unit2/web/icons/Icon-maskable-192.png b/unit2/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000000000000000000000000000000000000..eb9b4d76e525556d5d89141648c724331630325d GIT binary patch literal 5594 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 X>c!)3PgKfL&lX^$Z>Cpu&6)6jvi^Z! literal 0 HcmV?d00001 diff --git a/unit2/web/icons/Icon-maskable-512.png b/unit2/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000000000000000000000000000000000000..d69c56691fbdb0b7efa65097c7cc1edac12a6d3e GIT binary patch literal 20998 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 MPgg&ebxsLQ025IeI{*Lx literal 0 HcmV?d00001 diff --git a/unit2/web/index.html b/unit2/web/index.html new file mode 100644 index 0000000..10f6b60 --- /dev/null +++ b/unit2/web/index.html @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + unit2 + + + + + + + + + + diff --git a/unit2/web/manifest.json b/unit2/web/manifest.json new file mode 100644 index 0000000..9298dae --- /dev/null +++ b/unit2/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "unit2", + "short_name": "unit2", + "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/unit2/windows/.gitignore b/unit2/windows/.gitignore new file mode 100644 index 0000000..d492d0d --- /dev/null +++ b/unit2/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/unit2/windows/CMakeLists.txt b/unit2/windows/CMakeLists.txt new file mode 100644 index 0000000..7eae244 --- /dev/null +++ b/unit2/windows/CMakeLists.txt @@ -0,0 +1,101 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(unit2 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 "unit2") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# 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() + +# 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/unit2/windows/flutter/CMakeLists.txt b/unit2/windows/flutter/CMakeLists.txt new file mode 100644 index 0000000..930d207 --- /dev/null +++ b/unit2/windows/flutter/CMakeLists.txt @@ -0,0 +1,104 @@ +# 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") + +# === 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" + windows-x64 $ + 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/unit2/windows/flutter/generated_plugin_registrant.cc b/unit2/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000..8b6d468 --- /dev/null +++ b/unit2/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,11 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + + +void RegisterPlugins(flutter::PluginRegistry* registry) { +} diff --git a/unit2/windows/flutter/generated_plugin_registrant.h b/unit2/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000..dc139d8 --- /dev/null +++ b/unit2/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/unit2/windows/flutter/generated_plugins.cmake b/unit2/windows/flutter/generated_plugins.cmake new file mode 100644 index 0000000..b93c4c3 --- /dev/null +++ b/unit2/windows/flutter/generated_plugins.cmake @@ -0,0 +1,23 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +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/unit2/windows/runner/CMakeLists.txt b/unit2/windows/runner/CMakeLists.txt new file mode 100644 index 0000000..17411a8 --- /dev/null +++ b/unit2/windows/runner/CMakeLists.txt @@ -0,0 +1,39 @@ +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_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/unit2/windows/runner/Runner.rc b/unit2/windows/runner/Runner.rc new file mode 100644 index 0000000..a51295b --- /dev/null +++ b/unit2/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", "unit2" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "unit2" "\0" + VALUE "LegalCopyright", "Copyright (C) 2022 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "unit2.exe" "\0" + VALUE "ProductName", "unit2" "\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/unit2/windows/runner/flutter_window.cpp b/unit2/windows/runner/flutter_window.cpp new file mode 100644 index 0000000..b43b909 --- /dev/null +++ b/unit2/windows/runner/flutter_window.cpp @@ -0,0 +1,61 @@ +#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()); + 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/unit2/windows/runner/flutter_window.h b/unit2/windows/runner/flutter_window.h new file mode 100644 index 0000000..6da0652 --- /dev/null +++ b/unit2/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/unit2/windows/runner/main.cpp b/unit2/windows/runner/main.cpp new file mode 100644 index 0000000..f8b1c55 --- /dev/null +++ b/unit2/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.CreateAndShow(L"unit2", 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/unit2/windows/runner/resource.h b/unit2/windows/runner/resource.h new file mode 100644 index 0000000..66a65d1 --- /dev/null +++ b/unit2/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/unit2/windows/runner/resources/app_icon.ico b/unit2/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/unit2/windows/runner/runner.exe.manifest b/unit2/windows/runner/runner.exe.manifest new file mode 100644 index 0000000..a42ea76 --- /dev/null +++ b/unit2/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/unit2/windows/runner/utils.cpp b/unit2/windows/runner/utils.cpp new file mode 100644 index 0000000..f5bf9fa --- /dev/null +++ b/unit2/windows/runner/utils.cpp @@ -0,0 +1,64 @@ +#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(); + } + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr); + 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, + -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/unit2/windows/runner/utils.h b/unit2/windows/runner/utils.h new file mode 100644 index 0000000..3879d54 --- /dev/null +++ b/unit2/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/unit2/windows/runner/win32_window.cpp b/unit2/windows/runner/win32_window.cpp new file mode 100644 index 0000000..c10f08d --- /dev/null +++ b/unit2/windows/runner/win32_window.cpp @@ -0,0 +1,245 @@ +#include "win32_window.h" + +#include + +#include "resource.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// 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 registar 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::CreateAndShow(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 | WS_VISIBLE, + 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; + } + + return OnCreate(); +} + +// 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; + } + + 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. +} diff --git a/unit2/windows/runner/win32_window.h b/unit2/windows/runner/win32_window.h new file mode 100644 index 0000000..17ba431 --- /dev/null +++ b/unit2/windows/runner/win32_window.h @@ -0,0 +1,98 @@ +#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 and shows a win32 window with |title| and position and size 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 to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size); + + // 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 + // responsponds 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; + + 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_ From 20707745bb1cdc41f71c62d0a34f2ad7d0942f83 Mon Sep 17 00:00:00 2001 From: rodolfobacuinjr Date: Mon, 28 Nov 2022 16:03:13 +0800 Subject: [PATCH 02/86] create user interface for login screen --- unit2/lib/main.dart | 130 ++------ .../unit2/homepage.dart/module-screen.dart | 16 + .../login/components/login-via-qr-label.dart | 38 +++ .../login/functions/press-again-to-exit.dart | 20 ++ unit2/lib/screen/unit2/login/login.dart | 247 +++++++++++++++ unit2/lib/theme-data.dart/btn-style.dart | 15 + unit2/lib/theme-data.dart/colors.dart | 10 + unit2/lib/theme-data.dart/form-style.dart | 40 +++ unit2/lib/utils/global.dart | 8 + unit2/lib/utils/router.dart | 11 + unit2/lib/widgets/wave.dart | 36 +++ .../Flutter/GeneratedPluginRegistrant.swift | 2 + unit2/pubspec.lock | 284 ++++++++++++++++++ unit2/pubspec.yaml | 38 ++- 14 files changed, 783 insertions(+), 112 deletions(-) create mode 100644 unit2/lib/screen/unit2/homepage.dart/module-screen.dart create mode 100644 unit2/lib/screen/unit2/login/components/login-via-qr-label.dart create mode 100644 unit2/lib/screen/unit2/login/functions/press-again-to-exit.dart create mode 100644 unit2/lib/screen/unit2/login/login.dart create mode 100644 unit2/lib/theme-data.dart/btn-style.dart create mode 100644 unit2/lib/theme-data.dart/colors.dart create mode 100644 unit2/lib/theme-data.dart/form-style.dart create mode 100644 unit2/lib/utils/global.dart create mode 100644 unit2/lib/utils/router.dart create mode 100644 unit2/lib/widgets/wave.dart diff --git a/unit2/lib/main.dart b/unit2/lib/main.dart index e016029..7bfa0d4 100644 --- a/unit2/lib/main.dart +++ b/unit2/lib/main.dart @@ -1,115 +1,49 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:device_preview/device_preview.dart'; +import './utils/router.dart'; +import './utils/global.dart'; void main() { runApp(const MyApp()); } +// void main() => runApp( +// DevicePreview( +// enabled: !kReleaseMode, +// builder: (context) => const MyApp(), // Wrap your app +// ), +// ); + class MyApp extends StatelessWidget { const MyApp({super.key}); // This widget is the root of your application. @override Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Demo', + final mediaQueryData = + MediaQueryData.fromWindow(WidgetsBinding.instance.window); + screenWidth = mediaQueryData.size.width; + screenHeight = mediaQueryData.size.height; + blockSizeHorizontal = screenWidth / 100; + blockSizeVertical = screenHeight / 100; + safeAreaHorizontal = + mediaQueryData.padding.left + mediaQueryData.padding.right; + safeAreaVertical = + mediaQueryData.padding.top + mediaQueryData.padding.bottom; + safeBlockHorizontal = (screenWidth - safeAreaHorizontal) / 100; + safeBlockVertical = (screenHeight - safeAreaVertical) / 100; + return MaterialApp.router( + // useInheritedMediaQuery: true, + // locale: DevicePreview.locale(context), + // builder: DevicePreview.appBuilder, + routeInformationParser: goRouter.routeInformationParser, + routerDelegate: goRouter.routerDelegate, + title: 'uniT2 - Universal Tracker and Tracer', theme: ThemeData( - // This is the theme of your application. - // - // Try running your application with "flutter run". You'll see the - // application has a blue toolbar. Then, without quitting the app, try - // changing the primarySwatch below to Colors.green and then invoke - // "hot reload" (press "r" in the console where you ran "flutter run", - // or simply save your changes to "hot reload" in a Flutter IDE). - // Notice that the counter didn't reset back to zero; the application - // is not restarted. - primarySwatch: Colors.blue, + fontFamily: 'LexendDeca', ), - home: const MyHomePage(title: 'Flutter Demo Home Page'), - ); - } -} - -class MyHomePage extends StatefulWidget { - const MyHomePage({super.key, required this.title}); - - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - - final String title; - - @override - State createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _counter++; - }); - } - - @override - Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. - return Scaffold( - appBar: AppBar( - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(widget.title), - ), - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: Column( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Invoke "debug painting" (press "p" in the console, choose the - // "Toggle Debug Paint" action from the Flutter Inspector in Android - // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) - // to see the wireframe for each widget. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'You have pushed the button this many times:', - ), - Text( - '$_counter', - style: Theme.of(context).textTheme.headline4, - ), - ], - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. + debugShowCheckedModeBanner: false, ); } } diff --git a/unit2/lib/screen/unit2/homepage.dart/module-screen.dart b/unit2/lib/screen/unit2/homepage.dart/module-screen.dart new file mode 100644 index 0000000..f55a9c9 --- /dev/null +++ b/unit2/lib/screen/unit2/homepage.dart/module-screen.dart @@ -0,0 +1,16 @@ +import 'package:flutter/src/widgets/container.dart'; +import 'package:flutter/src/widgets/framework.dart'; + +class ModuleScreen extends StatefulWidget { + const ModuleScreen({super.key}); + + @override + State createState() => _ModuleScreenState(); +} + +class _ModuleScreenState extends State { + @override + Widget build(BuildContext context) { + return Container(); + } +} \ No newline at end of file diff --git a/unit2/lib/screen/unit2/login/components/login-via-qr-label.dart b/unit2/lib/screen/unit2/login/components/login-via-qr-label.dart new file mode 100644 index 0000000..e74183e --- /dev/null +++ b/unit2/lib/screen/unit2/login/components/login-via-qr-label.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; + +import '../../../../utils/global.dart'; + +class LoginViaQr extends StatelessWidget { + final String text; + const LoginViaQr({Key? key,@required required this.text}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SizedBox( + height: blockSizeVertical * 3, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox( + width: 30, + child: Divider( + color: Colors.grey, + height: 2, + ), + ), + // LOGIN VIA QR CODE + Text( + text, + style: const TextStyle(fontSize: 12), + ), + const SizedBox( + width: 30, + child: Divider( + color: Colors.grey, + height: 2, + ), + ), + ], + )); + } +} diff --git a/unit2/lib/screen/unit2/login/functions/press-again-to-exit.dart b/unit2/lib/screen/unit2/login/functions/press-again-to-exit.dart new file mode 100644 index 0000000..d430674 --- /dev/null +++ b/unit2/lib/screen/unit2/login/functions/press-again-to-exit.dart @@ -0,0 +1,20 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:fluttertoast/fluttertoast.dart'; + +Future pressAgainToExit() async { + DateTime? currentBackPressTime; + DateTime now = DateTime.now(); + if (currentBackPressTime == null || + now.difference(currentBackPressTime) > const Duration(seconds: 1)) { + currentBackPressTime = now; + Fluttertoast.showToast( + msg: "Press again to exit", + gravity: ToastGravity.CENTER, + backgroundColor: Colors.black); + return Future.value(false); + } + return true; +} diff --git a/unit2/lib/screen/unit2/login/login.dart b/unit2/lib/screen/unit2/login/login.dart new file mode 100644 index 0000000..4dc993e --- /dev/null +++ b/unit2/lib/screen/unit2/login/login.dart @@ -0,0 +1,247 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:fluttericon/font_awesome5_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import '../../../widgets/wave.dart'; +import '../../../utils/global.dart'; +import '../../../theme-data.dart/colors.dart'; +import '../../../theme-data.dart/form-style.dart'; +import '../../../theme-data.dart/btn-style.dart'; +import './components/login-via-qr-label.dart'; +import './functions/press-again-to-exit.dart'; + +class UniT2Login extends StatefulWidget { + const UniT2Login({super.key}); + + @override + State createState() => _UniT2LoginState(); +} + +class _UniT2LoginState extends State { + final _formKey = GlobalKey(); + bool showSuffixIcon = false; + bool _showPassword = true; + @override + Widget build(BuildContext context) { + return WillPopScope( + onWillPop: pressAgainToExit, + child: Scaffold( + body: SizedBox( + child: SingleChildScrollView( + child: Stack( + children: [ + Positioned( + bottom: 0, + right: 0, + child: WaveReverse(height: blockSizeVertical * 7)), + SizedBox( + height: blockSizeVertical * 100, + child: FormBuilder( + key: _formKey, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 25), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(height: blockSizeVertical * 7), + SvgPicture.asset( + 'assets/svgs/logo.svg', + height: blockSizeVertical * 16, + allowDrawingOutsideViewBox: true, + color: primary, + ), + + Text( + "Welcome to!", + style: TextStyle( + fontSize: blockSizeVertical * 5, + fontWeight: FontWeight.w600), + ), + Text("uniT-App", + style: TextStyle( + fontSize: blockSizeVertical * 8, + fontWeight: FontWeight.w800, + letterSpacing: .2, + height: 1, + color: primary)), + Text( + "Please login to continue.", + style: TextStyle( + fontSize: blockSizeVertical * 2, + height: 1.5, + fontWeight: FontWeight.w600), + ), + SizedBox( + height: blockSizeVertical * 1.5, + ), + // USERNAME + FormBuilderTextField( + name: 'username', + validator: FormBuilderValidators.compose([ + FormBuilderValidators.required( + errorText: "Username is required") + ]), + autofocus: false, + style: const TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black87), + decoration: loginTextFieldStyle()), + SizedBox( + height: blockSizeVertical * 1.5, + ), + // PASSWORD + FormBuilderTextField( + name: 'password', + validator: FormBuilderValidators.compose([ + FormBuilderValidators.required( + errorText: "Password is required") + ]), + // initialValue: state.password, + onChanged: (value) { + value!.isEmpty + ? setState(() { + showSuffixIcon = false; + }) + : setState(() { + showSuffixIcon = true; + }); + }, + autofocus: false, + style: const TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black87), + decoration: loginTextFieldStyle().copyWith( + suffixIcon: Visibility( + visible: showSuffixIcon, + child: _showPassword + ? IconButton( + icon: Icon(FontAwesome5.eye_slash, + size: 24, + color: Theme.of(context) + .textTheme + .displayLarge + ?.color), + onPressed: () { + setState(() { + _showPassword = false; + }); + }, + ) + : IconButton( + onPressed: () { + setState(() { + _showPassword = true; + }); + }, + icon: Icon(FontAwesome5.eye, + size: 24, + color: Theme.of(context) + .textTheme + .displayLarge + ?.color)), + ), + prefixIcon: const Icon(Icons.lock), + labelText: "Password", + hintText: "Enter Password..."), + obscureText: _showPassword ? true : false, + ), + SizedBox( + height: blockSizeVertical * 2, + ), + SizedBox( + height: blockSizeVertical * 7, + // Login Button + child: SizedBox( + width: MediaQuery.of(context).size.width, + child: ElevatedButton( + style: btnStyle( + second, Colors.transparent, Colors.white54), + child: const Text( + "LOGIN", + style: TextStyle(color: Colors.white), + ), + onPressed: () async { + if (_formKey.currentState! + .saveAndValidate()) { + debugPrint( + _formKey.currentState!.value['name']); + debugPrint(_formKey + .currentState!.value['password']); + } + // if (_formKey.currentState.validate()) { + // _formKey.currentState.save(); + // BlocProvider.of(context) + // .add(UserWebLogin( + // password: password, + // username: username)); + // } + }, + ), + ), + ), + SizedBox( + height: blockSizeVertical * 1.5, + ), + + SizedBox( + height: blockSizeVertical * 7, + child: SizedBox( + width: MediaQuery.of(context).size.width, + child: ElevatedButton.icon( + style: btnStyle(Colors.white, second, + primary.withOpacity(.4)), + icon: const Icon( + Icons.qr_code, + color: second, + ), + label: const Text( + "Login via QR code", + style: TextStyle(color: second), + ), + onPressed: () async { + // BlocProvider.of(context) + // .add(QRCodelogin()); + }, + ), + )), + SizedBox( + height: blockSizeVertical * 1, + ), + const LoginViaQr( + text: " Request Emergency Response "), + SizedBox( + height: blockSizeVertical * 1, + ), + // REQUEST SOS + SizedBox( + height: screenHeight * .07, + width: MediaQuery.of(context).size.width, + child: ElevatedButton.icon( + icon: const Icon( + FontAwesome5.life_ring, + color: Colors.white, + ), + style: btnStyle( + third, Colors.transparent, Colors.white38), + onPressed: () { + Navigator.pushNamed(context, '/SosScreen'); + }, + label: const Text( + "Request SOS", + style: TextStyle(color: Colors.white), + )), + ) + ], + ), + ), + ), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/unit2/lib/theme-data.dart/btn-style.dart b/unit2/lib/theme-data.dart/btn-style.dart new file mode 100644 index 0000000..998f54d --- /dev/null +++ b/unit2/lib/theme-data.dart/btn-style.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; + +ButtonStyle btnStyle(Color background, Color borderColor, Color overlay) { + return ButtonStyle( + elevation: MaterialStateProperty.all(0), + backgroundColor: MaterialStateProperty.all(background), + overlayColor: MaterialStateProperty.all(overlay), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(25.0), + side: BorderSide( + width: 2, + color: borderColor, + )))); +} diff --git a/unit2/lib/theme-data.dart/colors.dart b/unit2/lib/theme-data.dart/colors.dart new file mode 100644 index 0000000..219a3fd --- /dev/null +++ b/unit2/lib/theme-data.dart/colors.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +const primary = Color(0xff99191a); +const second = Color(0xffd92828); +const third = Color(0xffeb8b1e); +const fourth = Color(0xfff6c359); +const fifth = Color(0xfffdfefd); +const success = Color(0xffa5d6a7); +const success2 = Color(0xff66bb6a); +const primary2 = Color(0xff039be5); diff --git a/unit2/lib/theme-data.dart/form-style.dart b/unit2/lib/theme-data.dart/form-style.dart new file mode 100644 index 0000000..2f339a1 --- /dev/null +++ b/unit2/lib/theme-data.dart/form-style.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; + +InputDecoration loginTextFieldStyle() { + return InputDecoration( + floatingLabelBehavior: FloatingLabelBehavior.never, + prefixIcon: const Icon( + Icons.person, + ), + labelText: 'Username', + hintText: 'Enter your username...', + focusedBorder: OutlineInputBorder( + borderSide: const BorderSide( + width: 2, + color: Colors.black87, + ), + borderRadius: BorderRadius.circular(5), + ), + enabledBorder: OutlineInputBorder( + borderSide: const BorderSide( + color: Colors.black87, + width: 1, + ), + borderRadius: BorderRadius.circular(5), + ), + errorBorder: const OutlineInputBorder( + borderSide: BorderSide( + color: Colors.red, + width: 2, + ), + borderRadius: BorderRadius.all(Radius.circular(5)), + ), + focusedErrorBorder: const OutlineInputBorder( + borderSide: BorderSide( + color: Colors.red, + width: 2, + ), + borderRadius: BorderRadius.all(Radius.circular(5)), + ), + filled: false); +} \ No newline at end of file diff --git a/unit2/lib/utils/global.dart b/unit2/lib/utils/global.dart new file mode 100644 index 0000000..ccbabf2 --- /dev/null +++ b/unit2/lib/utils/global.dart @@ -0,0 +1,8 @@ +double screenWidth = 0; +double screenHeight = 0; +double blockSizeHorizontal = 0; +double blockSizeVertical = 0; +double safeAreaHorizontal = 0; +double safeAreaVertical = 0; +double safeBlockHorizontal = 0; +double safeBlockVertical = 0; \ No newline at end of file diff --git a/unit2/lib/utils/router.dart b/unit2/lib/utils/router.dart new file mode 100644 index 0000000..98a746a --- /dev/null +++ b/unit2/lib/utils/router.dart @@ -0,0 +1,11 @@ +import 'package:go_router/go_router.dart'; +import '../screen/unit2/login/login.dart'; + + +final GoRouter goRouter = GoRouter( + routes: [ + GoRoute(path: '/', + builder: (context,state) => const UniT2Login() + ) + ] +); \ No newline at end of file diff --git a/unit2/lib/widgets/wave.dart b/unit2/lib/widgets/wave.dart new file mode 100644 index 0000000..e78516a --- /dev/null +++ b/unit2/lib/widgets/wave.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_custom_clippers/flutter_custom_clippers.dart'; +import '../theme-data.dart/colors.dart'; + + +class Wave extends StatelessWidget { + final double height; + const Wave({Key? key, required this.height}) : super(key: key); + + @override + Widget build(BuildContext context) { + return ClipPath( + clipper: WaveClipperOne(), + child: Container( + height: height, + width: MediaQuery.of(context).size.width, + color: primary, + )); + } +} + +class WaveReverse extends StatelessWidget { + final double height; + const WaveReverse({Key? key, required this.height}) : super(key: key); + + @override + Widget build(BuildContext context) { + return ClipPath( + clipper: WaveClipperTwo(reverse: true), + child: Container( + height: height, + width: MediaQuery.of(context).size.width, + color: primary, + )); + } +} diff --git a/unit2/macos/Flutter/GeneratedPluginRegistrant.swift b/unit2/macos/Flutter/GeneratedPluginRegistrant.swift index cccf817..287b6a9 100644 --- a/unit2/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/unit2/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,8 @@ import FlutterMacOS import Foundation +import shared_preferences_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) } diff --git a/unit2/pubspec.lock b/unit2/pubspec.lock index 56c9d41..cd07b24 100644 --- a/unit2/pubspec.lock +++ b/unit2/pubspec.lock @@ -43,6 +43,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.5" + device_frame: + dependency: transitive + description: + name: device_frame + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + device_preview: + dependency: "direct main" + description: + name: device_preview + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" fake_async: dependency: transitive description: @@ -50,11 +64,39 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.4" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_custom_clippers: + dependency: "direct main" + description: + name: flutter_custom_clippers + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + flutter_form_builder: + dependency: "direct main" + description: + name: flutter_form_builder + url: "https://pub.dartlang.org" + source: hosted + version: "7.7.0" flutter_lints: dependency: "direct dev" description: @@ -62,11 +104,91 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.1" + flutter_localizations: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_svg: + dependency: "direct main" + description: + name: flutter_svg + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.6" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_zoom_drawer: + dependency: "direct main" + description: + name: flutter_zoom_drawer + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.3" + fluttericon: + dependency: "direct main" + description: + name: fluttericon + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + fluttertoast: + dependency: "direct main" + description: + name: fluttertoast + url: "https://pub.dartlang.org" + source: hosted + version: "8.1.1" + form_builder_validators: + dependency: "direct main" + description: + name: form_builder_validators + url: "https://pub.dartlang.org" + source: hosted + version: "8.4.0" + freezed_annotation: + dependency: transitive + description: + name: freezed_annotation + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" + go_router: + dependency: "direct main" + description: + name: go_router + url: "https://pub.dartlang.org" + source: hosted + version: "5.2.0" + intl: + dependency: transitive + description: + name: intl + url: "https://pub.dartlang.org" + source: hosted + version: "0.17.0" + js: + dependency: transitive + description: + name: js + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.4" + json_annotation: + dependency: transitive + description: + name: json_annotation + url: "https://pub.dartlang.org" + source: hosted + version: "4.7.0" lints: dependency: transitive description: @@ -74,6 +196,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.1" + logging: + dependency: transitive + description: + name: logging + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" matcher: dependency: transitive description: @@ -95,6 +224,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.0" + nested: + dependency: transitive + description: + name: nested + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" path: dependency: transitive description: @@ -102,6 +238,132 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.2" + path_drawing: + dependency: transitive + description: + name: path_drawing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + path_parsing: + dependency: transitive + description: + name: path_parsing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.7" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.3" + petitparser: + dependency: transitive + description: + name: petitparser + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.0" + platform: + dependency: transitive + description: + name: platform + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.3" + process: + dependency: transitive + description: + name: process + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.4" + provider: + dependency: transitive + description: + name: provider + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.4" + shared_preferences: + dependency: transitive + description: + name: shared_preferences + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.15" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.14" + shared_preferences_ios: + dependency: transitive + description: + name: shared_preferences_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + shared_preferences_macos: + dependency: transitive + description: + name: shared_preferences_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" sky_engine: dependency: transitive description: flutter @@ -156,5 +418,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.2" + win32: + dependency: transitive + description: + name: win32 + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.2" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0+2" + xml: + dependency: transitive + description: + name: xml + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.0" sdks: dart: ">=2.18.5 <3.0.0" + flutter: ">=3.3.0" diff --git a/unit2/pubspec.yaml b/unit2/pubspec.yaml index 543fda5..21b322e 100644 --- a/unit2/pubspec.yaml +++ b/unit2/pubspec.yaml @@ -36,6 +36,15 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 + go_router: ^5.2.0 + flutter_custom_clippers: ^2.0.0 + flutter_svg: ^1.1.6 + flutter_form_builder: ^7.7.0 + form_builder_validators: ^8.4.0 + fluttericon: ^2.0.0 + fluttertoast: ^8.1.1 + device_preview: ^1.1.0 + flutter_zoom_drawer: ^3.0.3 dev_dependencies: flutter_test: @@ -61,9 +70,9 @@ flutter: # To add assets to your application, add an assets section, like this: assets: - - assets/fonts - - assets/pngs - - assets/svgs + - assets/svgs/ + - assets/pngs/ + - assets/fonts/ # - images/a_dot_ham.jpeg # An image asset can refer to one or more resolution-specific "variants", see @@ -77,17 +86,18 @@ flutter: # "family" key with the font family name, and a "fonts" key with a # list giving the asset and other descriptors for the font. For # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 + fonts: + - family: LexendDeca + fonts: + - asset: assets/fonts/LexendDeca-Regular.ttf + - asset: assets/fonts/LexendDeca-Light.ttf + weight: 300 + - asset: assets/fonts/LexendDeca-Medium.ttf + weight: 500 + - asset: assets/fonts/LexendDeca-SemiBold.ttf + weight: 600 + - asset: assets/fonts/LexendDeca-Bold.ttf + weight: 700 # # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages From f924111815caf09c8f0fb62fb43af3310a4166a7 Mon Sep 17 00:00:00 2001 From: rodolfobacuinjr Date: Mon, 5 Dec 2022 16:06:45 +0800 Subject: [PATCH 03/86] create user interface for request SOS, profile, and empty module screen --- unit2/assets/pngs/bg.png | Bin 0 -> 62456 bytes unit2/assets/svgs/add_mobile.svg | 1 + unit2/assets/svgs/no_module.svg | 1 + unit2/assets/svgs/request_sos.svg | 1 + unit2/lib/main.dart | 10 +- unit2/lib/screen/sos/add_mobile.dart | 139 +++++++++++++++ unit2/lib/screen/sos/components/mobile.dart | 37 ++++ unit2/lib/screen/sos/request_sos.dart | 109 ++++++++++++ unit2/lib/screen/sos/sos_received.dart | 133 ++++++++++++++ .../components/drawer-screen.dart | 32 ++++ .../components/empty_module.dart | 53 ++++++ .../homepage.dart/components/menu-screen.dart | 75 ++++++++ .../unit2/homepage.dart/components/menu.dart | 25 +++ .../unit2/homepage.dart/module-screen.dart | 53 +++++- unit2/lib/screen/unit2/login/login.dart | 47 +++-- .../unit2/profile/components/cover-image.dart | 20 +++ unit2/lib/screen/unit2/profile/profile.dart | 109 ++++++++++++ unit2/lib/theme-data.dart/btn-style.dart | 17 +- unit2/lib/theme-data.dart/form-style.dart | 50 +++++- unit2/lib/theme-data.dart/text-styles.dart | 7 + unit2/lib/utils/router.dart | 41 ++++- unit2/lib/utils/screen_info.dart | 9 + unit2/lib/utils/text_container.dart | 23 +++ unit2/lib/utils/validators.dart | 12 ++ .../Flutter/GeneratedPluginRegistrant.swift | 4 + unit2/pubspec.lock | 163 +++++++++++++++++- unit2/pubspec.yaml | 6 +- 27 files changed, 1129 insertions(+), 48 deletions(-) create mode 100644 unit2/assets/pngs/bg.png create mode 100644 unit2/assets/svgs/add_mobile.svg create mode 100644 unit2/assets/svgs/no_module.svg create mode 100644 unit2/assets/svgs/request_sos.svg create mode 100644 unit2/lib/screen/sos/add_mobile.dart create mode 100644 unit2/lib/screen/sos/components/mobile.dart create mode 100644 unit2/lib/screen/sos/request_sos.dart create mode 100644 unit2/lib/screen/sos/sos_received.dart create mode 100644 unit2/lib/screen/unit2/homepage.dart/components/drawer-screen.dart create mode 100644 unit2/lib/screen/unit2/homepage.dart/components/empty_module.dart create mode 100644 unit2/lib/screen/unit2/homepage.dart/components/menu-screen.dart create mode 100644 unit2/lib/screen/unit2/homepage.dart/components/menu.dart create mode 100644 unit2/lib/screen/unit2/profile/components/cover-image.dart create mode 100644 unit2/lib/screen/unit2/profile/profile.dart create mode 100644 unit2/lib/theme-data.dart/text-styles.dart create mode 100644 unit2/lib/utils/screen_info.dart create mode 100644 unit2/lib/utils/text_container.dart create mode 100644 unit2/lib/utils/validators.dart diff --git a/unit2/assets/pngs/bg.png b/unit2/assets/pngs/bg.png new file mode 100644 index 0000000000000000000000000000000000000000..b8afe26cef4d94d498bb6b8f552f5c990b2c8b06 GIT binary patch literal 62456 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 wzVsasNdF{ \ No newline at end of file diff --git a/unit2/assets/svgs/no_module.svg b/unit2/assets/svgs/no_module.svg new file mode 100644 index 0000000..7d09b1e --- /dev/null +++ b/unit2/assets/svgs/no_module.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/unit2/assets/svgs/request_sos.svg b/unit2/assets/svgs/request_sos.svg new file mode 100644 index 0000000..f4cd5b8 --- /dev/null +++ b/unit2/assets/svgs/request_sos.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/unit2/lib/main.dart b/unit2/lib/main.dart index 7bfa0d4..f87f8ef 100644 --- a/unit2/lib/main.dart +++ b/unit2/lib/main.dart @@ -1,6 +1,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:device_preview/device_preview.dart'; +import 'package:flutter/services.dart'; import './utils/router.dart'; import './utils/global.dart'; @@ -33,14 +34,21 @@ class MyApp extends StatelessWidget { mediaQueryData.padding.top + mediaQueryData.padding.bottom; safeBlockHorizontal = (screenWidth - safeAreaHorizontal) / 100; safeBlockVertical = (screenHeight - safeAreaVertical) / 100; + return MaterialApp.router( // useInheritedMediaQuery: true, // locale: DevicePreview.locale(context), // builder: DevicePreview.appBuilder, routeInformationParser: goRouter.routeInformationParser, - routerDelegate: goRouter.routerDelegate, + routerDelegate: goRouter.routerDelegate, + // routeInformationProvider: goRouter.routeInformationProvider, title: 'uniT2 - Universal Tracker and Tracer', theme: ThemeData( + appBarTheme: const AppBarTheme( + systemOverlayStyle: SystemUiOverlayStyle( + statusBarBrightness: Brightness.dark, + statusBarColor: Colors.black), + ), fontFamily: 'LexendDeca', ), debugShowCheckedModeBanner: false, diff --git a/unit2/lib/screen/sos/add_mobile.dart b/unit2/lib/screen/sos/add_mobile.dart new file mode 100644 index 0000000..c102a0a --- /dev/null +++ b/unit2/lib/screen/sos/add_mobile.dart @@ -0,0 +1,139 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:go_router/go_router.dart'; +import 'package:unit2/theme-data.dart/text-styles.dart'; +import 'package:unit2/utils/screen_info.dart'; +import '../../theme-data.dart/btn-style.dart'; +import '../../theme-data.dart/colors.dart'; +import '../../theme-data.dart/form-style.dart'; +import '../../utils/global.dart'; +import '../../utils/text_container.dart'; +import '../../utils/validators.dart'; +import '../../widgets/wave.dart'; + +class AddMobile extends StatelessWidget { + AddMobile({super.key}); + final _formKey = GlobalKey(); + @override + Widget build(BuildContext context) { + return WillPopScope( + onWillPop: () async { + return true; + }, + child: Scaffold( + appBar: AppBar( + backgroundColor: Colors.transparent, + elevation: 0, + leading: IconButton( + icon: const Icon( + FontAwesome.left_big, + ), + onPressed: () { + context.go(context.namedLocation('login')); + }, + color: primary, + ), + ), + resizeToAvoidBottomInset: true, + body: SingleChildScrollView( + child: SizedBox( + height: screenHeight * .89, + child: Stack( + children: [ + Positioned( + bottom: 0, + right: 0, + child: WaveReverse(height: blockSizeVertical * 8)), + Container( + height: screenHeight, + padding: isMobile() + ? const EdgeInsets.symmetric(horizontal: 24) + : const EdgeInsets.symmetric(horizontal: 60), + width: double.infinity, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + height: isMobile() + ? screenHeight * .12 + : screenHeight * .20), + SvgPicture.asset( + 'assets/svgs/add_mobile.svg', + height: isMobile() + ? blockSizeVertical * 22 + : blockSizeVertical * 30, + allowDrawingOutsideViewBox: true, + ), + const SizedBox( + height: 24, + ), + Text(addMobile, style: titleTextStyle()), + const SizedBox( + height: 8, + ), + Text(addMobileCaption, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.caption), + const SizedBox( + height: 24, + ), + FormBuilder( + key: _formKey, + child: Column( + children: [ + // Mobile number 1 + FormBuilderTextField( + name: 'mobile1', + validator: mobileNumberValidator, + decoration: normalTextFieldStyle( + "mobile number 1", "+63")), + const SizedBox( + height: 12, + ), + FormBuilderTextField( + name: 'mobile2', + validator: mobileNumberValidator, + decoration: normalTextFieldStyle( + "mobile number 2", "+63")), + + SizedBox( + height: isMobile() + ? blockSizeVertical * 3 + : blockSizeHorizontal * 5), + SizedBox( + width: double.infinity, + height: screenHeight * .06, + child: ElevatedButton( + style: secondaryBtnStyle(second, + Colors.transparent, Colors.white54), + child: const Text( + submit, + style: TextStyle(color: Colors.white), + ), + onPressed: () { + context.go( + context.namedLocation('request-sos')); + // if (_formKey.currentState.validate()) { + // _formKey.currentState.save(); + // BlocProvider.of(context) + // .add(UserWebLogin( + // password: password, + // username: username)); + // } + }, + ), + ), + ], + )) + ]), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/unit2/lib/screen/sos/components/mobile.dart b/unit2/lib/screen/sos/components/mobile.dart new file mode 100644 index 0000000..bd0c522 --- /dev/null +++ b/unit2/lib/screen/sos/components/mobile.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; +import 'package:fluttericon/font_awesome5_icons.dart'; + +import '../../../theme-data.dart/colors.dart'; + +class Mobile extends StatelessWidget { + final String title; + final String subtitle; + final Function onPressed; + const Mobile({ + super.key, + required this.title, + required this.subtitle, + required this.onPressed, + }); + + @override + Widget build(BuildContext context) { + return ListTile( + leading: const Icon( + FontAwesome5.sim_card, + color: second, + ), + title: Text(title), + subtitle: Text( + subtitle, + style: Theme.of(context).textTheme.caption, + ), + trailing: IconButton( + icon: const Icon(Icons.edit), + onPressed: () { + onPressed(); + }, + ), + ); + } +} diff --git a/unit2/lib/screen/sos/request_sos.dart b/unit2/lib/screen/sos/request_sos.dart new file mode 100644 index 0000000..e284efd --- /dev/null +++ b/unit2/lib/screen/sos/request_sos.dart @@ -0,0 +1,109 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/container.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:fluttericon/font_awesome5_icons.dart'; +import 'package:fluttericon/typicons_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:unit2/screen/sos/components/mobile.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/text_container.dart'; + +import '../../theme-data.dart/btn-style.dart'; +import '../../theme-data.dart/form-style.dart'; +import '../../utils/global.dart'; + +class RequestSOS extends StatefulWidget { + const RequestSOS({super.key}); + + @override + State createState() => _RequestSOSState(); +} + +class _RequestSOSState extends State { + @override + Widget build(BuildContext context) { + return SafeArea( + child: Scaffold( + appBar: AppBar( + centerTitle: true, + title: const Text(sOSTitle), + backgroundColor: second, + ), + body: SingleChildScrollView( + child: Container( + height: screenHeight * .89, + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 10), + child: Column( + children: [ + SizedBox( + height: blockSizeVertical * 2, + ), + SvgPicture.asset( + 'assets/svgs/request_sos.svg', + height: blockSizeVertical * 22, + allowDrawingOutsideViewBox: true, + ), + Mobile( + title: "09661548775", + subtitle: mobile1, + onPressed: () {}), + const Divider(), + Mobile( + title: "09661548775", + subtitle: mobile2, + onPressed: () {}), + const Divider(), + ListTile( + leading: const Icon( + Typicons.location, + color: second, + ), + title: Text("Latitude/Longitude"), + subtitle: Text( + currentLocation, + style: Theme.of(context).textTheme.caption, + ), + ), + FormBuilderTextField( + name: "message", + validator: FormBuilderValidators.compose([ + FormBuilderValidators.required( + errorText: "Message is required") + ]), + autovalidateMode: AutovalidateMode.onUserInteraction, + maxLines: 5, + decoration: normalTextFieldStyle("", "SOS message.."), + ), + Expanded( + child: SizedBox(), + ), + SizedBox( + width: double.infinity, + height: screenHeight * .06, + child: ElevatedButton( + style: secondaryBtnStyle( + second, Colors.transparent, Colors.white54), + child: const Text( + submit, + style: TextStyle(color: Colors.white), + ), + onPressed: () { + // if (_formKey.currentState.validate()) { + // _formKey.currentState.save(); + // BlocProvider.of(context) + // .add(UserWebLogin( + // password: password, + // username: username)); + // } + }, + ), + ), + ], + )), + ), + ), + ); + } +} diff --git a/unit2/lib/screen/sos/sos_received.dart b/unit2/lib/screen/sos/sos_received.dart new file mode 100644 index 0000000..8a2de78 --- /dev/null +++ b/unit2/lib/screen/sos/sos_received.dart @@ -0,0 +1,133 @@ +import 'package:animate_do/animate_do.dart'; +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:unit2/theme-data.dart/btn-style.dart'; +import 'package:unit2/utils/text_container.dart'; +import '../../theme-data.dart/colors.dart'; +import '../../utils/global.dart'; + +class SOSreceived extends StatelessWidget { + final Function onpressed; + const SOSreceived({required Key key, required this.onpressed}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 35), + height: screenHeight, + child: Stack( + children: [ + Positioned( + bottom: 0, + child: SizedBox( + width: screenWidth, + height: screenHeight / 2, + child: Opacity( + opacity: .2, + child: Image.asset( + "assets/emergency.png", + fit: BoxFit.cover, + ), + ), + )), + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + height: blockSizeVertical * 6, + ), + Bounce( + from: 20, + infinite: true, + delay: const Duration(milliseconds: 800), + child: Stack( + alignment: AlignmentDirectional.topCenter, + children: [ + Container( + margin: const EdgeInsets.only(top: 20), + child: CircleAvatar( + radius: blockSizeVertical * 8, + backgroundColor: second, + child: Container( + margin: const EdgeInsets.only(top: 25), + child: SvgPicture.asset( + 'assets/sos.svg', + height: blockSizeHorizontal * 17, + color: Colors.white, + allowDrawingOutsideViewBox: true, + alignment: Alignment.bottomCenter, + ), + ), + ), + ), + Positioned( + top: blockSizeVertical * 3, + child: SpinKitPulse( + color: primary, + size: 120, + ), + ), + ], + ), + ), + const SizedBox(height: 16), + SlideInUp( + from: 50, + child: AutoSizeText( + "SOS Received!", + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.displayMedium!.copyWith( + fontSize: 40, + color: third, + fontWeight: FontWeight.w500, + ), + ), + ), + const SizedBox( + height: 8, + ), + SlideInUp( + from: 50, + child: Container( + padding: const EdgeInsets.all(15), + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(15))), + child: AutoSizeText( + sOSReceivedMessage, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.caption!.copyWith( + fontSize: 16, + color: Colors.black87, + ), + ), + ), + ), + const SizedBox( + height: 40, + ), + Expanded(child: Container()), + SlideInUp( + child: SizedBox( + height: 50, + width: 200, + child: TextButton( + style: mainBtnStyle(second, Colors.transparent, second), + onPressed: () {}, + child: const Text("Cancel request", + style: TextStyle( + color: Colors.white, + )), + ), + ), + ) + ], + ), + ], + ), + ); + } +} diff --git a/unit2/lib/screen/unit2/homepage.dart/components/drawer-screen.dart b/unit2/lib/screen/unit2/homepage.dart/components/drawer-screen.dart new file mode 100644 index 0000000..4ffd092 --- /dev/null +++ b/unit2/lib/screen/unit2/homepage.dart/components/drawer-screen.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_zoom_drawer/config.dart'; +import 'package:flutter_zoom_drawer/flutter_zoom_drawer.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'menu-screen.dart'; +import '../module-screen.dart'; + +class DrawerScreen extends StatefulWidget { + const DrawerScreen({Key? key}) : super(key: key); + @override + State createState() => _DrawerScreenState(); +} + +class _DrawerScreenState extends State { + final zoomDrawerController = ZoomDrawerController(); + @override + Widget build(BuildContext context) { + return ZoomDrawer( + controller: zoomDrawerController, + menuScreen: const MenuScreen(), + mainScreen: const MainScreen(), + style: DrawerStyle.defaultStyle, + borderRadius: 24.0, + showShadow: false, + angle: -0.0, + slideWidth: MediaQuery.of(context).size.width * .90, + openCurve: Curves.fastOutSlowIn, + closeCurve: Curves.easeOut, + menuBackgroundColor: Colors.grey, + ); + } +} diff --git a/unit2/lib/screen/unit2/homepage.dart/components/empty_module.dart b/unit2/lib/screen/unit2/homepage.dart/components/empty_module.dart new file mode 100644 index 0000000..e7c9590 --- /dev/null +++ b/unit2/lib/screen/unit2/homepage.dart/components/empty_module.dart @@ -0,0 +1,53 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/container.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:unit2/theme-data.dart/text-styles.dart'; + +import '../../../../utils/global.dart'; +import '../../../../utils/text_container.dart'; + +class NoModule extends StatelessWidget { + const NoModule({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 25), + width: double.infinity, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SvgPicture.asset( + 'assets/svgs/no_module.svg', + height: blockSizeVertical * 30, + allowDrawingOutsideViewBox: true, + ), + const SizedBox( + height: 24, + ), + Text( + noModule, + textAlign: TextAlign.center, + style: Theme.of(context) + .textTheme + .displayLarge! + .copyWith(fontSize: blockSizeVertical * 5, height: .8), + ), + const SizedBox( + height: 5, + ), + Text( + noModuleSubTitle, + style: Theme.of(context) + .textTheme + .caption! + .copyWith(fontSize: blockSizeVertical * 1.5), + textAlign: TextAlign.center, + ) + ]), + ); + } +} diff --git a/unit2/lib/screen/unit2/homepage.dart/components/menu-screen.dart b/unit2/lib/screen/unit2/homepage.dart/components/menu-screen.dart new file mode 100644 index 0000000..f207fde --- /dev/null +++ b/unit2/lib/screen/unit2/homepage.dart/components/menu-screen.dart @@ -0,0 +1,75 @@ +import 'package:flutter/material.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:fluttericon/web_symbols_icons.dart'; +import 'package:fluttericon/typicons_icons.dart'; +import 'package:fluttericon/font_awesome5_icons.dart'; +import 'menu.dart'; +import '../../../../utils/global.dart'; + +class MenuScreen extends StatefulWidget { + const MenuScreen({Key? key}) : super(key: key); + + @override + State createState() => _MenuScreenState(); +} + +class _MenuScreenState extends State { + @override + Widget build(BuildContext context) { + return SafeArea( + child: Drawer( + child: SingleChildScrollView( + child: SizedBox( + height: blockSizeVertical * 96, + child: Column( + // ignore: prefer_const_literals_to_create_immutables + children: [ + const UserAccountsDrawerHeader( + decoration: BoxDecoration( + color: primary, + image: DecorationImage( + image: AssetImage('assets/pngs/bg.png'), + fit: BoxFit.cover)), + accountName: Text("ACUIN" ", " "RODOLFO" " " "BERNALIS"), + accountEmail: null, + currentAccountPicture: CircleAvatar( + radius: 40, + backgroundColor: fifth, + child: CircleAvatar( + radius: 33, + backgroundColor: third, + child: //Icon(Icons.person, size: 40, color: fifth), + Text( + "A", + style: TextStyle(fontSize: 45.0, color: fifth), + ), + ), + ), + ), + getTile(Typicons.user_outline, "Profile", 'profile', context), + const Divider(), + getTile(Typicons.home_outline, "Address", '/SelfAddressScreen', + context), + const Divider(), + getTile(Typicons.contacts, "Contact Number", + '/SelfContactScreen', context), + const Divider(), + getTile(Typicons.mail, "Email Address", '/SelfEmailAddScreen', + context), + const Divider(), + getTile(FontAwesome5.life_ring, "Request SOS", 'request-sos', + context), + const Divider(), + Expanded( + child: Align( + alignment: FractionalOffset.bottomLeft, + child: getTile(WebSymbols.logout, "Logout", '/', context), + )), + ], + ), + ), + ), + ), + ); + } +} diff --git a/unit2/lib/screen/unit2/homepage.dart/components/menu.dart b/unit2/lib/screen/unit2/homepage.dart/components/menu.dart new file mode 100644 index 0000000..204fdd0 --- /dev/null +++ b/unit2/lib/screen/unit2/homepage.dart/components/menu.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_zoom_drawer/flutter_zoom_drawer.dart'; +import 'package:go_router/go_router.dart'; + +import '../../../../theme-data.dart/colors.dart'; + +Widget getTile( + IconData icondata, String title, String route, BuildContext context) { + return ListTile( + dense: true, + leading: Icon( + icondata, + color: primary, + ), + title: Text( + title, + style: const TextStyle(color: Colors.black), + ), + onTap: () async { + debugPrint(title); + ZoomDrawer.of(context)!.toggle(); + context.goNamed(route); + }, + ); +} diff --git a/unit2/lib/screen/unit2/homepage.dart/module-screen.dart b/unit2/lib/screen/unit2/homepage.dart/module-screen.dart index f55a9c9..96b7a99 100644 --- a/unit2/lib/screen/unit2/homepage.dart/module-screen.dart +++ b/unit2/lib/screen/unit2/homepage.dart/module-screen.dart @@ -1,16 +1,53 @@ -import 'package:flutter/src/widgets/container.dart'; -import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_zoom_drawer/flutter_zoom_drawer.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/text_container.dart'; -class ModuleScreen extends StatefulWidget { - const ModuleScreen({super.key}); +import 'components/empty_module.dart'; + +class MainScreen extends StatefulWidget { + const MainScreen({Key? key}) : super(key: key); @override - State createState() => _ModuleScreenState(); + State createState() => _MainScreenState(); } -class _ModuleScreenState extends State { +class _MainScreenState extends State { + bool hasModule = false; @override Widget build(BuildContext context) { - return Container(); + return WillPopScope( + onWillPop: () async { + return Future.value(true); + }, + child: SafeArea( + child: Scaffold( + appBar: AppBar( + backgroundColor: primary, + leading: IconButton( + onPressed: () { + ZoomDrawer.of(context)!.toggle(); + }, + icon: const Icon( + Icons.menu, + color: Colors.white, + ), + ), + centerTitle: true, + title: const Text( + unit2ModuleScreen, + style: TextStyle( + fontSize: 18.0, + color: Colors.white, + ), + ), + ), + body: hasModule + ? const Center( + child: Text('Main Screen'), + ) + : const NoModule(), + )), + ); } -} \ No newline at end of file +} diff --git a/unit2/lib/screen/unit2/login/login.dart b/unit2/lib/screen/unit2/login/login.dart index 4dc993e..31da7a8 100644 --- a/unit2/lib/screen/unit2/login/login.dart +++ b/unit2/lib/screen/unit2/login/login.dart @@ -3,6 +3,8 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:go_router/go_router.dart'; +import 'package:unit2/utils/text_container.dart'; import '../../../widgets/wave.dart'; import '../../../utils/global.dart'; import '../../../theme-data.dart/colors.dart'; @@ -53,12 +55,12 @@ class _UniT2LoginState extends State { ), Text( - "Welcome to!", + welcome, style: TextStyle( fontSize: blockSizeVertical * 5, fontWeight: FontWeight.w600), ), - Text("uniT-App", + Text(unitApp, style: TextStyle( fontSize: blockSizeVertical * 8, fontWeight: FontWeight.w800, @@ -66,7 +68,7 @@ class _UniT2LoginState extends State { height: 1, color: primary)), Text( - "Please login to continue.", + loginToContinue, style: TextStyle( fontSize: blockSizeVertical * 2, height: 1.5, @@ -78,10 +80,8 @@ class _UniT2LoginState extends State { // USERNAME FormBuilderTextField( name: 'username', - validator: FormBuilderValidators.compose([ - FormBuilderValidators.required( - errorText: "Username is required") - ]), + validator: FormBuilderValidators.required( + errorText: "Username is required"), autofocus: false, style: const TextStyle( fontWeight: FontWeight.bold, @@ -93,10 +93,9 @@ class _UniT2LoginState extends State { // PASSWORD FormBuilderTextField( name: 'password', - validator: FormBuilderValidators.compose([ - FormBuilderValidators.required( - errorText: "Password is required") - ]), + validator: FormBuilderValidators.required( + errorText: "Password is required"), + // initialValue: state.password, onChanged: (value) { value!.isEmpty @@ -155,20 +154,14 @@ class _UniT2LoginState extends State { child: SizedBox( width: MediaQuery.of(context).size.width, child: ElevatedButton( - style: btnStyle( + style: mainBtnStyle( second, Colors.transparent, Colors.white54), child: const Text( "LOGIN", style: TextStyle(color: Colors.white), ), - onPressed: () async { - if (_formKey.currentState! - .saveAndValidate()) { - debugPrint( - _formKey.currentState!.value['name']); - debugPrint(_formKey - .currentState!.value['password']); - } + onPressed: () { + context.go(context.namedLocation('home')); // if (_formKey.currentState.validate()) { // _formKey.currentState.save(); // BlocProvider.of(context) @@ -189,14 +182,14 @@ class _UniT2LoginState extends State { child: SizedBox( width: MediaQuery.of(context).size.width, child: ElevatedButton.icon( - style: btnStyle(Colors.white, second, + style: mainBtnStyle(Colors.white, second, primary.withOpacity(.4)), icon: const Icon( Icons.qr_code, color: second, ), label: const Text( - "Login via QR code", + loginViaQr, style: TextStyle(color: second), ), onPressed: () async { @@ -208,8 +201,7 @@ class _UniT2LoginState extends State { SizedBox( height: blockSizeVertical * 1, ), - const LoginViaQr( - text: " Request Emergency Response "), + const LoginViaQr(text: emergencyReponseLabel), SizedBox( height: blockSizeVertical * 1, ), @@ -222,13 +214,14 @@ class _UniT2LoginState extends State { FontAwesome5.life_ring, color: Colors.white, ), - style: btnStyle( + style: mainBtnStyle( third, Colors.transparent, Colors.white38), onPressed: () { - Navigator.pushNamed(context, '/SosScreen'); + context + .go(context.namedLocation('add-mobile')); }, label: const Text( - "Request SOS", + requestSOS, style: TextStyle(color: Colors.white), )), ) diff --git a/unit2/lib/screen/unit2/profile/components/cover-image.dart b/unit2/lib/screen/unit2/profile/components/cover-image.dart new file mode 100644 index 0000000..5ce6aaf --- /dev/null +++ b/unit2/lib/screen/unit2/profile/components/cover-image.dart @@ -0,0 +1,20 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; + +class CoverImage extends StatelessWidget { + const CoverImage({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + color: Colors.grey, + child: CachedNetworkImage( + imageUrl: + 'https://static.vecteezy.com/system/resources/thumbnails/008/074/253/small/tropical-forest-sunset-nature-background-with-coconut-trees-vector.jpg', + width: double.infinity, + height: 220, + fit: BoxFit.cover, + ), + ); + } +} \ No newline at end of file diff --git a/unit2/lib/screen/unit2/profile/profile.dart b/unit2/lib/screen/unit2/profile/profile.dart new file mode 100644 index 0000000..5803e2f --- /dev/null +++ b/unit2/lib/screen/unit2/profile/profile.dart @@ -0,0 +1,109 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:fluttericon/font_awesome5_icons.dart'; +import 'package:go_router/go_router.dart'; +import './components/cover-image.dart'; + +class Profile extends StatelessWidget { + const Profile({super.key}); + + @override + Widget build(BuildContext context) { + return WillPopScope( + onWillPop: () async { + return Future.value(true); + }, + child: SafeArea( + child: Scaffold( + body: Stack( + clipBehavior: Clip.none, + alignment: Alignment.center, + children: [ + const CoverImage(), + const Positioned(top: 125, child: BuildProfileImage()), + Positioned( + top: 10, + left: 20, + child: IconButton( + onPressed: () { + context.go(context.namedLocation('home')); + }, + icon: const Icon( + FontAwesome5.arrow_left, + size: 24, + color: Colors.white, + ), + )), + ], + ), + ), + ), + ); + } +} + +class BuildInformation extends StatelessWidget { + const BuildInformation({super.key}); + @override + Widget build(BuildContext context) { + return Column( + children: [ + const SizedBox( + height: 8, + ), + Text( + "Rodolfo Bernales Acuin", + style: Theme.of(context) + .textTheme + .headline5! + .copyWith(fontWeight: FontWeight.bold), + ), + Row( + children: [ + Icon( + FontAwesome5.birthday_cake, + color: Colors.blueAccent, + ), + const SizedBox( + width: 20, + ), + Text( + "july 14, 1994 | Male", + style: + Theme.of(context).textTheme.caption!.copyWith(fontSize: 22), + ), + ], + ), + ], + ); + } +} + +class BuildProfileImage extends StatelessWidget { + const BuildProfileImage({super.key}); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Stack( + alignment: Alignment.center, + children: [ + CircleAvatar( + radius: 85, + backgroundColor: Colors.white, + ), + CircleAvatar( + radius: 80, + backgroundColor: Colors.grey.shade800, + child: SvgPicture.asset( + 'assets/svgs/male.svg', + ), + ), + ], + ), + BuildInformation(), + ], + ); + } +} diff --git a/unit2/lib/theme-data.dart/btn-style.dart b/unit2/lib/theme-data.dart/btn-style.dart index 998f54d..89f7e86 100644 --- a/unit2/lib/theme-data.dart/btn-style.dart +++ b/unit2/lib/theme-data.dart/btn-style.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -ButtonStyle btnStyle(Color background, Color borderColor, Color overlay) { +ButtonStyle mainBtnStyle(Color background, Color borderColor, Color overlay) { return ButtonStyle( elevation: MaterialStateProperty.all(0), backgroundColor: MaterialStateProperty.all(background), @@ -13,3 +13,18 @@ ButtonStyle btnStyle(Color background, Color borderColor, Color overlay) { color: borderColor, )))); } + +ButtonStyle secondaryBtnStyle( + Color background, Color borderColor, Color overlay) { + return ButtonStyle( + elevation: MaterialStateProperty.all(0), + backgroundColor: MaterialStateProperty.all(background), + overlayColor: MaterialStateProperty.all(overlay), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + side: BorderSide( + width: 2, + color: borderColor, + )))); +} diff --git a/unit2/lib/theme-data.dart/form-style.dart b/unit2/lib/theme-data.dart/form-style.dart index 2f339a1..6fbcc4c 100644 --- a/unit2/lib/theme-data.dart/form-style.dart +++ b/unit2/lib/theme-data.dart/form-style.dart @@ -1,5 +1,53 @@ import 'package:flutter/material.dart'; +InputDecoration normalTextFieldStyle(String labelText, String hintText) { + return InputDecoration( + contentPadding: EdgeInsets.fromLTRB(12, 6, 6, 6), + floatingLabelBehavior: FloatingLabelBehavior.auto, + labelText: labelText, + labelStyle: const TextStyle(color: Colors.grey), + hintText: hintText, + hintStyle: const TextStyle( + color: Colors.grey, + ), + focusedBorder: OutlineInputBorder( + borderSide: const BorderSide( + width: 1, + color: Colors.black87, + ), + borderRadius: BorderRadius.circular(5), + ), + enabledBorder: OutlineInputBorder( + borderSide: const BorderSide( + color: Colors.grey, + width: 1, + ), + borderRadius: BorderRadius.circular(5), + ), + disabledBorder: OutlineInputBorder( + borderSide: const BorderSide( + width: 1, + color: Colors.grey, + ), + borderRadius: BorderRadius.circular(5), + ), + errorBorder: const OutlineInputBorder( + borderSide: BorderSide( + color: Colors.red, + width: 1, + ), + borderRadius: BorderRadius.all(Radius.circular(5)), + ), + focusedErrorBorder: const OutlineInputBorder( + borderSide: BorderSide( + color: Colors.red, + width: 1, + ), + borderRadius: BorderRadius.all(Radius.circular(5)), + ), + filled: false); +} + InputDecoration loginTextFieldStyle() { return InputDecoration( floatingLabelBehavior: FloatingLabelBehavior.never, @@ -37,4 +85,4 @@ InputDecoration loginTextFieldStyle() { borderRadius: BorderRadius.all(Radius.circular(5)), ), filled: false); -} \ No newline at end of file +} diff --git a/unit2/lib/theme-data.dart/text-styles.dart b/unit2/lib/theme-data.dart/text-styles.dart new file mode 100644 index 0000000..7ad4888 --- /dev/null +++ b/unit2/lib/theme-data.dart/text-styles.dart @@ -0,0 +1,7 @@ +import 'package:flutter/material.dart'; +import 'package:unit2/utils/global.dart'; + +TextStyle titleTextStyle() { + return TextStyle( + fontSize: blockSizeVertical * 2.5, fontWeight: FontWeight.w500); +} diff --git a/unit2/lib/utils/router.dart b/unit2/lib/utils/router.dart index 98a746a..7794074 100644 --- a/unit2/lib/utils/router.dart +++ b/unit2/lib/utils/router.dart @@ -1,11 +1,36 @@ import 'package:go_router/go_router.dart'; +import '../screen/sos/add_mobile.dart'; +import '../screen/sos/request_sos.dart'; import '../screen/unit2/login/login.dart'; +import '../screen/unit2/homepage.dart/components/drawer-screen.dart'; +import '../screen/unit2/profile/profile.dart'; - -final GoRouter goRouter = GoRouter( - routes: [ - GoRoute(path: '/', - builder: (context,state) => const UniT2Login() - ) - ] -); \ No newline at end of file +final GoRouter goRouter = GoRouter(routes: [ + GoRoute( + path: '/', + name: 'login', + builder: (context, state) => const UniT2Login(), + routes: [ + GoRoute( + name: 'home', + path: 'home', + builder: (context, state) => const DrawerScreen(), + routes: [ + GoRoute( + name: 'profile', + path: 'profile', + builder: (context, state) => const Profile()) + ]), + GoRoute( + name: 'add-mobile', + path: 'add-moble', + builder: (context, state) => AddMobile(), + routes: [ + GoRoute( + name: 'request-sos', + path: 'request-sos', + builder: (context, state) => const RequestSOS(), + ) + ]), + ]), +]); diff --git a/unit2/lib/utils/screen_info.dart b/unit2/lib/utils/screen_info.dart new file mode 100644 index 0000000..a536655 --- /dev/null +++ b/unit2/lib/utils/screen_info.dart @@ -0,0 +1,9 @@ +import 'package:unit2/utils/global.dart'; + +bool isMobile() { + if (screenWidth > 500.00) { + return false; + } else { + return true; + } +} diff --git a/unit2/lib/utils/text_container.dart b/unit2/lib/utils/text_container.dart new file mode 100644 index 0000000..a214c0b --- /dev/null +++ b/unit2/lib/utils/text_container.dart @@ -0,0 +1,23 @@ +const String addMobile = "Enter your mobile number(s)"; +const String addMobileCaption = + "These mobile numbers will be used to contact you if you request an emergency response. Please provide at least one active number"; +const String mobileNumberRequired = "You must add atleast one mobile"; +const String numericValidator = "Please a number only"; +const String mobile1 = "Mobile number 1"; +const String mobile2 = "Mobile number 2"; +const String currentLocation = "You current location"; +const String noModule = "No Module Assigned"; +const String noModuleSubTitle = + "Please contact the admin if you want to access a module."; +const String submit = "SUBMIT"; +const String login = "LOGIN"; +const String sOSTitle = "Request SOS"; +const String sOSReceivedMessage = + "your SOS request has been received. Please wait for respondent's acknowledgement."; +const String unit2ModuleScreen = "uniT2 Modules"; +const String welcome = "Welcome to!"; +const String unitApp = 'uniT-App'; +const String loginToContinue = "Please login to continue."; +const String loginViaQr = "Login via QR code"; +const String emergencyReponseLabel = " Request Emergency Response "; +const String requestSOS = "Request SOS"; \ No newline at end of file diff --git a/unit2/lib/utils/validators.dart b/unit2/lib/utils/validators.dart new file mode 100644 index 0000000..704e733 --- /dev/null +++ b/unit2/lib/utils/validators.dart @@ -0,0 +1,12 @@ +import 'package:form_builder_validators/form_builder_validators.dart'; +import '../utils/text_container.dart'; + + +final mobileNumberValidator = FormBuilderValidators.compose([ + FormBuilderValidators.required( + errorText: mobileNumberRequired), + FormBuilderValidators.numeric( + errorText: numericValidator) + ]); + + diff --git a/unit2/macos/Flutter/GeneratedPluginRegistrant.swift b/unit2/macos/Flutter/GeneratedPluginRegistrant.swift index 287b6a9..e9cf192 100644 --- a/unit2/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/unit2/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,8 +5,12 @@ import FlutterMacOS import Foundation +import path_provider_macos import shared_preferences_macos +import sqflite func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) } diff --git a/unit2/pubspec.lock b/unit2/pubspec.lock index cd07b24..06adf03 100644 --- a/unit2/pubspec.lock +++ b/unit2/pubspec.lock @@ -1,6 +1,13 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + animate_do: + dependency: "direct main" + description: + name: animate_do + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" async: dependency: transitive description: @@ -8,6 +15,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.9.0" + auto_size_text: + dependency: "direct main" + description: + name: auto_size_text + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" boolean_selector: dependency: transitive description: @@ -15,6 +29,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + cached_network_image: + dependency: "direct main" + description: + name: cached_network_image + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.3" + cached_network_image_platform_interface: + dependency: transitive + description: + name: cached_network_image_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + cached_network_image_web: + dependency: transitive + description: + name: cached_network_image_web + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" characters: dependency: transitive description: @@ -36,6 +71,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.16.0" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" cupertino_icons: dependency: "direct main" description: @@ -83,6 +125,20 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_blurhash: + dependency: transitive + description: + name: flutter_blurhash + url: "https://pub.dartlang.org" + source: hosted + version: "0.7.0" + flutter_cache_manager: + dependency: transitive + description: + name: flutter_cache_manager + url: "https://pub.dartlang.org" + source: hosted + version: "3.3.0" flutter_custom_clippers: dependency: "direct main" description: @@ -109,6 +165,13 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_spinkit: + dependency: "direct main" + description: + name: flutter_spinkit + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.0" flutter_svg: dependency: "direct main" description: @@ -167,7 +230,21 @@ packages: name: go_router url: "https://pub.dartlang.org" source: hosted - version: "5.2.0" + version: "3.1.1" + http: + dependency: transitive + description: + name: http + url: "https://pub.dartlang.org" + source: hosted + version: "0.13.5" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.2" intl: dependency: transitive description: @@ -231,6 +308,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.0" + octo_image: + dependency: transitive + description: + name: octo_image + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" path: dependency: transitive description: @@ -252,6 +336,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.1" + path_provider: + dependency: transitive + description: + name: path_provider + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.11" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.22" + path_provider_ios: + dependency: transitive + description: + name: path_provider_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.11" path_provider_linux: dependency: transitive description: @@ -259,6 +364,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.7" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.6" path_provider_platform_interface: dependency: transitive description: @@ -273,6 +385,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.3" + pedantic: + dependency: transitive + description: + name: pedantic + url: "https://pub.dartlang.org" + source: hosted + version: "1.11.1" petitparser: dependency: transitive description: @@ -308,6 +427,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "6.0.4" + rxdart: + dependency: transitive + description: + name: rxdart + url: "https://pub.dartlang.org" + source: hosted + version: "0.27.7" shared_preferences: dependency: transitive description: @@ -376,6 +502,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.9.0" + sqflite: + dependency: transitive + description: + name: sqflite + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0+3" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + url: "https://pub.dartlang.org" + source: hosted + version: "2.4.0+2" stack_trace: dependency: transitive description: @@ -397,6 +537,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.1" + synchronized: + dependency: transitive + description: + name: synchronized + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0+3" term_glyph: dependency: transitive description: @@ -411,6 +558,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.4.12" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + uuid: + dependency: transitive + description: + name: uuid + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.7" vector_math: dependency: transitive description: diff --git a/unit2/pubspec.yaml b/unit2/pubspec.yaml index 21b322e..af6129a 100644 --- a/unit2/pubspec.yaml +++ b/unit2/pubspec.yaml @@ -36,7 +36,7 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 - go_router: ^5.2.0 + go_router: ^3.0.6 flutter_custom_clippers: ^2.0.0 flutter_svg: ^1.1.6 flutter_form_builder: ^7.7.0 @@ -45,6 +45,10 @@ dependencies: fluttertoast: ^8.1.1 device_preview: ^1.1.0 flutter_zoom_drawer: ^3.0.3 + cached_network_image: ^3.2.3 + auto_size_text: ^3.0.0 + animate_do: ^3.0.2 + flutter_spinkit: ^5.1.0 dev_dependencies: flutter_test: From a2d7da54356694360341473b7235619c273f32a7 Mon Sep 17 00:00:00 2001 From: rodolfobacuinjr Date: Tue, 6 Dec 2022 10:45:18 +0800 Subject: [PATCH 04/86] create user interface for QR-Code scanner settings --- unit2/assets/svgs/switch.svg | 1 + .../components/custom_switch.dart | 44 ++++ .../qr_code_scanner.dart/settings_screen.dart | 205 ++++++++++++++++++ unit2/lib/test_data.dart | 3 + unit2/lib/utils/text_container.dart | 7 +- unit2/pubspec.lock | 7 + unit2/pubspec.yaml | 1 + 7 files changed, 265 insertions(+), 3 deletions(-) create mode 100644 unit2/assets/svgs/switch.svg create mode 100644 unit2/lib/screen/unit2/roles/qr_code_scanner.dart/components/custom_switch.dart create mode 100644 unit2/lib/screen/unit2/roles/qr_code_scanner.dart/settings_screen.dart create mode 100644 unit2/lib/test_data.dart diff --git a/unit2/assets/svgs/switch.svg b/unit2/assets/svgs/switch.svg new file mode 100644 index 0000000..68521a8 --- /dev/null +++ b/unit2/assets/svgs/switch.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/unit2/lib/screen/unit2/roles/qr_code_scanner.dart/components/custom_switch.dart b/unit2/lib/screen/unit2/roles/qr_code_scanner.dart/components/custom_switch.dart new file mode 100644 index 0000000..907993f --- /dev/null +++ b/unit2/lib/screen/unit2/roles/qr_code_scanner.dart/components/custom_switch.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; +import 'package:toggle_switch/toggle_switch.dart'; + +class CostumToggleSwitch extends StatelessWidget { + final List activeBGColors; + final List icons; +final int initialLabelIndex; + final void Function(int?)? onToggle; + final List labels; + const CostumToggleSwitch( + {Key? key, + required this.activeBGColors, + required this.icons, + required this.onToggle, + required this.labels, + required this.initialLabelIndex + + }) + : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(15), + height: 80, + child: ToggleSwitch( + minWidth: 150.0, + cornerRadius: 25.0, + activeBgColors: [ + [Colors.green[800]!], + [Colors.red[800]!] + ], + activeFgColor: Colors.white, + inactiveBgColor: Colors.grey, + inactiveFgColor: Colors.white, + initialLabelIndex: initialLabelIndex, + totalSwitches: 2, + labels: labels, + icons: icons, + radiusStyle: false, + onToggle: onToggle), + ); + } +} diff --git a/unit2/lib/screen/unit2/roles/qr_code_scanner.dart/settings_screen.dart b/unit2/lib/screen/unit2/roles/qr_code_scanner.dart/settings_screen.dart new file mode 100644 index 0000000..5a79040 --- /dev/null +++ b/unit2/lib/screen/unit2/roles/qr_code_scanner.dart/settings_screen.dart @@ -0,0 +1,205 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:fluttericon/entypo_icons.dart'; +import 'package:fluttericon/modern_pictograms_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:unit2/screen/unit2/roles/qr_code_scanner.dart/components/custom_switch.dart'; +import 'package:unit2/test_data.dart'; +import 'package:unit2/utils/text_container.dart'; +import '../../../../theme-data.dart/btn-style.dart'; +import '../../../../theme-data.dart/colors.dart'; +import '../../../../theme-data.dart/form-style.dart'; +import '../../../../utils/global.dart'; + +class QRCodeScannerSettings extends StatefulWidget { + const QRCodeScannerSettings({super.key}); + + @override + State createState() => _QRCodeScannerSettingsState(); +} + +class _QRCodeScannerSettingsState extends State { + bool _includeOtherInputs = false; + String scanMode = 'INCOMING'; + String selectedLevel = ''; + String selectedEstablishment = ''; + String selectedArea = ''; + final _formKey = GlobalKey(); + @override + Widget build(BuildContext context) { + return SafeArea( + child: Scaffold( + appBar: AppBar( + title: const Text(qrScannerTitle), + centerTitle: true, + backgroundColor: second, + ), + body: SingleChildScrollView( + child: Container( + height: screenHeight * .88, + padding: const EdgeInsets.symmetric(horizontal: 30), + child: FormBuilder( + key: _formKey, + child: Column( + children: [ + const SizedBox( + height: 32, + ), + SvgPicture.asset( + 'assets/svgs/switch.svg', + height: blockSizeVertical * 14, + allowDrawingOutsideViewBox: true, + ), + ListTile( + title: Text( + "Set QR Scanner Settings", + style: Theme.of(context) + .textTheme + .headline6! + .copyWith(color: third), + textAlign: TextAlign.center, + ), + ), + Text("Include other inputs?", + style: Theme.of(context).textTheme.subtitle1), + Text( + "inputs such as body temperature, etc.", + style: Theme.of(context).textTheme.caption, + ), + SizedBox( + child: FittedBox( + child: CostumToggleSwitch( + activeBGColors: [ + Colors.green[800]!, + Colors.red[800]! + ], + initialLabelIndex: _includeOtherInputs ? 0 : 1, + icons: const [Entypo.check, ModernPictograms.cancel], + labels: const ['YES', 'NO'], + onToggle: (value) { + value == 0 + ? _includeOtherInputs = true + : _includeOtherInputs = false; + }, + ), + ), + ), + // Incoming or outgoing + Text("Incoming or Outgoing? ", + style: Theme.of(context).textTheme.subtitle1), + Text( + "incoming for entrance outgoing for exit.", + style: Theme.of(context).textTheme.caption, + ), + FittedBox( + child: CostumToggleSwitch( + activeBGColors: [Colors.green[800]!, Colors.red[800]!], + initialLabelIndex: scanMode == 'INCOMING' ? 0 : 1, + icons: const [ + Entypo.down_bold, + Entypo.up_bold, + ], + labels: const ['INCOMING', 'OUTGOING'], + onToggle: (value) { + value == 0 + ? scanMode = 'INCOMING' + : scanMode = 'OUTGOING'; + }, + ), + ), + const SizedBox( + height: 10, + ), + + //SELECT LEVEL + + FormBuilderDropdown( + name: 'level', + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: normalTextFieldStyle("Select level", "level"), + items: levels + .map((level) => DropdownMenuItem( + child: Text(level), + value: level, + )) + .toList(), + // value: selectedLevel, + onChanged: (value) async { + selectedLevel = value!; + }, + ), + + const SizedBox( + height: 8, + ), + + FormBuilderDropdown( + name: 'establishment', + decoration: normalTextFieldStyle( + "Select establishment", "establishments"), + isExpanded: true, + items: establishments + .map((est) => DropdownMenuItem( + child: Text(est), + value: est, + )) + .toList(), + // value: selectedArea, + onChanged: (value) async { + selectedArea = value!; + }), + const SizedBox( + height: 8, + ), + DropdownButtonFormField( + decoration: normalTextFieldStyle( + "Select establishment", "establishments"), + isExpanded: true, + items: establishments + .map((est) => DropdownMenuItem( + child: Text(est), + value: est, + )) + .toList(), + // value: selectedArea, + onChanged: (value) async { + selectedArea = value!; + }), + const Expanded( + child: SizedBox(), + ), + SizedBox( + width: double.infinity, + height: screenHeight * .06, + child: ElevatedButton( + style: secondaryBtnStyle( + second, Colors.transparent, Colors.white54), + child: const Text( + submit, + style: TextStyle(color: Colors.white), + ), + onPressed: () { + // if (_formKey.currentState.validate()) { + // _formKey.currentState.save(); + // BlocProvider.of(context) + // .add(UserWebLogin( + // password: password, + // username: username)); + // } + }, + ), + ), + + const SizedBox( + height: 8, + ), + ], + ), + ), + ), + )), + ); + } +} diff --git a/unit2/lib/test_data.dart b/unit2/lib/test_data.dart new file mode 100644 index 0000000..57b6cd3 --- /dev/null +++ b/unit2/lib/test_data.dart @@ -0,0 +1,3 @@ +List levels = ['Establishments', 'Office']; +List establishments = ['Provincial Government of Agusan del Norte']; +List checkPointAreas = ['Agusan Up', 'Bids and Awards Committee','Cabadbaran District Hospital','Commision on Audit']; diff --git a/unit2/lib/utils/text_container.dart b/unit2/lib/utils/text_container.dart index a214c0b..1cb3b80 100644 --- a/unit2/lib/utils/text_container.dart +++ b/unit2/lib/utils/text_container.dart @@ -6,7 +6,7 @@ const String numericValidator = "Please a number only"; const String mobile1 = "Mobile number 1"; const String mobile2 = "Mobile number 2"; const String currentLocation = "You current location"; -const String noModule = "No Module Assigned"; +const String noModule = "No Module Assign"; const String noModuleSubTitle = "Please contact the admin if you want to access a module."; const String submit = "SUBMIT"; @@ -18,6 +18,7 @@ const String unit2ModuleScreen = "uniT2 Modules"; const String welcome = "Welcome to!"; const String unitApp = 'uniT-App'; const String loginToContinue = "Please login to continue."; -const String loginViaQr = "Login via QR code"; +const String loginViaQr = "Login via QR code"; const String emergencyReponseLabel = " Request Emergency Response "; -const String requestSOS = "Request SOS"; \ No newline at end of file +const String requestSOS = "Request SOS"; +const String qrScannerTitle = "QR-Code Scanner"; diff --git a/unit2/pubspec.lock b/unit2/pubspec.lock index 06adf03..93767ed 100644 --- a/unit2/pubspec.lock +++ b/unit2/pubspec.lock @@ -558,6 +558,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.4.12" + toggle_switch: + dependency: "direct main" + description: + name: toggle_switch + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" typed_data: dependency: transitive description: diff --git a/unit2/pubspec.yaml b/unit2/pubspec.yaml index af6129a..729c747 100644 --- a/unit2/pubspec.yaml +++ b/unit2/pubspec.yaml @@ -49,6 +49,7 @@ dependencies: auto_size_text: ^3.0.0 animate_do: ^3.0.2 flutter_spinkit: ^5.1.0 + toggle_switch: ^2.0.1 dev_dependencies: flutter_test: From 7b880a08bd67ea216d3b5a02e0d24628ac860440 Mon Sep 17 00:00:00 2001 From: rodolfobacuinjr Date: Tue, 6 Dec 2022 11:10:34 +0800 Subject: [PATCH 05/86] create user interface for QR-Code scanner scan qr codes --- .../components/save_settings.dart | 41 +++++ .../roles/qr_code_scanner.dart/scan.dart | 150 ++++++++++++++++++ unit2/lib/utils/router.dart | 3 +- unit2/lib/utils/text_container.dart | 3 + 4 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 unit2/lib/screen/unit2/roles/qr_code_scanner.dart/components/save_settings.dart create mode 100644 unit2/lib/screen/unit2/roles/qr_code_scanner.dart/scan.dart diff --git a/unit2/lib/screen/unit2/roles/qr_code_scanner.dart/components/save_settings.dart b/unit2/lib/screen/unit2/roles/qr_code_scanner.dart/components/save_settings.dart new file mode 100644 index 0000000..ce4b27c --- /dev/null +++ b/unit2/lib/screen/unit2/roles/qr_code_scanner.dart/components/save_settings.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; + +import '../../../../../theme-data.dart/colors.dart'; + +class SelectedState extends StatelessWidget { + final String title; + final String subtitle; + const SelectedState({Key? key,required this.subtitle, required this.title}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + height: 80, + width: double.infinity, + padding: const EdgeInsets.all(10), + child: Wrap( + alignment: WrapAlignment.center, + runAlignment: WrapAlignment.center, + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + title, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.button!.copyWith( + color: third, fontWeight: FontWeight.bold, fontSize: 18), + ), + Text( + subtitle, + style: Theme.of(context).textTheme.labelMedium, + ), + const Divider(), + + ]), + ], + ), + ); + } +} diff --git a/unit2/lib/screen/unit2/roles/qr_code_scanner.dart/scan.dart b/unit2/lib/screen/unit2/roles/qr_code_scanner.dart/scan.dart new file mode 100644 index 0000000..8908e49 --- /dev/null +++ b/unit2/lib/screen/unit2/roles/qr_code_scanner.dart/scan.dart @@ -0,0 +1,150 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/container.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:fluttericon/entypo_icons.dart'; +import 'package:unit2/utils/text_container.dart'; + +import '../../../../theme-data.dart/colors.dart'; +import '../../../../utils/global.dart'; +import 'components/save_settings.dart'; + +class QRCodeScanner extends StatelessWidget { + const QRCodeScanner({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text(qrScannerTitle), + centerTitle: true, + backgroundColor: primary, + ), + body: SizedBox( + height: screenHeight * 100, + width: double.infinity, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const SizedBox( + height: 40, + ), + GestureDetector( + onTap: () {}, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + height: 160, + child: Image.asset('assets/pngs/qr-scan.png')), + const SizedBox( + height: 8, + ), + Text( + "TAP TO SCAN QR CODE", + textAlign: TextAlign.center, + style: Theme.of(context) + .textTheme + .displayMedium! + .copyWith( + fontSize: 22, + color: primary, + fontWeight: FontWeight.bold), + ), + const SizedBox( + height: 8, + ), + //TODO add API data + "INCOMING" == "INCOMING" + ? SizedBox( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "INCOMING", + textAlign: TextAlign.center, + style: Theme.of(context) + .textTheme + .displayMedium! + .copyWith( + fontSize: 20, + color: success2, + fontWeight: FontWeight.bold), + ), + const Icon( + Entypo.down_bold, + color: success2, + ) + ], + ), + ) + : SizedBox( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + //TODO add API data + "INCOMING", + textAlign: TextAlign.center, + style: Theme.of(context) + .textTheme + .displayMedium! + .copyWith( + fontSize: 20, + color: second, + fontWeight: FontWeight.bold), + ), + const Icon( + Entypo.up_bold, + color: second, + ) + ], + ), + ) + ], + ), + ), + Container( + padding: const EdgeInsets.only(top: 12), + decoration: const BoxDecoration( + boxShadow: [ + BoxShadow( + color: Colors.black38, + blurRadius: 30, + offset: Offset(-5, 0), + ), + ], + color: Colors.white, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(15), + topRight: Radius.circular(15))), + width: double.infinity, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: const [ + SelectedState( + //TODO add api data + + title: "Provincial Government of ADN", + subtitle: establishment, + ), + Center( + child: SelectedState( + //TODO add api data + title: "Agusan Up", + subtitle: checkpointArea, + ), + ), + SelectedState( + //TODO add api data + title: "INCOMING", + subtitle: scanMode, + ), + ], + ), + ) + ], + ), + )); + } +} diff --git a/unit2/lib/utils/router.dart b/unit2/lib/utils/router.dart index 7794074..a5e5216 100644 --- a/unit2/lib/utils/router.dart +++ b/unit2/lib/utils/router.dart @@ -1,4 +1,5 @@ import 'package:go_router/go_router.dart'; +import 'package:unit2/screen/unit2/roles/qr_code_scanner.dart/scan.dart'; import '../screen/sos/add_mobile.dart'; import '../screen/sos/request_sos.dart'; import '../screen/unit2/login/login.dart'; @@ -9,7 +10,7 @@ final GoRouter goRouter = GoRouter(routes: [ GoRoute( path: '/', name: 'login', - builder: (context, state) => const UniT2Login(), + builder: (context, state) => const QRCodeScanner(), routes: [ GoRoute( name: 'home', diff --git a/unit2/lib/utils/text_container.dart b/unit2/lib/utils/text_container.dart index 1cb3b80..856ae72 100644 --- a/unit2/lib/utils/text_container.dart +++ b/unit2/lib/utils/text_container.dart @@ -22,3 +22,6 @@ const String loginViaQr = "Login via QR code"; const String emergencyReponseLabel = " Request Emergency Response "; const String requestSOS = "Request SOS"; const String qrScannerTitle = "QR-Code Scanner"; +const String establishment = "Establishment"; +const String checkpointArea = "Checkpoint Area"; +const String scanMode = 'Scan Mode'; \ No newline at end of file From cbca4ec97d5933cf6920e675b874d67dd437a0e5 Mon Sep 17 00:00:00 2001 From: rodolfobacuinjr Date: Mon, 12 Dec 2022 16:44:26 +0800 Subject: [PATCH 06/86] create user interface for registration in charge and create password for new user --- .../docsms/components/doc_info_tile.dart | 25 ++ unit2/lib/screens/docsms/request_receipt.dart | 156 ++++++++++++ .../{screen => screens}/sos/add_mobile.dart | 15 +- .../sos/components/mobile.dart | 0 .../{screen => screens}/sos/request_sos.dart | 2 +- .../{screen => screens}/sos/sos_received.dart | 0 .../components/drawer-screen.dart | 0 .../components/empty_module.dart | 0 .../homepage.dart/components/menu-screen.dart | 0 .../unit2/homepage.dart/components/menu.dart | 0 .../unit2/homepage.dart/module-screen.dart | 0 .../login/components/login-via-qr-label.dart | 0 .../login/functions/press-again-to-exit.dart | 0 .../unit2/login/login.dart | 11 +- unit2/lib/screens/unit2/login/register.dart | 202 +++++++++++++++ .../unit2/profile/components/cover-image.dart | 0 .../unit2/profile/profile.dart | 0 .../components/custom_switch.dart | 0 .../components/save_settings.dart | 0 .../roles/qr_code_scanner.dart/scan.dart | 0 .../qr_code_scanner.dart/settings_screen.dart | 2 +- .../components/add.dart | 232 ++++++++++++++++++ .../components/request_qr.dart | 19 ++ .../components/sync.dart | 63 +++++ .../components/view.dart | 100 ++++++++ .../roles/registration_in_charge/home.dart | 88 +++++++ unit2/lib/test_data.dart | 86 ++++++- unit2/lib/theme-data.dart/colors.dart | 7 + unit2/lib/theme-data.dart/text-styles.dart | 14 ++ unit2/lib/utils/router.dart | 17 +- unit2/lib/utils/text_container.dart | 17 +- unit2/lib/utils/validators.dart | 18 +- unit2/lib/widgets/costum_divider.dart | 16 ++ unit2/lib/widgets/label.dart | 22 ++ unit2/lib/widgets/text_icon.dart | 30 +++ unit2/notes.txt | 4 + unit2/pubspec.lock | 30 ++- unit2/pubspec.yaml | 4 + 38 files changed, 1148 insertions(+), 32 deletions(-) create mode 100644 unit2/lib/screens/docsms/components/doc_info_tile.dart create mode 100644 unit2/lib/screens/docsms/request_receipt.dart rename unit2/lib/{screen => screens}/sos/add_mobile.dart (89%) rename unit2/lib/{screen => screens}/sos/components/mobile.dart (100%) rename unit2/lib/{screen => screens}/sos/request_sos.dart (98%) rename unit2/lib/{screen => screens}/sos/sos_received.dart (100%) rename unit2/lib/{screen => screens}/unit2/homepage.dart/components/drawer-screen.dart (100%) rename unit2/lib/{screen => screens}/unit2/homepage.dart/components/empty_module.dart (100%) rename unit2/lib/{screen => screens}/unit2/homepage.dart/components/menu-screen.dart (100%) rename unit2/lib/{screen => screens}/unit2/homepage.dart/components/menu.dart (100%) rename unit2/lib/{screen => screens}/unit2/homepage.dart/module-screen.dart (100%) rename unit2/lib/{screen => screens}/unit2/login/components/login-via-qr-label.dart (100%) rename unit2/lib/{screen => screens}/unit2/login/functions/press-again-to-exit.dart (100%) rename unit2/lib/{screen => screens}/unit2/login/login.dart (96%) create mode 100644 unit2/lib/screens/unit2/login/register.dart rename unit2/lib/{screen => screens}/unit2/profile/components/cover-image.dart (100%) rename unit2/lib/{screen => screens}/unit2/profile/profile.dart (100%) rename unit2/lib/{screen => screens}/unit2/roles/qr_code_scanner.dart/components/custom_switch.dart (100%) rename unit2/lib/{screen => screens}/unit2/roles/qr_code_scanner.dart/components/save_settings.dart (100%) rename unit2/lib/{screen => screens}/unit2/roles/qr_code_scanner.dart/scan.dart (100%) rename unit2/lib/{screen => screens}/unit2/roles/qr_code_scanner.dart/settings_screen.dart (98%) create mode 100644 unit2/lib/screens/unit2/roles/registration_in_charge/components/add.dart create mode 100644 unit2/lib/screens/unit2/roles/registration_in_charge/components/request_qr.dart create mode 100644 unit2/lib/screens/unit2/roles/registration_in_charge/components/sync.dart create mode 100644 unit2/lib/screens/unit2/roles/registration_in_charge/components/view.dart create mode 100644 unit2/lib/screens/unit2/roles/registration_in_charge/home.dart create mode 100644 unit2/lib/widgets/costum_divider.dart create mode 100644 unit2/lib/widgets/label.dart create mode 100644 unit2/lib/widgets/text_icon.dart create mode 100644 unit2/notes.txt diff --git a/unit2/lib/screens/docsms/components/doc_info_tile.dart b/unit2/lib/screens/docsms/components/doc_info_tile.dart new file mode 100644 index 0000000..a209c76 --- /dev/null +++ b/unit2/lib/screens/docsms/components/doc_info_tile.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/container.dart'; +import 'package:flutter/src/widgets/framework.dart'; + +import '../../../theme-data.dart/text-styles.dart'; +import '../../../utils/global.dart'; + +class DocInfo extends StatelessWidget { + final String title; + final String subTitle; + const DocInfo({super.key, required this.title, required this.subTitle}); + + @override + Widget build(BuildContext context) { + return ListTile( + dense: true, + title: Text( + title, + style: titleTextStyle() + .copyWith(color: Colors.black87, fontSize: blockSizeVertical * 2), + ), + subtitle: Text(subTitle), + ); + } +} diff --git a/unit2/lib/screens/docsms/request_receipt.dart b/unit2/lib/screens/docsms/request_receipt.dart new file mode 100644 index 0000000..338cb98 --- /dev/null +++ b/unit2/lib/screens/docsms/request_receipt.dart @@ -0,0 +1,156 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/container.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:fluttericon/entypo_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:unit2/screens/docsms/components/doc_info_tile.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/text_container.dart'; +import 'package:unit2/widgets/costum_divider.dart'; +import 'package:unit2/widgets/text_icon.dart'; + +import '../../test_data.dart'; +import '../../theme-data.dart/btn-style.dart'; +import '../../theme-data.dart/form-style.dart'; +import '../../utils/global.dart'; + +class RequetAutoReceipt extends StatefulWidget { + RequetAutoReceipt({super.key}); + + @override + State createState() => _RequetAutoReceiptState(); +} + +class _RequetAutoReceiptState extends State { + final _formKey = GlobalKey(); + @override + Widget build(BuildContext context) { + return SafeArea( + child: Scaffold( + appBar: AppBar( + backgroundColor: primary, + ), + body: SingleChildScrollView( + // ignore: avoid_unnecessary_containers + child: Container( + padding: const EdgeInsets.symmetric(vertical: 8), + child: + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + const DocInfo(title: "4427", subTitle: documentId), + const CostumDivider(), + const DocInfo(title: "Purchase of Diesel", subTitle: documentTitle), + const CostumDivider(), + const DocInfo(title: "N/A", subTitle: documentSubject), + const CostumDivider(), + const DocInfo(title: "Request for Quotation", subTitle: documentType), + const CostumDivider(), + Form( + child: Column(children: [ + FormBuilder( + key: _formKey, + child: Container( + padding: const EdgeInsets.symmetric( + vertical: 12, horizontal: 25), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const TextIcon(title: "Source", icon: Entypo.reply), + const SizedBox( + height: 8, + ), + Text( + "Source Remarks", + style: Theme.of(context).textTheme.caption, + ), + const SizedBox( + height: 12, + ), + FormBuilderTextField( + name: "remarks", + validator: FormBuilderValidators.compose([ + FormBuilderValidators.required( + errorText: "Remarks is required") + ]), + autovalidateMode: + AutovalidateMode.onUserInteraction, + maxLines: 5, + decoration: normalTextFieldStyle( + "Enter your remarks", "..."), + ), + const SizedBox( + height: 8, + ), + const TextIcon( + title: "Destination", icon: Entypo.forward), + const SizedBox( + height: 8, + ), + FormBuilderDropdown( + name: 'department', + autofocus: false, + validator: FormBuilderValidators.compose([ + FormBuilderValidators.required( + errorText: "Department is required") + ]), + decoration: + normalTextFieldStyle("Department", ""), + items: puroks + .map((purok) => DropdownMenuItem( + value: purok, + child: Text(purok), + )) + .toList()), + const SizedBox( + height: 12, + ), + FormBuilderDropdown( + name: 'purok', + autofocus: false, + validator: FormBuilderValidators.compose([ + FormBuilderValidators.required( + errorText: "Purok is required") + ]), + decoration: + normalTextFieldStyle("Substation", ""), + items: puroks + .map((purok) => DropdownMenuItem( + value: purok, + child: Text(purok), + )) + .toList()), + const SizedBox( + height: 24, + ), + SizedBox( + width: double.infinity, + height: screenHeight * .06, + child: ElevatedButton( + style: secondaryBtnStyle( + second, Colors.transparent, Colors.white54), + child: const Text( + "Request Auto Receipt", + style: TextStyle(color: Colors.white), + ), + onPressed: () { + if (_formKey.currentState! + .saveAndValidate()) { + debugPrint(_formKey + .currentState!.value['remarks']); + } + }, + ), + ), + ], + ), + )), + ]), + ) + ]), + ), + ), + ), + ); + } +} diff --git a/unit2/lib/screen/sos/add_mobile.dart b/unit2/lib/screens/sos/add_mobile.dart similarity index 89% rename from unit2/lib/screen/sos/add_mobile.dart rename to unit2/lib/screens/sos/add_mobile.dart index c102a0a..4d7ee6a 100644 --- a/unit2/lib/screen/sos/add_mobile.dart +++ b/unit2/lib/screens/sos/add_mobile.dart @@ -94,7 +94,6 @@ class AddMobile extends StatelessWidget { ), FormBuilderTextField( name: 'mobile2', - validator: mobileNumberValidator, decoration: normalTextFieldStyle( "mobile number 2", "+63")), @@ -113,14 +112,12 @@ class AddMobile extends StatelessWidget { style: TextStyle(color: Colors.white), ), onPressed: () { - context.go( - context.namedLocation('request-sos')); - // if (_formKey.currentState.validate()) { - // _formKey.currentState.save(); - // BlocProvider.of(context) - // .add(UserWebLogin( - // password: password, - // username: username)); + if (_formKey.currentState! + .saveAndValidate()) { + context.go(context + .namedLocation('request-sos')); + } + // } }, ), diff --git a/unit2/lib/screen/sos/components/mobile.dart b/unit2/lib/screens/sos/components/mobile.dart similarity index 100% rename from unit2/lib/screen/sos/components/mobile.dart rename to unit2/lib/screens/sos/components/mobile.dart diff --git a/unit2/lib/screen/sos/request_sos.dart b/unit2/lib/screens/sos/request_sos.dart similarity index 98% rename from unit2/lib/screen/sos/request_sos.dart rename to unit2/lib/screens/sos/request_sos.dart index e284efd..76cab37 100644 --- a/unit2/lib/screen/sos/request_sos.dart +++ b/unit2/lib/screens/sos/request_sos.dart @@ -6,7 +6,7 @@ import 'package:flutter_svg/svg.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:fluttericon/typicons_icons.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; -import 'package:unit2/screen/sos/components/mobile.dart'; +import 'package:unit2/screens/sos/components/mobile.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; diff --git a/unit2/lib/screen/sos/sos_received.dart b/unit2/lib/screens/sos/sos_received.dart similarity index 100% rename from unit2/lib/screen/sos/sos_received.dart rename to unit2/lib/screens/sos/sos_received.dart diff --git a/unit2/lib/screen/unit2/homepage.dart/components/drawer-screen.dart b/unit2/lib/screens/unit2/homepage.dart/components/drawer-screen.dart similarity index 100% rename from unit2/lib/screen/unit2/homepage.dart/components/drawer-screen.dart rename to unit2/lib/screens/unit2/homepage.dart/components/drawer-screen.dart diff --git a/unit2/lib/screen/unit2/homepage.dart/components/empty_module.dart b/unit2/lib/screens/unit2/homepage.dart/components/empty_module.dart similarity index 100% rename from unit2/lib/screen/unit2/homepage.dart/components/empty_module.dart rename to unit2/lib/screens/unit2/homepage.dart/components/empty_module.dart diff --git a/unit2/lib/screen/unit2/homepage.dart/components/menu-screen.dart b/unit2/lib/screens/unit2/homepage.dart/components/menu-screen.dart similarity index 100% rename from unit2/lib/screen/unit2/homepage.dart/components/menu-screen.dart rename to unit2/lib/screens/unit2/homepage.dart/components/menu-screen.dart diff --git a/unit2/lib/screen/unit2/homepage.dart/components/menu.dart b/unit2/lib/screens/unit2/homepage.dart/components/menu.dart similarity index 100% rename from unit2/lib/screen/unit2/homepage.dart/components/menu.dart rename to unit2/lib/screens/unit2/homepage.dart/components/menu.dart diff --git a/unit2/lib/screen/unit2/homepage.dart/module-screen.dart b/unit2/lib/screens/unit2/homepage.dart/module-screen.dart similarity index 100% rename from unit2/lib/screen/unit2/homepage.dart/module-screen.dart rename to unit2/lib/screens/unit2/homepage.dart/module-screen.dart diff --git a/unit2/lib/screen/unit2/login/components/login-via-qr-label.dart b/unit2/lib/screens/unit2/login/components/login-via-qr-label.dart similarity index 100% rename from unit2/lib/screen/unit2/login/components/login-via-qr-label.dart rename to unit2/lib/screens/unit2/login/components/login-via-qr-label.dart diff --git a/unit2/lib/screen/unit2/login/functions/press-again-to-exit.dart b/unit2/lib/screens/unit2/login/functions/press-again-to-exit.dart similarity index 100% rename from unit2/lib/screen/unit2/login/functions/press-again-to-exit.dart rename to unit2/lib/screens/unit2/login/functions/press-again-to-exit.dart diff --git a/unit2/lib/screen/unit2/login/login.dart b/unit2/lib/screens/unit2/login/login.dart similarity index 96% rename from unit2/lib/screen/unit2/login/login.dart rename to unit2/lib/screens/unit2/login/login.dart index 31da7a8..e19f924 100644 --- a/unit2/lib/screen/unit2/login/login.dart +++ b/unit2/lib/screens/unit2/login/login.dart @@ -161,7 +161,11 @@ class _UniT2LoginState extends State { style: TextStyle(color: Colors.white), ), onPressed: () { - context.go(context.namedLocation('home')); + if (_formKey.currentState! + .saveAndValidate()) { + context.go(context.namedLocation('home')); + } + // if (_formKey.currentState.validate()) { // _formKey.currentState.save(); // BlocProvider.of(context) @@ -193,6 +197,8 @@ class _UniT2LoginState extends State { style: TextStyle(color: second), ), onPressed: () async { + context.go(context.namedLocation('home')); + // BlocProvider.of(context) // .add(QRCodelogin()); }, @@ -217,8 +223,7 @@ class _UniT2LoginState extends State { style: mainBtnStyle( third, Colors.transparent, Colors.white38), onPressed: () { - context - .go(context.namedLocation('add-mobile')); + context.goNamed('add-mobile'); }, label: const Text( requestSOS, diff --git a/unit2/lib/screens/unit2/login/register.dart b/unit2/lib/screens/unit2/login/register.dart new file mode 100644 index 0000000..3d4e614 --- /dev/null +++ b/unit2/lib/screens/unit2/login/register.dart @@ -0,0 +1,202 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/container.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:fluttericon/font_awesome5_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:unit2/theme-data.dart/btn-style.dart'; +import 'package:unit2/widgets/wave.dart'; + +import '../../../theme-data.dart/colors.dart'; +import '../../../theme-data.dart/form-style.dart'; +import '../../../utils/global.dart'; +import '../../../utils/text_container.dart'; +import '../../../utils/validators.dart'; + +class Register extends StatefulWidget { + const Register({super.key}); + + @override + State createState() => _RegisterState(); +} + +class _RegisterState extends State { + bool showSuffixIcon = false; + bool _showPassword = true; + final _formKey = GlobalKey(); + @override + Widget build(BuildContext context) { + return SafeArea( + child: Scaffold( + resizeToAvoidBottomInset: true, + body: SingleChildScrollView( + child: Stack( + children: [ + Wave(height: blockSizeVertical * 8), + Positioned( + bottom: 0, child: WaveReverse(height: blockSizeVertical * 8)), + Container( + height: screenHeight * .97, + padding: const EdgeInsets.symmetric(horizontal: 15), + child: FormBuilder( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SvgPicture.asset( + 'assets/svgs/logo.svg', + height: blockSizeVertical * 16, + allowDrawingOutsideViewBox: true, + color: primary, + ), + Text(unitApp, + style: TextStyle( + fontSize: blockSizeVertical * 8, + fontWeight: FontWeight.w800, + letterSpacing: .2, + height: 1, + color: Colors.black87)), + Text( + registerToContinue, + style: TextStyle( + fontSize: blockSizeVertical * 2, + height: 1.5, + fontWeight: FontWeight.w600), + ), + const SizedBox( + height: 15, + ), + // Password + FormBuilderTextField( + name: 'password', + validator: registerPasswordValidator, + // initialValue: state.password, + onChanged: (value) { + value!.isEmpty + ? setState(() { + showSuffixIcon = false; + }) + : setState(() { + showSuffixIcon = true; + }); + }, + autofocus: false, + style: const TextStyle( + fontWeight: FontWeight.bold, color: Colors.black87), + decoration: loginTextFieldStyle().copyWith( + suffixIcon: Visibility( + visible: showSuffixIcon, + child: _showPassword + ? IconButton( + icon: Icon(FontAwesome5.eye_slash, + size: 24, + color: Theme.of(context) + .textTheme + .displayLarge + ?.color), + onPressed: () { + setState(() { + _showPassword = false; + }); + }, + ) + : IconButton( + onPressed: () { + setState(() { + _showPassword = true; + }); + }, + icon: Icon(FontAwesome5.eye, + size: 24, + color: Theme.of(context) + .textTheme + .displayLarge + ?.color)), + ), + prefixIcon: const Icon(Icons.lock), + labelText: "Password", + hintText: "Enter Password..."), + obscureText: _showPassword ? true : false, + ), + const SizedBox( + height: 15, + ), + FormBuilderTextField( + name: 'confirmPassword', + validator: registerPasswordValidator, + // initialValue: state.password, + onChanged: (value) { + value!.isEmpty + ? setState(() { + showSuffixIcon = false; + }) + : setState(() { + showSuffixIcon = true; + }); + }, + autofocus: false, + style: const TextStyle( + fontWeight: FontWeight.bold, color: Colors.black87), + decoration: loginTextFieldStyle().copyWith( + suffixIcon: Visibility( + visible: showSuffixIcon, + child: _showPassword + ? IconButton( + icon: Icon(FontAwesome5.eye_slash, + size: 24, + color: Theme.of(context) + .textTheme + .displayLarge + ?.color), + onPressed: () { + setState(() { + _showPassword = false; + }); + }, + ) + : IconButton( + onPressed: () { + setState(() { + _showPassword = true; + }); + }, + icon: Icon(FontAwesome5.eye, + size: 24, + color: Theme.of(context) + .textTheme + .displayLarge + ?.color)), + ), + prefixIcon: const Icon(Icons.lock), + labelText: "Confirm Password", + hintText: "Confirm Password..."), + obscureText: _showPassword ? true : false, + ), + const SizedBox( + height: 15, + ), + SizedBox( + width: double.infinity, + height: blockSizeVertical * 6, + child: ElevatedButton( + style: secondaryBtnStyle( + second, Colors.transparent, primary), + onPressed: () { + if (_formKey.currentState!.saveAndValidate()) {} + }, + child: const Text("Register")), + ) + ], + ), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/unit2/lib/screen/unit2/profile/components/cover-image.dart b/unit2/lib/screens/unit2/profile/components/cover-image.dart similarity index 100% rename from unit2/lib/screen/unit2/profile/components/cover-image.dart rename to unit2/lib/screens/unit2/profile/components/cover-image.dart diff --git a/unit2/lib/screen/unit2/profile/profile.dart b/unit2/lib/screens/unit2/profile/profile.dart similarity index 100% rename from unit2/lib/screen/unit2/profile/profile.dart rename to unit2/lib/screens/unit2/profile/profile.dart diff --git a/unit2/lib/screen/unit2/roles/qr_code_scanner.dart/components/custom_switch.dart b/unit2/lib/screens/unit2/roles/qr_code_scanner.dart/components/custom_switch.dart similarity index 100% rename from unit2/lib/screen/unit2/roles/qr_code_scanner.dart/components/custom_switch.dart rename to unit2/lib/screens/unit2/roles/qr_code_scanner.dart/components/custom_switch.dart diff --git a/unit2/lib/screen/unit2/roles/qr_code_scanner.dart/components/save_settings.dart b/unit2/lib/screens/unit2/roles/qr_code_scanner.dart/components/save_settings.dart similarity index 100% rename from unit2/lib/screen/unit2/roles/qr_code_scanner.dart/components/save_settings.dart rename to unit2/lib/screens/unit2/roles/qr_code_scanner.dart/components/save_settings.dart diff --git a/unit2/lib/screen/unit2/roles/qr_code_scanner.dart/scan.dart b/unit2/lib/screens/unit2/roles/qr_code_scanner.dart/scan.dart similarity index 100% rename from unit2/lib/screen/unit2/roles/qr_code_scanner.dart/scan.dart rename to unit2/lib/screens/unit2/roles/qr_code_scanner.dart/scan.dart diff --git a/unit2/lib/screen/unit2/roles/qr_code_scanner.dart/settings_screen.dart b/unit2/lib/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart similarity index 98% rename from unit2/lib/screen/unit2/roles/qr_code_scanner.dart/settings_screen.dart rename to unit2/lib/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart index 5a79040..7f3e4a4 100644 --- a/unit2/lib/screen/unit2/roles/qr_code_scanner.dart/settings_screen.dart +++ b/unit2/lib/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart @@ -4,7 +4,7 @@ import 'package:flutter_svg/svg.dart'; import 'package:fluttericon/entypo_icons.dart'; import 'package:fluttericon/modern_pictograms_icons.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; -import 'package:unit2/screen/unit2/roles/qr_code_scanner.dart/components/custom_switch.dart'; +import 'package:unit2/screens/unit2/roles/qr_code_scanner.dart/components/custom_switch.dart'; import 'package:unit2/test_data.dart'; import 'package:unit2/utils/text_container.dart'; import '../../../../theme-data.dart/btn-style.dart'; diff --git a/unit2/lib/screens/unit2/roles/registration_in_charge/components/add.dart b/unit2/lib/screens/unit2/roles/registration_in_charge/components/add.dart new file mode 100644 index 0000000..45b3b71 --- /dev/null +++ b/unit2/lib/screens/unit2/roles/registration_in_charge/components/add.dart @@ -0,0 +1,232 @@ +import 'package:date_time_picker/date_time_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:intl/intl.dart'; +import 'package:unit2/theme-data.dart/form-style.dart'; +import 'package:unit2/utils/global.dart'; +import 'package:unit2/utils/text_container.dart'; +import 'package:unit2/widgets/label.dart'; +import '../../../../../test_data.dart'; +import '../../../../../theme-data.dart/btn-style.dart'; +import '../../../../../theme-data.dart/colors.dart'; + +class AddPerson extends StatefulWidget { + const AddPerson({super.key}); + + @override + State createState() => _AddPersonState(); +} + +class _AddPersonState extends State { + final _formKey = GlobalKey(); + + DateFormat dteFormat = DateFormat("y-M-d"); + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 25), + child: SingleChildScrollView( + child: FormBuilder( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Label(text: basicInformation), + //Firstname + FormBuilderTextField( + name: 'firstname', + validator: FormBuilderValidators.required( + errorText: "First name is required"), + autofocus: false, + decoration: normalTextFieldStyle("First name", ""), + textCapitalization: TextCapitalization.characters, + keyboardType: TextInputType.text, + ), + const SizedBox( + height: 10, + ), + //Firstname + FormBuilderTextField( + name: 'lastname', + validator: FormBuilderValidators.required( + errorText: "Middle name is required"), + autofocus: false, + textCapitalization: TextCapitalization.characters, + keyboardType: TextInputType.text, + decoration: normalTextFieldStyle("Last name", "")), + const SizedBox( + height: 10, + ), + Row( + children: [ + Expanded( + flex: 2, + child: FormBuilderTextField( + name: 'middlename', + validator: FormBuilderValidators.required( + errorText: "Middle name is required"), + autofocus: false, + textCapitalization: TextCapitalization.characters, + keyboardType: TextInputType.text, + decoration: normalTextFieldStyle("Middle name", "")), + ), + const SizedBox( + width: 10, + ), + Expanded( + flex: 1, + child: DateTimePicker( + decoration: normalTextFieldStyle("Birth date", ""), + initialValue: '', + firstDate: DateTime(2000), + lastDate: DateTime(2100), + dateLabelText: 'Date', + onChanged: (val) => print(val), + validator: (val) { + if (val!.isEmpty) { + return "Birthdate is required"; + } + return null; + }, + onSaved: (val) => print(val), + ), + ) + ], + ), + const SizedBox( + height: 10, + ), + Row( + children: [ + Expanded( + flex: 1, + child: FormBuilderTextField( + name: 'extension-name', + autofocus: false, + textCapitalization: TextCapitalization.characters, + keyboardType: TextInputType.text, + decoration: + normalTextFieldStyle("Extenstion name", "")), + ), + const SizedBox( + width: 10, + ), + Expanded( + flex: 1, + child: FormBuilderDropdown( + validator: FormBuilderValidators.required( + errorText: "Gender is required"), + name: 'gender', + autofocus: false, + decoration: normalTextFieldStyle("Gender", ""), + items: genders + .map((gender) => DropdownMenuItem( + value: gender, + child: Text(gender), + )) + .toList()), + ) + ], + ), + const Label(text: address), + FormBuilderDropdown( + name: 'region', + autofocus: false, + validator: FormBuilderValidators.required( + errorText: "Region is required"), + decoration: normalTextFieldStyle("Region", ""), + items: regions + .map((region) => DropdownMenuItem( + value: region, + child: Text(region), + )) + .toList()), + const SizedBox( + height: 10, + ), + FormBuilderDropdown( + name: 'province', + autofocus: false, + validator: FormBuilderValidators.required( + errorText: "Province is required"), + decoration: normalTextFieldStyle("Province", ""), + items: provinces + .map((province) => DropdownMenuItem( + value: province, + child: Text(province), + )) + .toList()), + const SizedBox( + height: 10, + ), + FormBuilderDropdown( + name: 'municipality', + autofocus: false, + validator: FormBuilderValidators.required( + errorText: "Municipality is required"), + decoration: normalTextFieldStyle("Municipalities", ""), + items: municipalities + .map((municipality) => DropdownMenuItem( + value: municipality, + child: Text(municipality), + )) + .toList()), + const SizedBox( + height: 10, + ), + FormBuilderDropdown( + name: 'barangay', + autofocus: false, + validator: FormBuilderValidators.required( + errorText: "Barangay is required"), + decoration: normalTextFieldStyle("Barangay", ""), + items: barangays + .map((barangay) => DropdownMenuItem( + value: barangay, + child: Text(barangay), + )) + .toList()), + const SizedBox( + height: 10, + ), + FormBuilderDropdown( + name: 'purok', + autofocus: false, + validator: FormBuilderValidators.required( + errorText: "Purok is required"), + decoration: normalTextFieldStyle("Purok", ""), + items: puroks + .map((purok) => DropdownMenuItem( + value: purok, + child: Text(purok), + )) + .toList()), + const SizedBox( + height: 10, + ), + SizedBox( + width: double.infinity, + height: screenHeight * .06, + child: ElevatedButton( + style: secondaryBtnStyle( + second, Colors.transparent, Colors.white54), + child: const Text( + submit, + style: TextStyle(color: Colors.white), + ), + onPressed: () { + print(_formKey.currentState!.value['firstname']); + if (_formKey.currentState!.saveAndValidate()) {} + }, + ), + ), + const SizedBox( + height: 20, + ), + ], + )), + ), + ); + } +} diff --git a/unit2/lib/screens/unit2/roles/registration_in_charge/components/request_qr.dart b/unit2/lib/screens/unit2/roles/registration_in_charge/components/request_qr.dart new file mode 100644 index 0000000..7f95879 --- /dev/null +++ b/unit2/lib/screens/unit2/roles/registration_in_charge/components/request_qr.dart @@ -0,0 +1,19 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/src/widgets/container.dart'; +import 'package:flutter/src/widgets/framework.dart'; + +class RequestQR extends StatefulWidget { + const RequestQR({super.key}); + + @override + State createState() => _RequestQRState(); +} + +class _RequestQRState extends State { + @override + Widget build(BuildContext context) { + return Container( + child: Center(child: Text("Request QR")), + ); + } +} diff --git a/unit2/lib/screens/unit2/roles/registration_in_charge/components/sync.dart b/unit2/lib/screens/unit2/roles/registration_in_charge/components/sync.dart new file mode 100644 index 0000000..02e0236 --- /dev/null +++ b/unit2/lib/screens/unit2/roles/registration_in_charge/components/sync.dart @@ -0,0 +1,63 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/container.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:fluttericon/entypo_icons.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:fluttericon/linearicons_free_icons.dart'; +import 'package:fluttericon/octicons_icons.dart'; +import 'package:fluttericon/typicons_icons.dart'; +import 'package:unit2/widgets/label.dart'; + +import '../../../../../theme-data.dart/btn-style.dart'; +import '../../../../../theme-data.dart/colors.dart'; +import '../../../../../utils/global.dart'; +import '../../../../../utils/text_container.dart'; + +class SyncData extends StatefulWidget { + const SyncData({super.key}); + + @override + State createState() => _SyncDataState(); +} + +class _SyncDataState extends State { + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 25), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + const Label(text: syncTitle), + Icon( + Octicons.sync_icon, + size: blockSizeVertical * 25, + color: success2, + ), + Column( + children: [ + const Text(syncSubTittle), + const SizedBox( + height: 10, + ), + SizedBox( + width: double.infinity, + height: screenHeight * .06, + child: ElevatedButton( + style: secondaryBtnStyle( + second, Colors.transparent, Colors.white54), + child: const Text( + syncNow, + style: TextStyle(color: Colors.white), + ), + onPressed: () {}, + ), + ), + ], + ), + ], + ), + ); + } +} diff --git a/unit2/lib/screens/unit2/roles/registration_in_charge/components/view.dart b/unit2/lib/screens/unit2/roles/registration_in_charge/components/view.dart new file mode 100644 index 0000000..a103af9 --- /dev/null +++ b/unit2/lib/screens/unit2/roles/registration_in_charge/components/view.dart @@ -0,0 +1,100 @@ +import 'package:azlistview/azlistview.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/container.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:fluttericon/octicons_icons.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/global.dart'; + +import '../../../../../test_data.dart'; +import '../../../../../theme-data.dart/text-styles.dart'; + +class _AZItem extends ISuspensionBean { + final String title; + final String tag; + + _AZItem({required this.title, required this.tag}); + + @override + String getSuspensionTag() { + return tag; + } +} + +class ViewList extends StatefulWidget { + const ViewList({super.key}); + + @override + State createState() => _ViewListState(); +} + +class _ViewListState extends State { + List<_AZItem> convertedPersonList = []; + @override + void initState() { + addedPersons.sort( + (a, b) => a.lastname.toLowerCase().compareTo(b.lastname.toLowerCase())); + initList(addedPersons); + super.initState(); + } + + void initList(List persons) { + convertedPersonList = persons + .map((person) => _AZItem( + title: person.lastname + ' ' + person.name, + tag: person.lastname[0])) + .toList(); + SuspensionUtil.sortListBySuspensionTag(persons); + setState(() {}); + } + + @override + Widget build(BuildContext context) { + return AzListView( + data: addedPersons, + itemCount: addedPersons.length, + itemBuilder: (BuildContext context, int index) { + final person = convertedPersonList[index]; + return _buildListItem(person, true); + }, + physics: const BouncingScrollPhysics(), + indexBarItemHeight: blockSizeVertical * 3, + indexBarData: SuspensionUtil.getTagIndexList(addedPersons), + indexBarMargin: const EdgeInsets.all(0), + indexBarOptions: const IndexBarOptions( + selectItemDecoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.grey, + ), + selectTextStyle: + TextStyle(color: Colors.white, fontWeight: FontWeight.bold), + needRebuild: true, + )); + } + + _buildListItem(_AZItem person, bool selected) { + return Card( + elevation: 0, + child: ListTile( + onTap: (() => setState(() { + selected = !selected; + })), + dense: true, + subtitle: Text( + "December 15 1994", + style: Theme.of(context).textTheme.caption, + ), + leading: Checkbox( + onChanged: (value) { + selected = value!; + setState(() {}); + print(selected); + }, + value: selected, + ), + title: Text( + person.title, + style: personInfo(), + ))); + } +} diff --git a/unit2/lib/screens/unit2/roles/registration_in_charge/home.dart b/unit2/lib/screens/unit2/roles/registration_in_charge/home.dart new file mode 100644 index 0000000..d778b69 --- /dev/null +++ b/unit2/lib/screens/unit2/roles/registration_in_charge/home.dart @@ -0,0 +1,88 @@ +import 'package:convex_bottom_bar/convex_bottom_bar.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/container.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:fluttericon/entypo_icons.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:fluttericon/iconic_icons.dart'; +import 'package:unit2/screens/unit2/roles/registration_in_charge/components/add.dart'; +import 'package:unit2/screens/unit2/roles/registration_in_charge/components/request_qr.dart'; +import 'package:unit2/screens/unit2/roles/registration_in_charge/components/sync.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/text_container.dart'; + +import 'components/view.dart'; + +class RegistrationInCharge extends StatefulWidget { + const RegistrationInCharge({super.key}); + + @override + State createState() => _RegistrationInChargeState(); +} + +class _RegistrationInChargeState extends State { + final List _pages = [ + const ViewList(), + const AddPerson(), + const SyncData(), + const RequestQR() + ]; + int currentIndex = 0; + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text(registrationInChargeTitle), + backgroundColor: second, + centerTitle: true, + actions: [ + IconButton( + // ##SEARCH BUTTON + icon: const Icon(Icons.search), + color: Colors.white, + onPressed: () { + // Navigator.pushNamed(context, '/Registration Search'); + }, + ), + PopupMenuButton( + onSelected: (value) { + // SORT VIA DATE ADDED + if (value == 1) { + } + // SORT VIA LAST NAME + else if (value == 2) {} + }, + itemBuilder: (context) => [ + PopupMenuItem( + value: 1, + child: Row( + children: const [Text('Sort By Date Added')], + )), + PopupMenuItem( + value: 2, + child: Row( + children: const [Text('Sort by Lastname')], + )), + ]), + ], + ), + bottomNavigationBar: ConvexAppBar( + color: Colors.white70, + gradient: primaryGradient(), + style: TabStyle.react, + items: const [ + TabItem(icon: Entypo.home, title: "Home"), + TabItem(icon: Iconic.plus_circle, title: "Add"), + TabItem(icon: Iconic.loop, title: "Sync"), + TabItem(icon: FontAwesome.qrcode, title: "Request QR"), + ], + initialActiveIndex: 0, + onTap: (int i) { + setState(() { + currentIndex = i; + }); + }), + body: _pages[currentIndex], + ); + } +} diff --git a/unit2/lib/test_data.dart b/unit2/lib/test_data.dart index 57b6cd3..a16e395 100644 --- a/unit2/lib/test_data.dart +++ b/unit2/lib/test_data.dart @@ -1,3 +1,87 @@ +import 'package:azlistview/azlistview.dart'; + List levels = ['Establishments', 'Office']; List establishments = ['Provincial Government of Agusan del Norte']; -List checkPointAreas = ['Agusan Up', 'Bids and Awards Committee','Cabadbaran District Hospital','Commision on Audit']; +List checkPointAreas = [ + 'Agusan Up', + 'Bids and Awards Committee', + 'Cabadbaran District Hospital', + 'Commision on Audit' +]; + +final List genders = ["MALE", "FEMALE"]; +final List regions = [ + "Region I", + "Region II", + "Region III", + "Region IV", + "Region V" +]; +final List barangays = [ + "Ambago", + "Lingayao", + "Obrero", + "Bading", + "Limaha", + "Peqeno" +]; +final List puroks = [ + "Ambago", + "Lingayao", + "Obrero", + "Bading", + "Limaha", + "Peqeno" +]; + +final List municipalities = [ + "Buenavista", + "Butuan City", + "Carmen", + "Cabadbaran", + "Jabonga" +]; +final List provinces = [ + "Agusan del Norte", + "Agusan del Sur", + "Surigao del Norte", + "Surigao del Sur" +]; + +List addedPersons = [ + Person(name: "Nav", lastname: "Acuin"), + Person(name: "Jhonny", lastname: "Agoy"), + Person(name: "Philip", lastname: "Amaw"), + Person(name: "Jojo", lastname: "Asus"), + Person(name: "Naruto", lastname: "Acer"), + Person(name: "Agata", lastname: "Azarcon"), + Person(name: "Nav", lastname: "Amen"), + Person(name: "Cristine", lastname: "Albarina"), + Person(name: "Nav", lastname: "Zcuin"), + Person(name: "Jhonny", lastname: "Zgoy"), + Person(name: "Philip", lastname: "Zmaw"), + Person(name: "Jojo", lastname: "Zsus"), + Person(name: "Naruto", lastname: "Zcer"), + Person(name: "Agata", lastname: "Zzarcon"), + Person(name: "Nav", lastname: "Zmen"), + Person(name: "Cristine", lastname: "Zlbarina"), + Person(name: "Nav", lastname: "Bcuin"), + Person(name: "Jhonny", lastname: "Tgoy"), + Person(name: "Philip", lastname: "Smaw"), + Person(name: "Jojo", lastname: "Dsus"), + Person(name: "Naruto", lastname: "Fcer"), + Person(name: "Agata", lastname: "Ezarcon"), + Person(name: "Nav", lastname: "Cmen"), + Person(name: "Cristine", lastname: "Blbarina"), +]; + +class Person extends ISuspensionBean { + final String name; + final String lastname; + Person({required this.name, required this.lastname}); + + @override + String getSuspensionTag() { + return lastname[0]; + } +} diff --git a/unit2/lib/theme-data.dart/colors.dart b/unit2/lib/theme-data.dart/colors.dart index 219a3fd..6fe66a9 100644 --- a/unit2/lib/theme-data.dart/colors.dart +++ b/unit2/lib/theme-data.dart/colors.dart @@ -8,3 +8,10 @@ const fifth = Color(0xfffdfefd); const success = Color(0xffa5d6a7); const success2 = Color(0xff66bb6a); const primary2 = Color(0xff039be5); + +LinearGradient primaryGradient() { + return const LinearGradient( + begin: Alignment.topRight, + end: Alignment.bottomLeft, + colors: [second, Color(0xffFF5151)]); +} diff --git a/unit2/lib/theme-data.dart/text-styles.dart b/unit2/lib/theme-data.dart/text-styles.dart index 7ad4888..aa14dd8 100644 --- a/unit2/lib/theme-data.dart/text-styles.dart +++ b/unit2/lib/theme-data.dart/text-styles.dart @@ -5,3 +5,17 @@ TextStyle titleTextStyle() { return TextStyle( fontSize: blockSizeVertical * 2.5, fontWeight: FontWeight.w500); } + +TextStyle personInitials() { + return TextStyle( + fontSize: blockSizeVertical * 2, + fontWeight: FontWeight.w500, + color: Colors.white); +} + +TextStyle personInfo() { + return TextStyle( + fontWeight: FontWeight.w500, + color: Colors.black54, + fontSize: blockSizeVertical * 2.5); +} diff --git a/unit2/lib/utils/router.dart b/unit2/lib/utils/router.dart index a5e5216..956278b 100644 --- a/unit2/lib/utils/router.dart +++ b/unit2/lib/utils/router.dart @@ -1,16 +1,19 @@ import 'package:go_router/go_router.dart'; -import 'package:unit2/screen/unit2/roles/qr_code_scanner.dart/scan.dart'; -import '../screen/sos/add_mobile.dart'; -import '../screen/sos/request_sos.dart'; -import '../screen/unit2/login/login.dart'; -import '../screen/unit2/homepage.dart/components/drawer-screen.dart'; -import '../screen/unit2/profile/profile.dart'; +import 'package:unit2/screens/unit2/login/register.dart'; +import '../screens/docsms/components/doc_info_tile.dart'; +import '../screens/docsms/request_receipt.dart'; +import '../screens/sos/add_mobile.dart'; +import '../screens/sos/request_sos.dart'; +import '../screens/unit2/login/login.dart'; +import '../screens/unit2/homepage.dart/components/drawer-screen.dart'; +import '../screens/unit2/profile/profile.dart'; +import '../screens/unit2/roles/registration_in_charge/home.dart'; final GoRouter goRouter = GoRouter(routes: [ GoRoute( path: '/', name: 'login', - builder: (context, state) => const QRCodeScanner(), + builder: (context, state) => Register(), routes: [ GoRoute( name: 'home', diff --git a/unit2/lib/utils/text_container.dart b/unit2/lib/utils/text_container.dart index 856ae72..3fcd534 100644 --- a/unit2/lib/utils/text_container.dart +++ b/unit2/lib/utils/text_container.dart @@ -24,4 +24,19 @@ const String requestSOS = "Request SOS"; const String qrScannerTitle = "QR-Code Scanner"; const String establishment = "Establishment"; const String checkpointArea = "Checkpoint Area"; -const String scanMode = 'Scan Mode'; \ No newline at end of file +const String scanMode = 'Scan Mode'; +const String registrationInChargeTitle = "Registration In Charge"; +const String basicInformation = "Basic Information"; +const String address = "Address"; +const String syncTitle = "Sync Data"; +const String syncSubTittle = "Press Sync button to get latest data available"; +const String syncNow = "SYNC NOW"; +const String documentId = "Document ID"; +const String documentTitle = "Document Title"; +const String documentSubject = "Document Subject"; +const String documentType = "Document Type"; +const String registerToContinue = "Create your password to register"; +// +// +// +// \ No newline at end of file diff --git a/unit2/lib/utils/validators.dart b/unit2/lib/utils/validators.dart index 704e733..f647b4d 100644 --- a/unit2/lib/utils/validators.dart +++ b/unit2/lib/utils/validators.dart @@ -1,12 +1,14 @@ import 'package:form_builder_validators/form_builder_validators.dart'; import '../utils/text_container.dart'; +final mobileNumberValidator = FormBuilderValidators.compose([ + FormBuilderValidators.minLength(11), + FormBuilderValidators.required(errorText: mobileNumberRequired), + FormBuilderValidators.numeric(errorText: numericValidator) +]); -final mobileNumberValidator = FormBuilderValidators.compose([ - FormBuilderValidators.required( - errorText: mobileNumberRequired), - FormBuilderValidators.numeric( - errorText: numericValidator) - ]); - - +final registerPasswordValidator = FormBuilderValidators.compose([ + FormBuilderValidators.required(errorText: "Password is required"), + FormBuilderValidators.minLength(6, + errorText: "Password must be equal or greater than 6 characters"), +]); diff --git a/unit2/lib/widgets/costum_divider.dart b/unit2/lib/widgets/costum_divider.dart new file mode 100644 index 0000000..14bf211 --- /dev/null +++ b/unit2/lib/widgets/costum_divider.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; + +class CostumDivider extends StatelessWidget { + const CostumDivider({ + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Divider( + color: Colors.grey.withOpacity(.5), + height: 1, + thickness: 1, + ); + } +} diff --git a/unit2/lib/widgets/label.dart b/unit2/lib/widgets/label.dart new file mode 100644 index 0000000..c6e9af5 --- /dev/null +++ b/unit2/lib/widgets/label.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/container.dart'; +import 'package:flutter/src/widgets/framework.dart'; + +import '../utils/global.dart'; + +class Label extends StatelessWidget { + final String text; + const Label({super.key, required this.text}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 12), + child: Text(text, + style: Theme.of(context) + .textTheme + .displayMedium! + .copyWith(fontSize: blockSizeVertical * 2.5)), + ); + } +} \ No newline at end of file diff --git a/unit2/lib/widgets/text_icon.dart b/unit2/lib/widgets/text_icon.dart new file mode 100644 index 0000000..5a8f50b --- /dev/null +++ b/unit2/lib/widgets/text_icon.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; +import 'package:unit2/theme-data.dart/text-styles.dart'; + +import '../theme-data.dart/colors.dart'; + +class TextIcon extends StatelessWidget { + final String title; + final IconData icon; + + const TextIcon({Key? key, required this.title, required this.icon}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return Wrap( + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + Text( + title, + style: titleTextStyle().copyWith(color: primary, fontSize: 18), + ), + Icon( + icon, + color: primary, + size: 24, + ), + ], + ); + } +} diff --git a/unit2/notes.txt b/unit2/notes.txt new file mode 100644 index 0000000..07ecc0a --- /dev/null +++ b/unit2/notes.txt @@ -0,0 +1,4 @@ + onPressed: () { + print(_formKey.currentState!.value['firstname']); + if (_formKey.currentState!.saveAndValidate()) {} + }, \ No newline at end of file diff --git a/unit2/pubspec.lock b/unit2/pubspec.lock index 93767ed..d2f2cea 100644 --- a/unit2/pubspec.lock +++ b/unit2/pubspec.lock @@ -22,6 +22,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.0" + azlistview: + dependency: "direct main" + description: + name: azlistview + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" boolean_selector: dependency: transitive description: @@ -71,6 +78,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.16.0" + convex_bottom_bar: + dependency: "direct main" + description: + name: convex_bottom_bar + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0+1" crypto: dependency: transitive description: @@ -85,6 +99,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.5" + date_time_picker: + dependency: "direct main" + description: + name: date_time_picker + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" device_frame: dependency: transitive description: @@ -246,7 +267,7 @@ packages: source: hosted version: "4.0.2" intl: - dependency: transitive + dependency: "direct main" description: name: intl url: "https://pub.dartlang.org" @@ -434,6 +455,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.27.7" + scrollable_positioned_list: + dependency: transitive + description: + name: scrollable_positioned_list + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.3" shared_preferences: dependency: transitive description: diff --git a/unit2/pubspec.yaml b/unit2/pubspec.yaml index 729c747..b08bfd4 100644 --- a/unit2/pubspec.yaml +++ b/unit2/pubspec.yaml @@ -50,6 +50,10 @@ dependencies: animate_do: ^3.0.2 flutter_spinkit: ^5.1.0 toggle_switch: ^2.0.1 + convex_bottom_bar: ^3.1.0+1 + azlistview: ^2.0.0 + intl: ^0.17.0 + date_time_picker: ^2.1.0 dev_dependencies: flutter_test: From 8d5f713e5247a48aae66a4407d8c6abebac122df Mon Sep 17 00:00:00 2001 From: rodolfobacuinjr Date: Tue, 13 Dec 2022 14:56:09 +0800 Subject: [PATCH 07/86] add costum alerts and finalize profile user interface --- .../android/app/src/main/AndroidManifest.xml | 1 + .../homepage.dart/components/menu-screen.dart | 2 +- .../unit2/homepage.dart/components/menu.dart | 11 +- unit2/lib/screens/unit2/login/login.dart | 427 +++++++++--------- .../unit2/profile/components/cover-image.dart | 2 +- unit2/lib/screens/unit2/profile/profile.dart | 142 +++--- unit2/lib/screens/unit2/signature/pad.dart | 0 unit2/lib/test_data.dart | 2 +- unit2/lib/utils/alerts.dart | 29 ++ unit2/lib/utils/router.dart | 2 +- unit2/lib/utils/scanner.dart | 36 ++ unit2/pubspec.lock | 84 ++++ unit2/pubspec.yaml | 4 + 13 files changed, 483 insertions(+), 259 deletions(-) create mode 100644 unit2/lib/screens/unit2/signature/pad.dart create mode 100644 unit2/lib/utils/alerts.dart create mode 100644 unit2/lib/utils/scanner.dart diff --git a/unit2/android/app/src/main/AndroidManifest.xml b/unit2/android/app/src/main/AndroidManifest.xml index 9e1ccf9..1e8a920 100644 --- a/unit2/android/app/src/main/AndroidManifest.xml +++ b/unit2/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ + { Expanded( child: Align( alignment: FractionalOffset.bottomLeft, - child: getTile(WebSymbols.logout, "Logout", '/', context), + child: getTile(WebSymbols.logout, "Logout", 'login', context), )), ], ), diff --git a/unit2/lib/screens/unit2/homepage.dart/components/menu.dart b/unit2/lib/screens/unit2/homepage.dart/components/menu.dart index 204fdd0..ec3a330 100644 --- a/unit2/lib/screens/unit2/homepage.dart/components/menu.dart +++ b/unit2/lib/screens/unit2/homepage.dart/components/menu.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_zoom_drawer/flutter_zoom_drawer.dart'; import 'package:go_router/go_router.dart'; - +import 'package:cool_alert/cool_alert.dart'; +import 'package:unit2/utils/alerts.dart'; import '../../../../theme-data.dart/colors.dart'; Widget getTile( @@ -17,9 +18,13 @@ Widget getTile( style: const TextStyle(color: Colors.black), ), onTap: () async { - debugPrint(title); - ZoomDrawer.of(context)!.toggle(); + if (title.toLowerCase() == "logout") { + confirmAlert(context, () { + context.goNamed("login"); + }); + } else { context.goNamed(route); + } }, ); } diff --git a/unit2/lib/screens/unit2/login/login.dart b/unit2/lib/screens/unit2/login/login.dart index e19f924..c3d2e58 100644 --- a/unit2/lib/screens/unit2/login/login.dart +++ b/unit2/lib/screens/unit2/login/login.dart @@ -1,10 +1,16 @@ +import 'package:barcode_scan2/barcode_scan2.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:go_router/go_router.dart'; + +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:unit2/utils/alerts.dart'; +import 'package:unit2/utils/scanner.dart'; import 'package:unit2/utils/text_container.dart'; + import '../../../widgets/wave.dart'; import '../../../utils/global.dart'; import '../../../theme-data.dart/colors.dart'; @@ -29,215 +35,234 @@ class _UniT2LoginState extends State { return WillPopScope( onWillPop: pressAgainToExit, child: Scaffold( - body: SizedBox( - child: SingleChildScrollView( - child: Stack( - children: [ - Positioned( - bottom: 0, - right: 0, - child: WaveReverse(height: blockSizeVertical * 7)), - SizedBox( - height: blockSizeVertical * 100, - child: FormBuilder( - key: _formKey, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 25), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox(height: blockSizeVertical * 7), - SvgPicture.asset( - 'assets/svgs/logo.svg', - height: blockSizeVertical * 16, - allowDrawingOutsideViewBox: true, - color: primary, - ), - - Text( - welcome, - style: TextStyle( - fontSize: blockSizeVertical * 5, - fontWeight: FontWeight.w600), - ), - Text(unitApp, - style: TextStyle( - fontSize: blockSizeVertical * 8, - fontWeight: FontWeight.w800, - letterSpacing: .2, - height: 1, - color: primary)), - Text( - loginToContinue, - style: TextStyle( - fontSize: blockSizeVertical * 2, - height: 1.5, - fontWeight: FontWeight.w600), - ), - SizedBox( - height: blockSizeVertical * 1.5, - ), - // USERNAME - FormBuilderTextField( - name: 'username', - validator: FormBuilderValidators.required( - errorText: "Username is required"), - autofocus: false, - style: const TextStyle( - fontWeight: FontWeight.bold, - color: Colors.black87), - decoration: loginTextFieldStyle()), - SizedBox( - height: blockSizeVertical * 1.5, - ), - // PASSWORD - FormBuilderTextField( - name: 'password', - validator: FormBuilderValidators.required( - errorText: "Password is required"), - - // initialValue: state.password, - onChanged: (value) { - value!.isEmpty - ? setState(() { - showSuffixIcon = false; - }) - : setState(() { - showSuffixIcon = true; - }); - }, - autofocus: false, - style: const TextStyle( - fontWeight: FontWeight.bold, - color: Colors.black87), - decoration: loginTextFieldStyle().copyWith( - suffixIcon: Visibility( - visible: showSuffixIcon, - child: _showPassword - ? IconButton( - icon: Icon(FontAwesome5.eye_slash, - size: 24, - color: Theme.of(context) - .textTheme - .displayLarge - ?.color), - onPressed: () { - setState(() { - _showPassword = false; - }); - }, - ) - : IconButton( - onPressed: () { - setState(() { - _showPassword = true; - }); - }, - icon: Icon(FontAwesome5.eye, - size: 24, - color: Theme.of(context) - .textTheme - .displayLarge - ?.color)), - ), - prefixIcon: const Icon(Icons.lock), - labelText: "Password", - hintText: "Enter Password..."), - obscureText: _showPassword ? true : false, - ), - SizedBox( - height: blockSizeVertical * 2, - ), - SizedBox( - height: blockSizeVertical * 7, - // Login Button - child: SizedBox( - width: MediaQuery.of(context).size.width, - child: ElevatedButton( - style: mainBtnStyle( - second, Colors.transparent, Colors.white54), - child: const Text( - "LOGIN", - style: TextStyle(color: Colors.white), - ), - onPressed: () { - if (_formKey.currentState! - .saveAndValidate()) { - context.go(context.namedLocation('home')); - } - - // if (_formKey.currentState.validate()) { - // _formKey.currentState.save(); - // BlocProvider.of(context) - // .add(UserWebLogin( - // password: password, - // username: username)); - // } - }, + body: ProgressHUD( + child: Builder(builder: (context) { + return SizedBox( + child: SingleChildScrollView( + child: Stack( + children: [ + Positioned( + bottom: 0, + right: 0, + child: WaveReverse(height: blockSizeVertical * 7)), + SizedBox( + height: blockSizeVertical * 100, + child: FormBuilder( + key: _formKey, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 25), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(height: blockSizeVertical * 7), + SvgPicture.asset( + 'assets/svgs/logo.svg', + height: blockSizeVertical * 16, + allowDrawingOutsideViewBox: true, + color: primary, ), - ), - ), - SizedBox( - height: blockSizeVertical * 1.5, - ), - SizedBox( - height: blockSizeVertical * 7, - child: SizedBox( + Text( + welcome, + style: TextStyle( + fontSize: blockSizeVertical * 5, + fontWeight: FontWeight.w600), + ), + Text(unitApp, + style: TextStyle( + fontSize: blockSizeVertical * 8, + fontWeight: FontWeight.w800, + letterSpacing: .2, + height: 1, + color: primary)), + Text( + loginToContinue, + style: TextStyle( + fontSize: blockSizeVertical * 2, + height: 1.5, + fontWeight: FontWeight.w600), + ), + SizedBox( + height: blockSizeVertical * 1.5, + ), + // USERNAME + FormBuilderTextField( + name: 'username', + validator: FormBuilderValidators.required( + errorText: "Username is required"), + autofocus: false, + style: const TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black87), + decoration: loginTextFieldStyle()), + SizedBox( + height: blockSizeVertical * 1.5, + ), + // PASSWORD + FormBuilderTextField( + name: 'password', + validator: FormBuilderValidators.required( + errorText: "Password is required"), + + // initialValue: state.password, + onChanged: (value) { + value!.isEmpty + ? setState(() { + showSuffixIcon = false; + }) + : setState(() { + showSuffixIcon = true; + }); + }, + autofocus: false, + style: const TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black87), + decoration: loginTextFieldStyle().copyWith( + suffixIcon: Visibility( + visible: showSuffixIcon, + child: _showPassword + ? IconButton( + icon: Icon(FontAwesome5.eye_slash, + size: 24, + color: Theme.of(context) + .textTheme + .displayLarge + ?.color), + onPressed: () { + setState(() { + _showPassword = false; + }); + }, + ) + : IconButton( + onPressed: () { + setState(() { + _showPassword = true; + }); + }, + icon: Icon(FontAwesome5.eye, + size: 24, + color: Theme.of(context) + .textTheme + .displayLarge + ?.color)), + ), + prefixIcon: const Icon(Icons.lock), + labelText: "Password", + hintText: "Enter Password..."), + obscureText: _showPassword ? true : false, + ), + SizedBox( + height: blockSizeVertical * 2, + ), + SizedBox( + height: blockSizeVertical * 7, + // Login Button + child: SizedBox( + width: MediaQuery.of(context).size.width, + child: ElevatedButton( + style: mainBtnStyle(second, + Colors.transparent, Colors.white54), + child: const Text( + login, + style: TextStyle(color: Colors.white), + ), + onPressed: () { + final progress = ProgressHUD.of(context); + progress?.showWithText( + 'Logging in...', + ); + Future.delayed(const Duration(seconds: 5), + () { + progress!.dismiss(); + + context.goNamed("home"); + }); + // if (_formKey.currentState! + // .saveAndValidate()) { + // context.go(context.namedLocation('home')); + // } + + // if (_formKey.currentState.validate()) { + // _formKey.currentState.save(); + // BlocProvider.of(context) + // .add(UserWebLogin( + // password: password, + // username: username)); + // } + }, + ), + ), + ), + SizedBox( + height: blockSizeVertical * 1.5, + ), + + SizedBox( + height: blockSizeVertical * 7, + child: SizedBox( + width: MediaQuery.of(context).size.width, + child: ElevatedButton.icon( + style: mainBtnStyle(Colors.white, second, + primary.withOpacity(.4)), + icon: const Icon( + Icons.qr_code, + color: second, + ), + label: const Text( + loginViaQr, + style: TextStyle(color: second), + ), + onPressed: () async { + ScanResult result = + await QRCodeBarCodeScanner.instance + .scanner(); + + debugPrint(result.type.toString()); + debugPrint( + result.rawContent.toString()); + // BlocProvider.of(context) + // .add(QRCodelogin()); + }, + ), + )), + SizedBox( + height: blockSizeVertical * 1, + ), + const LoginViaQr(text: emergencyReponseLabel), + SizedBox( + height: blockSizeVertical * 1, + ), + // REQUEST SOS + SizedBox( + height: screenHeight * .07, width: MediaQuery.of(context).size.width, child: ElevatedButton.icon( - style: mainBtnStyle(Colors.white, second, - primary.withOpacity(.4)), - icon: const Icon( - Icons.qr_code, - color: second, - ), - label: const Text( - loginViaQr, - style: TextStyle(color: second), - ), - onPressed: () async { - context.go(context.namedLocation('home')); - - // BlocProvider.of(context) - // .add(QRCodelogin()); - }, - ), - )), - SizedBox( - height: blockSizeVertical * 1, + icon: const Icon( + FontAwesome5.life_ring, + color: Colors.white, + ), + style: mainBtnStyle(third, + Colors.transparent, Colors.white38), + onPressed: () { + context.goNamed('add-mobile'); + }, + label: const Text( + requestSOS, + style: TextStyle(color: Colors.white), + )), + ) + ], ), - const LoginViaQr(text: emergencyReponseLabel), - SizedBox( - height: blockSizeVertical * 1, - ), - // REQUEST SOS - SizedBox( - height: screenHeight * .07, - width: MediaQuery.of(context).size.width, - child: ElevatedButton.icon( - icon: const Icon( - FontAwesome5.life_ring, - color: Colors.white, - ), - style: mainBtnStyle( - third, Colors.transparent, Colors.white38), - onPressed: () { - context.goNamed('add-mobile'); - }, - label: const Text( - requestSOS, - style: TextStyle(color: Colors.white), - )), - ) - ], + ), ), ), - ), + ], ), - ], - ), - ), + ), + ); + }), ), ), ); diff --git a/unit2/lib/screens/unit2/profile/components/cover-image.dart b/unit2/lib/screens/unit2/profile/components/cover-image.dart index 5ce6aaf..69c734c 100644 --- a/unit2/lib/screens/unit2/profile/components/cover-image.dart +++ b/unit2/lib/screens/unit2/profile/components/cover-image.dart @@ -17,4 +17,4 @@ class CoverImage extends StatelessWidget { ), ); } -} \ No newline at end of file +} diff --git a/unit2/lib/screens/unit2/profile/profile.dart b/unit2/lib/screens/unit2/profile/profile.dart index 5803e2f..8228e25 100644 --- a/unit2/lib/screens/unit2/profile/profile.dart +++ b/unit2/lib/screens/unit2/profile/profile.dart @@ -1,7 +1,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; +import 'package:fluttericon/web_symbols_icons.dart'; import 'package:go_router/go_router.dart'; +import 'package:qr_flutter/qr_flutter.dart'; +import 'package:unit2/test_data.dart'; +import 'package:unit2/theme-data.dart/btn-style.dart'; +import 'package:unit2/utils/global.dart'; +import '../../../theme-data.dart/colors.dart'; import './components/cover-image.dart'; class Profile extends StatelessWidget { @@ -15,26 +21,40 @@ class Profile extends StatelessWidget { }, child: SafeArea( child: Scaffold( - body: Stack( - clipBehavior: Clip.none, - alignment: Alignment.center, - children: [ - const CoverImage(), - const Positioned(top: 125, child: BuildProfileImage()), - Positioned( - top: 10, - left: 20, - child: IconButton( - onPressed: () { - context.go(context.namedLocation('home')); - }, - icon: const Icon( - FontAwesome5.arrow_left, - size: 24, - color: Colors.white, - ), - )), - ], + body: SizedBox( + width: screenWidth, + child: Stack( + clipBehavior: Clip.none, + alignment: Alignment.center, + children: [ + const CoverImage(), + const Positioned(top: 125, child: BuildProfileImage()), + Positioned( + top: 10, + left: 20, + child: IconButton( + onPressed: () { + context.go(context.namedLocation('home')); + }, + icon: const Icon( + FontAwesome5.arrow_left, + size: 24, + color: Colors.white, + ), + )), + Positioned( + top: 10, + right: 20, + child: IconButton( + onPressed: () {}, + icon: const Icon( + Icons.edit, + size: 24, + color: Colors.white, + ), + )), + ], + ), ), ), ), @@ -46,35 +66,55 @@ class BuildInformation extends StatelessWidget { const BuildInformation({super.key}); @override Widget build(BuildContext context) { - return Column( - children: [ - const SizedBox( - height: 8, - ), - Text( - "Rodolfo Bernales Acuin", - style: Theme.of(context) - .textTheme - .headline5! - .copyWith(fontWeight: FontWeight.bold), - ), - Row( - children: [ - Icon( - FontAwesome5.birthday_cake, - color: Colors.blueAccent, + return Container( + padding: const EdgeInsets.symmetric(horizontal: 25), + width: screenWidth, + child: Column( + children: [ + const SizedBox( + height: 25, + ), + Text( + "Rodolfo Bernales Acuin", + style: Theme.of(context) + .textTheme + .headline5! + .copyWith(fontWeight: FontWeight.bold), + ), + const SizedBox( + height: 10, + ), + Text( + "july 14, 1994 | Male", + style: Theme.of(context).textTheme.caption!.copyWith(fontSize: 18), + ), + const SizedBox( + height: 15, + ), + QrImage( + data: uuid, + size: blockSizeVertical * 30, + ), + SizedBox( + height: 25, + ), + SizedBox( + height: blockSizeVertical * 6, + width: screenWidth * .60, + child: ElevatedButton.icon( + label: const Text("Signature pad"), + icon: const Icon( + FontAwesome5.signature, + ), + style: mainBtnStyle(third, Colors.transparent, Colors.white54), + onPressed: () {}, ), - const SizedBox( - width: 20, - ), - Text( - "july 14, 1994 | Male", - style: - Theme.of(context).textTheme.caption!.copyWith(fontSize: 22), - ), - ], - ), - ], + ), + const SizedBox( + height: 5, + ), + ], + ), ); } } @@ -89,12 +129,12 @@ class BuildProfileImage extends StatelessWidget { Stack( alignment: Alignment.center, children: [ - CircleAvatar( - radius: 85, + const CircleAvatar( + radius: 72, backgroundColor: Colors.white, ), CircleAvatar( - radius: 80, + radius: 69, backgroundColor: Colors.grey.shade800, child: SvgPicture.asset( 'assets/svgs/male.svg', diff --git a/unit2/lib/screens/unit2/signature/pad.dart b/unit2/lib/screens/unit2/signature/pad.dart new file mode 100644 index 0000000..e69de29 diff --git a/unit2/lib/test_data.dart b/unit2/lib/test_data.dart index a16e395..47714c3 100644 --- a/unit2/lib/test_data.dart +++ b/unit2/lib/test_data.dart @@ -1,5 +1,5 @@ import 'package:azlistview/azlistview.dart'; - +String uuid = 'f68c3142-b939-11ec-9acb-3939f0cc109a'; List levels = ['Establishments', 'Office']; List establishments = ['Provincial Government of Agusan del Norte']; List checkPointAreas = [ diff --git a/unit2/lib/utils/alerts.dart b/unit2/lib/utils/alerts.dart new file mode 100644 index 0000000..39c65f6 --- /dev/null +++ b/unit2/lib/utils/alerts.dart @@ -0,0 +1,29 @@ +import 'package:cool_alert/cool_alert.dart'; +import 'package:flutter/material.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; + +confirmAlert(context, Function() yes) { + CoolAlert.show( + loopAnimation: true, + context: context, + type: CoolAlertType.confirm, + title: 'LOGOUT!', + text: 'Are you sure you want to logout?', + cancelBtnText: 'No', + confirmBtnText: 'Yes', + confirmBtnColor: second, + showCancelBtn: true, + barrierDismissible: false, + onConfirmBtnTap: yes); +} + +errorAlert(context) { + CoolAlert.show( + context: context, + type: CoolAlertType.error, + confirmBtnColor: second, + title: 'Login Failed!', + text: 'username or password is incorrect.', + loopAnimation: false, + ); +} diff --git a/unit2/lib/utils/router.dart b/unit2/lib/utils/router.dart index 956278b..37864e3 100644 --- a/unit2/lib/utils/router.dart +++ b/unit2/lib/utils/router.dart @@ -13,7 +13,7 @@ final GoRouter goRouter = GoRouter(routes: [ GoRoute( path: '/', name: 'login', - builder: (context, state) => Register(), + builder: (context, state) => UniT2Login(), routes: [ GoRoute( name: 'home', diff --git a/unit2/lib/utils/scanner.dart b/unit2/lib/utils/scanner.dart new file mode 100644 index 0000000..25501f6 --- /dev/null +++ b/unit2/lib/utils/scanner.dart @@ -0,0 +1,36 @@ +import 'package:barcode_scan2/barcode_scan2.dart'; + +class QRCodeBarCodeScanner { + static final QRCodeBarCodeScanner _instance = QRCodeBarCodeScanner(); + static QRCodeBarCodeScanner get instance => _instance; + final _selectedCamera = -1; + final bool _useAutoFocus = true; + static final _possibleFormats = BarcodeFormat.values.toList() + ..removeWhere((e) => e == BarcodeFormat.unknown); + ScanResult scanResult = ScanResult(); + + List selectedFormats = [..._possibleFormats]; + + Future scanner() async { + ScanOptions options = ScanOptions( + strings: { + "cancel": "Back", + "flash_on": "Flash on", + "flash_off": "Flash off", + }, + restrictFormat: selectedFormats, + useCamera: _selectedCamera, +// autoEnableFlash: _autoEnableFlash, + android: AndroidOptions( +// aspectTolerance: _aspectTolerance, + useAutoFocus: _useAutoFocus, + ), + ); + try { + scanResult = await BarcodeScanner.scan(options: options); + } on Error catch (e) { + throw (e.toString()); + } + return scanResult; + } +} diff --git a/unit2/pubspec.lock b/unit2/pubspec.lock index d2f2cea..e19a094 100644 --- a/unit2/pubspec.lock +++ b/unit2/pubspec.lock @@ -8,6 +8,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.2" + archive: + dependency: transitive + description: + name: archive + url: "https://pub.dartlang.org" + source: hosted + version: "3.3.5" async: dependency: transitive description: @@ -29,6 +36,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.0" + barcode_scan2: + dependency: "direct main" + description: + name: barcode_scan2 + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.1" boolean_selector: dependency: transitive description: @@ -78,6 +92,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.16.0" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.1" convex_bottom_bar: dependency: "direct main" description: @@ -85,6 +106,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.1.0+1" + cool_alert: + dependency: "direct main" + description: + name: cool_alert + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" crypto: dependency: transitive description: @@ -141,6 +169,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "6.1.4" + fixnum: + dependency: transitive + description: + name: fixnum + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + flare_flutter: + dependency: transitive + description: + name: flare_flutter + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" flutter: dependency: "direct main" description: flutter @@ -186,6 +228,13 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_progress_hud: + dependency: "direct main" + description: + name: flutter_progress_hud + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" flutter_spinkit: dependency: "direct main" description: @@ -301,6 +350,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.0" + lottie: + dependency: transitive + description: + name: lottie + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.3" matcher: dependency: transitive description: @@ -434,6 +490,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.3" + pointycastle: + dependency: transitive + description: + name: pointycastle + url: "https://pub.dartlang.org" + source: hosted + version: "3.6.2" process: dependency: transitive description: @@ -441,6 +504,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.2.4" + protobuf: + dependency: transitive + description: + name: protobuf + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" provider: dependency: transitive description: @@ -448,6 +518,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "6.0.4" + qr: + dependency: transitive + description: + name: qr + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + qr_flutter: + dependency: "direct main" + description: + name: qr_flutter + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.0" rxdart: dependency: transitive description: diff --git a/unit2/pubspec.yaml b/unit2/pubspec.yaml index b08bfd4..6947ded 100644 --- a/unit2/pubspec.yaml +++ b/unit2/pubspec.yaml @@ -54,6 +54,10 @@ dependencies: azlistview: ^2.0.0 intl: ^0.17.0 date_time_picker: ^2.1.0 + flutter_progress_hud: ^2.0.2 + barcode_scan2: ^4.2.1 + cool_alert: ^1.1.0 + qr_flutter: ^4.0.0 dev_dependencies: flutter_test: From 25e0f0905f511d524ad68f4a7153fb9c7632bb55 Mon Sep 17 00:00:00 2001 From: rodolfobacuinjr Date: Tue, 20 Dec 2022 14:26:37 +0800 Subject: [PATCH 08/86] stable version 3.3.9 --- unit2/lib/main.dart | 2 +- .../docsms/components/doc_info_tile.dart | 3 - unit2/lib/screens/docsms/request_receipt.dart | 22 +- unit2/lib/screens/sos/add_mobile.dart | 201 +++++++++--------- unit2/lib/screens/sos/request_sos.dart | 6 +- unit2/lib/screens/sos/sos_received.dart | 4 +- .../unit2/homepage.dart/components/menu.dart | 1 - unit2/lib/screens/unit2/login/login.dart | 27 ++- unit2/lib/screens/unit2/login/register.dart | 23 +- unit2/lib/screens/unit2/profile/profile.dart | 135 ++++++------ .../roles/qr_code_scanner.dart/scan.dart | 6 +- .../qr_code_scanner.dart/settings_screen.dart | 26 +-- unit2/lib/screens/unit2/signature/pad.dart | 0 .../unit2/signature/signature_pad.dart | 47 ++++ unit2/lib/theme-data.dart/form-style.dart | 2 + unit2/lib/utils/alerts.dart | 64 ++++-- unit2/lib/utils/router.dart | 17 +- unit2/lib/utils/text_container.dart | 29 ++- unit2/lib/utils/validators.dart | 2 +- unit2/pubspec.lock | 56 +++-- unit2/pubspec.yaml | 3 +- 21 files changed, 406 insertions(+), 270 deletions(-) delete mode 100644 unit2/lib/screens/unit2/signature/pad.dart create mode 100644 unit2/lib/screens/unit2/signature/signature_pad.dart diff --git a/unit2/lib/main.dart b/unit2/lib/main.dart index f87f8ef..9b80a31 100644 --- a/unit2/lib/main.dart +++ b/unit2/lib/main.dart @@ -40,7 +40,7 @@ class MyApp extends StatelessWidget { // locale: DevicePreview.locale(context), // builder: DevicePreview.appBuilder, routeInformationParser: goRouter.routeInformationParser, - routerDelegate: goRouter.routerDelegate, + routerDelegate: goRouter.routerDelegate, // routeInformationProvider: goRouter.routeInformationProvider, title: 'uniT2 - Universal Tracker and Tracer', theme: ThemeData( diff --git a/unit2/lib/screens/docsms/components/doc_info_tile.dart b/unit2/lib/screens/docsms/components/doc_info_tile.dart index a209c76..f34f219 100644 --- a/unit2/lib/screens/docsms/components/doc_info_tile.dart +++ b/unit2/lib/screens/docsms/components/doc_info_tile.dart @@ -1,7 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/container.dart'; -import 'package:flutter/src/widgets/framework.dart'; - import '../../../theme-data.dart/text-styles.dart'; import '../../../utils/global.dart'; diff --git a/unit2/lib/screens/docsms/request_receipt.dart b/unit2/lib/screens/docsms/request_receipt.dart index 338cb98..b72897a 100644 --- a/unit2/lib/screens/docsms/request_receipt.dart +++ b/unit2/lib/screens/docsms/request_receipt.dart @@ -1,7 +1,5 @@ -import 'package:flutter/cupertino.dart'; + import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/container.dart'; -import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:fluttericon/entypo_icons.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; @@ -61,7 +59,7 @@ class _RequetAutoReceiptState extends State { height: 8, ), Text( - "Source Remarks", + sourceRemarks, style: Theme.of(context).textTheme.caption, ), const SizedBox( @@ -69,15 +67,13 @@ class _RequetAutoReceiptState extends State { ), FormBuilderTextField( name: "remarks", - validator: FormBuilderValidators.compose([ - FormBuilderValidators.required( - errorText: "Remarks is required") - ]), + validator: FormBuilderValidators.required( + errorText: remarksRequired), autovalidateMode: AutovalidateMode.onUserInteraction, maxLines: 5, decoration: normalTextFieldStyle( - "Enter your remarks", "..."), + enterRemarks, "..."), ), const SizedBox( height: 8, @@ -92,7 +88,7 @@ class _RequetAutoReceiptState extends State { autofocus: false, validator: FormBuilderValidators.compose([ FormBuilderValidators.required( - errorText: "Department is required") + errorText: departmentRequired) ]), decoration: normalTextFieldStyle("Department", ""), @@ -106,11 +102,11 @@ class _RequetAutoReceiptState extends State { height: 12, ), FormBuilderDropdown( - name: 'purok', + name: 'substation', autofocus: false, validator: FormBuilderValidators.compose([ FormBuilderValidators.required( - errorText: "Purok is required") + errorText: substationRequired) ]), decoration: normalTextFieldStyle("Substation", ""), @@ -130,7 +126,7 @@ class _RequetAutoReceiptState extends State { style: secondaryBtnStyle( second, Colors.transparent, Colors.white54), child: const Text( - "Request Auto Receipt", + requestAutoReceipt, style: TextStyle(color: Colors.white), ), onPressed: () { diff --git a/unit2/lib/screens/sos/add_mobile.dart b/unit2/lib/screens/sos/add_mobile.dart index 4d7ee6a..185d003 100644 --- a/unit2/lib/screens/sos/add_mobile.dart +++ b/unit2/lib/screens/sos/add_mobile.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_svg/svg.dart'; import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:go_router/go_router.dart'; import 'package:unit2/theme-data.dart/text-styles.dart'; import 'package:unit2/utils/screen_info.dart'; @@ -22,111 +23,107 @@ class AddMobile extends StatelessWidget { onWillPop: () async { return true; }, - child: Scaffold( - appBar: AppBar( - backgroundColor: Colors.transparent, - elevation: 0, - leading: IconButton( - icon: const Icon( - FontAwesome.left_big, - ), - onPressed: () { - context.go(context.namedLocation('login')); - }, - color: primary, + child: SafeArea( + child: Scaffold( + appBar: AppBar( + backgroundColor: primary, + elevation: 0, ), - ), - resizeToAvoidBottomInset: true, - body: SingleChildScrollView( - child: SizedBox( - height: screenHeight * .89, - child: Stack( - children: [ - Positioned( - bottom: 0, - right: 0, - child: WaveReverse(height: blockSizeVertical * 8)), - Container( - height: screenHeight, - padding: isMobile() - ? const EdgeInsets.symmetric(horizontal: 24) - : const EdgeInsets.symmetric(horizontal: 60), - width: double.infinity, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox( + resizeToAvoidBottomInset: true, + body: SingleChildScrollView( + child: SizedBox( + height: screenHeight * .90, + child: Stack( + children: [ + Wave(height: blockSizeVertical * 8), + Positioned( + bottom: 0, + right: 0, + child: WaveReverse(height: blockSizeVertical * 8)), + Container( + height: screenHeight, + padding: isMobile() + ? const EdgeInsets.symmetric(horizontal: 24) + : const EdgeInsets.symmetric(horizontal: 60), + width: double.infinity, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + height: isMobile() + ? screenHeight * .12 + : screenHeight * .20), + SvgPicture.asset( + 'assets/svgs/add_mobile.svg', height: isMobile() - ? screenHeight * .12 - : screenHeight * .20), - SvgPicture.asset( - 'assets/svgs/add_mobile.svg', - height: isMobile() - ? blockSizeVertical * 22 - : blockSizeVertical * 30, - allowDrawingOutsideViewBox: true, - ), - const SizedBox( - height: 24, - ), - Text(addMobile, style: titleTextStyle()), - const SizedBox( - height: 8, - ), - Text(addMobileCaption, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.caption), - const SizedBox( - height: 24, - ), - FormBuilder( - key: _formKey, - child: Column( - children: [ - // Mobile number 1 - FormBuilderTextField( - name: 'mobile1', - validator: mobileNumberValidator, - decoration: normalTextFieldStyle( - "mobile number 1", "+63")), - const SizedBox( - height: 12, - ), - FormBuilderTextField( - name: 'mobile2', - decoration: normalTextFieldStyle( - "mobile number 2", "+63")), - - SizedBox( - height: isMobile() - ? blockSizeVertical * 3 - : blockSizeHorizontal * 5), - SizedBox( - width: double.infinity, - height: screenHeight * .06, - child: ElevatedButton( - style: secondaryBtnStyle(second, - Colors.transparent, Colors.white54), - child: const Text( - submit, - style: TextStyle(color: Colors.white), - ), - onPressed: () { - if (_formKey.currentState! - .saveAndValidate()) { - context.go(context - .namedLocation('request-sos')); - } - - // } - }, + ? blockSizeVertical * 22 + : blockSizeVertical * 30, + allowDrawingOutsideViewBox: true, + ), + const SizedBox( + height: 24, + ), + Text(addMobile, style: titleTextStyle()), + const SizedBox( + height: 8, + ), + Text(addMobileCaption, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.caption), + const SizedBox( + height: 24, + ), + FormBuilder( + key: _formKey, + child: Column( + children: [ + // Mobile number 1 + FormBuilderTextField( + name: 'mobile1', + validator: mobileNumberValidator, + maxLength: 11, + decoration: + normalTextFieldStyle(mobile1, "+63")), + const SizedBox( + height: 12, ), - ), - ], - )) - ]), - ), - ], + FormBuilderTextField( + name: 'mobile2', + maxLength: 11, + decoration: + normalTextFieldStyle(mobile2, "+63")), + + SizedBox( + height: isMobile() + ? blockSizeVertical * 3 + : blockSizeHorizontal * 5), + SizedBox( + width: double.infinity, + height: screenHeight * .06, + child: ElevatedButton( + style: secondaryBtnStyle(second, + Colors.transparent, Colors.white54), + child: const Text( + submit, + style: TextStyle(color: Colors.white), + ), + onPressed: () { + if (_formKey.currentState! + .saveAndValidate()) { + context.go(context + .namedLocation('request-sos')); + } + + // } + }, + ), + ), + ], + )) + ]), + ), + ], + ), ), ), ), diff --git a/unit2/lib/screens/sos/request_sos.dart b/unit2/lib/screens/sos/request_sos.dart index 76cab37..a98b654 100644 --- a/unit2/lib/screens/sos/request_sos.dart +++ b/unit2/lib/screens/sos/request_sos.dart @@ -70,13 +70,13 @@ class _RequestSOSState extends State { name: "message", validator: FormBuilderValidators.compose([ FormBuilderValidators.required( - errorText: "Message is required") + errorText: messageRequired) ]), autovalidateMode: AutovalidateMode.onUserInteraction, maxLines: 5, - decoration: normalTextFieldStyle("", "SOS message.."), + decoration: normalTextFieldStyle("", sosMessage), ), - Expanded( + const Expanded( child: SizedBox(), ), SizedBox( diff --git a/unit2/lib/screens/sos/sos_received.dart b/unit2/lib/screens/sos/sos_received.dart index 8a2de78..b25cd14 100644 --- a/unit2/lib/screens/sos/sos_received.dart +++ b/unit2/lib/screens/sos/sos_received.dart @@ -77,7 +77,7 @@ class SOSreceived extends StatelessWidget { SlideInUp( from: 50, child: AutoSizeText( - "SOS Received!", + sosReceived, textAlign: TextAlign.center, style: Theme.of(context).textTheme.displayMedium!.copyWith( fontSize: 40, @@ -117,7 +117,7 @@ class SOSreceived extends StatelessWidget { child: TextButton( style: mainBtnStyle(second, Colors.transparent, second), onPressed: () {}, - child: const Text("Cancel request", + child: const Text(cancelRequest, style: TextStyle( color: Colors.white, )), diff --git a/unit2/lib/screens/unit2/homepage.dart/components/menu.dart b/unit2/lib/screens/unit2/homepage.dart/components/menu.dart index ec3a330..cdfbd44 100644 --- a/unit2/lib/screens/unit2/homepage.dart/components/menu.dart +++ b/unit2/lib/screens/unit2/homepage.dart/components/menu.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_zoom_drawer/flutter_zoom_drawer.dart'; import 'package:go_router/go_router.dart'; -import 'package:cool_alert/cool_alert.dart'; import 'package:unit2/utils/alerts.dart'; import '../../../../theme-data.dart/colors.dart'; diff --git a/unit2/lib/screens/unit2/login/login.dart b/unit2/lib/screens/unit2/login/login.dart index c3d2e58..1ad705a 100644 --- a/unit2/lib/screens/unit2/login/login.dart +++ b/unit2/lib/screens/unit2/login/login.dart @@ -89,12 +89,12 @@ class _UniT2LoginState extends State { FormBuilderTextField( name: 'username', validator: FormBuilderValidators.required( - errorText: "Username is required"), + errorText: usernameRequired), autofocus: false, style: const TextStyle( fontWeight: FontWeight.bold, color: Colors.black87), - decoration: loginTextFieldStyle()), + decoration: loginTextFieldStyle().copyWith()), SizedBox( height: blockSizeVertical * 1.5, ), @@ -102,7 +102,7 @@ class _UniT2LoginState extends State { FormBuilderTextField( name: 'password', validator: FormBuilderValidators.required( - errorText: "Password is required"), + errorText: passwordRequired), // initialValue: state.password, onChanged: (value) { @@ -148,9 +148,12 @@ class _UniT2LoginState extends State { .displayLarge ?.color)), ), - prefixIcon: const Icon(Icons.lock), + prefixIcon: const Icon( + Icons.lock, + color: primary, + ), labelText: "Password", - hintText: "Enter Password..."), + hintText: enterPassword), obscureText: _showPassword ? true : false, ), SizedBox( @@ -173,6 +176,7 @@ class _UniT2LoginState extends State { progress?.showWithText( 'Logging in...', ); + FocusScope.of(context).unfocus(); Future.delayed(const Duration(seconds: 5), () { progress!.dismiss(); @@ -215,13 +219,14 @@ class _UniT2LoginState extends State { style: TextStyle(color: second), ), onPressed: () async { - ScanResult result = - await QRCodeBarCodeScanner.instance - .scanner(); + context.goNamed('register'); + // ScanResult result = + // await QRCodeBarCodeScanner.instance + // .scanner(); - debugPrint(result.type.toString()); - debugPrint( - result.rawContent.toString()); + // debugPrint(result.type.toString()); + // debugPrint( + // result.rawContent.toString()); // BlocProvider.of(context) // .add(QRCodelogin()); }, diff --git a/unit2/lib/screens/unit2/login/register.dart b/unit2/lib/screens/unit2/login/register.dart index 3d4e614..0468000 100644 --- a/unit2/lib/screens/unit2/login/register.dart +++ b/unit2/lib/screens/unit2/login/register.dart @@ -5,7 +5,9 @@ import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_svg/svg.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:go_router/go_router.dart'; import 'package:unit2/theme-data.dart/btn-style.dart'; import 'package:unit2/widgets/wave.dart'; @@ -31,6 +33,13 @@ class _RegisterState extends State { return SafeArea( child: Scaffold( resizeToAvoidBottomInset: true, + appBar: AppBar( + backgroundColor: primary, + elevation: 0, + automaticallyImplyLeading: true, + title: const Text("Register password"), + centerTitle: true, + ), body: SingleChildScrollView( child: Stack( children: [ @@ -38,7 +47,7 @@ class _RegisterState extends State { Positioned( bottom: 0, child: WaveReverse(height: blockSizeVertical * 8)), Container( - height: screenHeight * .97, + height: screenHeight * .90, padding: const EdgeInsets.symmetric(horizontal: 15), child: FormBuilder( key: _formKey, @@ -118,7 +127,7 @@ class _RegisterState extends State { ), prefixIcon: const Icon(Icons.lock), labelText: "Password", - hintText: "Enter Password..."), + hintText: enterPassword), obscureText: _showPassword ? true : false, ), const SizedBox( @@ -171,8 +180,8 @@ class _RegisterState extends State { ?.color)), ), prefixIcon: const Icon(Icons.lock), - labelText: "Confirm Password", - hintText: "Confirm Password..."), + labelText: confirmPassword, + hintText: enterConfirmPassword), obscureText: _showPassword ? true : false, ), const SizedBox( @@ -185,9 +194,11 @@ class _RegisterState extends State { style: secondaryBtnStyle( second, Colors.transparent, primary), onPressed: () { - if (_formKey.currentState!.saveAndValidate()) {} + if (_formKey.currentState!.saveAndValidate()) { + context.goNamed('home'); + } }, - child: const Text("Register")), + child: const Text(register)), ) ], ), diff --git a/unit2/lib/screens/unit2/profile/profile.dart b/unit2/lib/screens/unit2/profile/profile.dart index 8228e25..33cfec9 100644 --- a/unit2/lib/screens/unit2/profile/profile.dart +++ b/unit2/lib/screens/unit2/profile/profile.dart @@ -7,6 +7,7 @@ import 'package:qr_flutter/qr_flutter.dart'; import 'package:unit2/test_data.dart'; import 'package:unit2/theme-data.dart/btn-style.dart'; import 'package:unit2/utils/global.dart'; +import 'package:unit2/utils/text_container.dart'; import '../../../theme-data.dart/colors.dart'; import './components/cover-image.dart'; @@ -23,36 +24,62 @@ class Profile extends StatelessWidget { child: Scaffold( body: SizedBox( width: screenWidth, - child: Stack( - clipBehavior: Clip.none, - alignment: Alignment.center, + child: Column( children: [ - const CoverImage(), - const Positioned(top: 125, child: BuildProfileImage()), - Positioned( - top: 10, - left: 20, - child: IconButton( - onPressed: () { - context.go(context.namedLocation('home')); - }, - icon: const Icon( - FontAwesome5.arrow_left, - size: 24, - color: Colors.white, + Stack( + clipBehavior: Clip.none, + alignment: Alignment.center, + children: [ + const CoverImage(), + Positioned( + top: blockSizeVertical * 17.5, + child: Stack( + alignment: Alignment.center, + children: [ + const CircleAvatar( + radius: 72, + backgroundColor: Colors.white, + ), + CircleAvatar( + radius: 69, + backgroundColor: Colors.grey.shade800, + child: SvgPicture.asset( + 'assets/svgs/male.svg', + ), + ), + ], ), - )), - Positioned( - top: 10, - right: 20, - child: IconButton( - onPressed: () {}, - icon: const Icon( - Icons.edit, - size: 24, - color: Colors.white, - ), - )), + ), + Positioned( + top: 10, + left: 20, + child: IconButton( + onPressed: () { + context.go(context.namedLocation('home')); + }, + icon: const Icon( + FontAwesome5.arrow_left, + size: 24, + color: Colors.white, + ), + )), + Positioned( + top: 10, + right: 20, + child: IconButton( + onPressed: () {}, + icon: const Icon( + Icons.edit, + size: 24, + color: Colors.white, + ), + )), + ], + ), + SizedBox( + height: blockSizeVertical * 5, + ), + const BuildInformation(), ], ), ), @@ -95,19 +122,24 @@ class BuildInformation extends StatelessWidget { data: uuid, size: blockSizeVertical * 30, ), - SizedBox( + const SizedBox( height: 25, ), SizedBox( - height: blockSizeVertical * 6, width: screenWidth * .60, - child: ElevatedButton.icon( - label: const Text("Signature pad"), - icon: const Icon( - FontAwesome5.signature, - ), - style: mainBtnStyle(third, Colors.transparent, Colors.white54), - onPressed: () {}, + height: blockSizeVertical * 6, + child: SizedBox( + child: ElevatedButton.icon( + style: + mainBtnStyle(third, Colors.transparent, Colors.white54), + onPressed: () { + context.goNamed('signature'); + }, + icon: const Icon( + FontAwesome5.signature, + size: 15, + ), + label: const Text(signature)), ), ), const SizedBox( @@ -118,32 +150,3 @@ class BuildInformation extends StatelessWidget { ); } } - -class BuildProfileImage extends StatelessWidget { - const BuildProfileImage({super.key}); - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Stack( - alignment: Alignment.center, - children: [ - const CircleAvatar( - radius: 72, - backgroundColor: Colors.white, - ), - CircleAvatar( - radius: 69, - backgroundColor: Colors.grey.shade800, - child: SvgPicture.asset( - 'assets/svgs/male.svg', - ), - ), - ], - ), - BuildInformation(), - ], - ); - } -} diff --git a/unit2/lib/screens/unit2/roles/qr_code_scanner.dart/scan.dart b/unit2/lib/screens/unit2/roles/qr_code_scanner.dart/scan.dart index 8908e49..8a26cb8 100644 --- a/unit2/lib/screens/unit2/roles/qr_code_scanner.dart/scan.dart +++ b/unit2/lib/screens/unit2/roles/qr_code_scanner.dart/scan.dart @@ -1,6 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/container.dart'; -import 'package:flutter/src/widgets/framework.dart'; import 'package:fluttericon/entypo_icons.dart'; import 'package:unit2/utils/text_container.dart'; @@ -40,7 +38,7 @@ class QRCodeScanner extends StatelessWidget { height: 8, ), Text( - "TAP TO SCAN QR CODE", + tapToScanQR, textAlign: TextAlign.center, style: Theme.of(context) .textTheme @@ -60,7 +58,7 @@ class QRCodeScanner extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - "INCOMING", + incoming, textAlign: TextAlign.center, style: Theme.of(context) .textTheme diff --git a/unit2/lib/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart b/unit2/lib/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart index 7f3e4a4..72b3f18 100644 --- a/unit2/lib/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart +++ b/unit2/lib/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart @@ -53,7 +53,7 @@ class _QRCodeScannerSettingsState extends State { ), ListTile( title: Text( - "Set QR Scanner Settings", + setQRScannerSettings, style: Theme.of(context) .textTheme .headline6! @@ -61,10 +61,10 @@ class _QRCodeScannerSettingsState extends State { textAlign: TextAlign.center, ), ), - Text("Include other inputs?", + Text(includeOtherInputs, style: Theme.of(context).textTheme.subtitle1), Text( - "inputs such as body temperature, etc.", + includeOtherInputsSubTitle, style: Theme.of(context).textTheme.caption, ), SizedBox( @@ -86,10 +86,10 @@ class _QRCodeScannerSettingsState extends State { ), ), // Incoming or outgoing - Text("Incoming or Outgoing? ", + Text(incomingORoutgoing, style: Theme.of(context).textTheme.subtitle1), Text( - "incoming for entrance outgoing for exit.", + incomingORoutgoingSubTitle, style: Theme.of(context).textTheme.caption, ), FittedBox( @@ -117,12 +117,12 @@ class _QRCodeScannerSettingsState extends State { FormBuilderDropdown( name: 'level', validator: FormBuilderValidators.required( - errorText: "This field is required"), - decoration: normalTextFieldStyle("Select level", "level"), + errorText: fieldIsRequired), + decoration: normalTextFieldStyle(selectLevel, "level"), items: levels .map((level) => DropdownMenuItem( - child: Text(level), value: level, + child: Text(level), )) .toList(), // value: selectedLevel, @@ -138,12 +138,14 @@ class _QRCodeScannerSettingsState extends State { FormBuilderDropdown( name: 'establishment', decoration: normalTextFieldStyle( - "Select establishment", "establishments"), + selectedEstablishment, "establishments"), isExpanded: true, + validator: FormBuilderValidators.required( + errorText: fieldIsRequired), items: establishments .map((est) => DropdownMenuItem( - child: Text(est), value: est, + child: Text(est), )) .toList(), // value: selectedArea, @@ -155,12 +157,12 @@ class _QRCodeScannerSettingsState extends State { ), DropdownButtonFormField( decoration: normalTextFieldStyle( - "Select establishment", "establishments"), + selectEstablishment, "establishments"), isExpanded: true, items: establishments .map((est) => DropdownMenuItem( - child: Text(est), value: est, + child: Text(est), )) .toList(), // value: selectedArea, diff --git a/unit2/lib/screens/unit2/signature/pad.dart b/unit2/lib/screens/unit2/signature/pad.dart deleted file mode 100644 index e69de29..0000000 diff --git a/unit2/lib/screens/unit2/signature/signature_pad.dart b/unit2/lib/screens/unit2/signature/signature_pad.dart new file mode 100644 index 0000000..2a4b404 --- /dev/null +++ b/unit2/lib/screens/unit2/signature/signature_pad.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/container.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:signature/signature.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; + +class SignaturePad extends StatefulWidget { + const SignaturePad({super.key}); + + @override + State createState() => _SignaturePadState(); +} + +class _SignaturePadState extends State { + SignatureController? _signatureController; + + @override + void initState() { + _signatureController = SignatureController( + penStrokeWidth: 3, + penColor: Colors.black87, + ); + super.initState(); + } + + @override + void dispose() { + _signatureController!.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("Create Signature"), + centerTitle: true, + backgroundColor: second, + automaticallyImplyLeading: true, + ), + body: Signature( + controller: _signatureController!, + backgroundColor: Colors.white, + ), + ); + } +} diff --git a/unit2/lib/theme-data.dart/form-style.dart b/unit2/lib/theme-data.dart/form-style.dart index 6fbcc4c..7089e98 100644 --- a/unit2/lib/theme-data.dart/form-style.dart +++ b/unit2/lib/theme-data.dart/form-style.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; InputDecoration normalTextFieldStyle(String labelText, String hintText) { return InputDecoration( @@ -53,6 +54,7 @@ InputDecoration loginTextFieldStyle() { floatingLabelBehavior: FloatingLabelBehavior.never, prefixIcon: const Icon( Icons.person, + color: primary, ), labelText: 'Username', hintText: 'Enter your username...', diff --git a/unit2/lib/utils/alerts.dart b/unit2/lib/utils/alerts.dart index 39c65f6..a9d488d 100644 --- a/unit2/lib/utils/alerts.dart +++ b/unit2/lib/utils/alerts.dart @@ -1,29 +1,51 @@ -import 'package:cool_alert/cool_alert.dart'; +import 'package:awesome_dialog/awesome_dialog.dart'; import 'package:flutter/material.dart'; import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/global.dart'; confirmAlert(context, Function() yes) { - CoolAlert.show( - loopAnimation: true, - context: context, - type: CoolAlertType.confirm, - title: 'LOGOUT!', - text: 'Are you sure you want to logout?', - cancelBtnText: 'No', - confirmBtnText: 'Yes', - confirmBtnColor: second, - showCancelBtn: true, - barrierDismissible: false, - onConfirmBtnTap: yes); + AwesomeDialog( + context: context, + dialogType: DialogType.question, + borderSide: const BorderSide( + color: Colors.green, + width: 0, + ), + width: blockSizeHorizontal * 90, + buttonsBorderRadius: const BorderRadius.all( + Radius.circular(2), + ), + dismissOnTouchOutside: false, + dismissOnBackKeyPress: false, + // onDismissCallback: (type) { + // ScaffoldMessenger.of(context).showSnackBar( + // SnackBar( + // content: Text('Dismissed by $type'), + // ), + // ); + // }, + headerAnimationLoop: false, + animType: AnimType.bottomSlide, + title: 'LOGOUT!', + desc: 'Are you sure you want to logout?', + btnOkText: "Yes", + btnCancelText: "No", + showCloseIcon: false, + btnCancelOnPress: () {}, + btnOkOnPress: yes, + ).show(); } -errorAlert(context) { - CoolAlert.show( +errorAlert(context, title, description) { + AwesomeDialog( + width: blockSizeHorizontal * 90, context: context, - type: CoolAlertType.error, - confirmBtnColor: second, - title: 'Login Failed!', - text: 'username or password is incorrect.', - loopAnimation: false, - ); + dialogType: DialogType.error, + animType: AnimType.scale, + headerAnimationLoop: false, + title: title, + desc: description, + btnOkOnPress: () {}, + btnOkColor: Colors.red, + ).show(); } diff --git a/unit2/lib/utils/router.dart b/unit2/lib/utils/router.dart index 37864e3..97d210b 100644 --- a/unit2/lib/utils/router.dart +++ b/unit2/lib/utils/router.dart @@ -1,5 +1,9 @@ import 'package:go_router/go_router.dart'; import 'package:unit2/screens/unit2/login/register.dart'; +import 'package:unit2/screens/unit2/roles/qr_code_scanner.dart/scan.dart'; +import 'package:unit2/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart'; +import 'package:unit2/screens/unit2/signature/signature_pad.dart'; +import 'package:unit2/utils/scanner.dart'; import '../screens/docsms/components/doc_info_tile.dart'; import '../screens/docsms/request_receipt.dart'; import '../screens/sos/add_mobile.dart'; @@ -15,6 +19,10 @@ final GoRouter goRouter = GoRouter(routes: [ name: 'login', builder: (context, state) => UniT2Login(), routes: [ + GoRoute( + name: 'register', + path: 'register', + builder: ((context, state) => const Register())), GoRoute( name: 'home', path: 'home', @@ -23,7 +31,14 @@ final GoRouter goRouter = GoRouter(routes: [ GoRoute( name: 'profile', path: 'profile', - builder: (context, state) => const Profile()) + builder: (context, state) => const Profile(), + routes: [ + GoRoute( + name: 'signature', + path: 'signature', + builder: (context, state) => const SignaturePad(), + ) + ]) ]), GoRoute( name: 'add-mobile', diff --git a/unit2/lib/utils/text_container.dart b/unit2/lib/utils/text_container.dart index 3fcd534..e102dbb 100644 --- a/unit2/lib/utils/text_container.dart +++ b/unit2/lib/utils/text_container.dart @@ -36,7 +36,34 @@ const String documentTitle = "Document Title"; const String documentSubject = "Document Subject"; const String documentType = "Document Type"; const String registerToContinue = "Create your password to register"; +const String sourceRemarks = "Source Remarks"; +const String remarksRequired = "Remarks is required"; +const String enterRemarks = "Enter your remarks"; +const String departmentRequired = "Department is required"; +const String substationRequired = "Substation is required"; +const String requestAutoReceipt = "Request Auto Receipt"; +const String messageRequired = "Message is required"; +const String sosMessage = "SOS message ..."; +const String sosReceived = "SOS Received!"; +const String cancelRequest = "Cancel Request"; +const String usernameRequired = "Username is required"; +const String passwordRequired = "Password is required"; +const String enterPassword = "Enter Password ..."; +const String confirmPassword = "Confirm Password"; +const String enterConfirmPassword = "Enter Confirm Password ..."; +const String register = "Register"; +const String signature = "Signature pad"; +const String tapToScanQR = "TAP TO SCAN QR CODE"; +const String incoming = "INCOMING"; +const String setQRScannerSettings = "Set QR Scanner Settings"; +const String includeOtherInputs = "Include other inputs?"; +const String includeOtherInputsSubTitle = "Inputs such as body temperature, etc."; +const String incomingORoutgoing = "Incoming or Outgoing?"; +const String incomingORoutgoingSubTitle = "incoming for entrance outgoing for exit."; +const String fieldIsRequired = "This field is required"; +const String selectLevel = "Select level"; +const String selectEstablishment = "Select Establishment"; // // // -// \ No newline at end of file + diff --git a/unit2/lib/utils/validators.dart b/unit2/lib/utils/validators.dart index f647b4d..52f4c1f 100644 --- a/unit2/lib/utils/validators.dart +++ b/unit2/lib/utils/validators.dart @@ -2,7 +2,7 @@ import 'package:form_builder_validators/form_builder_validators.dart'; import '../utils/text_container.dart'; final mobileNumberValidator = FormBuilderValidators.compose([ - FormBuilderValidators.minLength(11), + FormBuilderValidators.equalLength(11), FormBuilderValidators.required(errorText: mobileNumberRequired), FormBuilderValidators.numeric(errorText: numericValidator) ]); diff --git a/unit2/pubspec.lock b/unit2/pubspec.lock index e19a094..d52d27b 100644 --- a/unit2/pubspec.lock +++ b/unit2/pubspec.lock @@ -29,6 +29,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.0" + awesome_dialog: + dependency: "direct main" + description: + name: awesome_dialog + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" azlistview: dependency: "direct main" description: @@ -106,13 +113,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.1.0+1" - cool_alert: - dependency: "direct main" - description: - name: cool_alert - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" crypto: dependency: transitive description: @@ -176,13 +176,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.1" - flare_flutter: - dependency: transitive - description: - name: flare_flutter - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.2" flutter: dependency: "direct main" description: flutter @@ -301,6 +294,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.1.1" + graphs: + dependency: transitive + description: + name: graphs + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" http: dependency: transitive description: @@ -315,6 +315,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.0.2" + image: + dependency: transitive + description: + name: image + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.2" intl: dependency: "direct main" description: @@ -350,13 +357,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.0" - lottie: - dependency: transitive - description: - name: lottie - url: "https://pub.dartlang.org" - source: hosted - version: "1.4.3" matcher: dependency: transitive description: @@ -532,6 +532,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.0.0" + rive: + dependency: transitive + description: + name: rive + url: "https://pub.dartlang.org" + source: hosted + version: "0.9.1" rxdart: dependency: transitive description: @@ -602,6 +609,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.1" + signature: + dependency: "direct main" + description: + name: signature + url: "https://pub.dartlang.org" + source: hosted + version: "5.3.0" sky_engine: dependency: transitive description: flutter diff --git a/unit2/pubspec.yaml b/unit2/pubspec.yaml index 6947ded..e6afd2a 100644 --- a/unit2/pubspec.yaml +++ b/unit2/pubspec.yaml @@ -56,8 +56,9 @@ dependencies: date_time_picker: ^2.1.0 flutter_progress_hud: ^2.0.2 barcode_scan2: ^4.2.1 - cool_alert: ^1.1.0 qr_flutter: ^4.0.0 + signature: ^5.3.0 + awesome_dialog: ^3.0.2 dev_dependencies: flutter_test: From f973820a3b6f08f44d9f214cb96c0a5828076311 Mon Sep 17 00:00:00 2001 From: rodolfobacuinjr Date: Tue, 17 Jan 2023 15:52:31 +0800 Subject: [PATCH 09/86] add request and url utils --- unit2/lib/screens/sos/add_mobile.dart | 2 + unit2/lib/screens/sos/sos_received.dart | 2 +- unit2/lib/utils/request.dart | 97 +++++++++++++++++++++++++ unit2/lib/utils/text_container.dart | 13 +++- unit2/lib/utils/urls.dart | 13 ++++ unit2/pubspec.lock | 41 ++++++++--- unit2/pubspec.yaml | 1 + 7 files changed, 154 insertions(+), 15 deletions(-) create mode 100644 unit2/lib/utils/request.dart create mode 100644 unit2/lib/utils/urls.dart diff --git a/unit2/lib/screens/sos/add_mobile.dart b/unit2/lib/screens/sos/add_mobile.dart index 185d003..128aaa8 100644 --- a/unit2/lib/screens/sos/add_mobile.dart +++ b/unit2/lib/screens/sos/add_mobile.dart @@ -26,6 +26,8 @@ class AddMobile extends StatelessWidget { child: SafeArea( child: Scaffold( appBar: AppBar( + title: const Text("Add contact info"), + centerTitle: true, backgroundColor: primary, elevation: 0, ), diff --git a/unit2/lib/screens/sos/sos_received.dart b/unit2/lib/screens/sos/sos_received.dart index b25cd14..c89b81c 100644 --- a/unit2/lib/screens/sos/sos_received.dart +++ b/unit2/lib/screens/sos/sos_received.dart @@ -65,7 +65,7 @@ class SOSreceived extends StatelessWidget { ), Positioned( top: blockSizeVertical * 3, - child: SpinKitPulse( + child: const SpinKitPulse( color: primary, size: 120, ), diff --git a/unit2/lib/utils/request.dart b/unit2/lib/utils/request.dart new file mode 100644 index 0000000..ef472e6 --- /dev/null +++ b/unit2/lib/utils/request.dart @@ -0,0 +1,97 @@ +import 'dart:async'; +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:http/http.dart'; +import 'package:unit2/utils/text_container.dart'; +import 'package:unit2/utils/urls.dart'; + +class Request { + static final Request _instance = Request(); + static Request get instance => _instance; + int requestTimeout = 25; + String host = Url.instance.host(); + + Future getRequest( + {String? path, + Map? headers, + Map? param}) async { + Response response; + try { + response = await get(Uri.http(host, path!, param), headers: headers) + .timeout(Duration(seconds: requestTimeout)); + } on TimeoutException catch (_) { + Fluttertoast.showToast( + msg: timeoutError, + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.black, + ); + throw (timeoutError); + } on SocketException catch (_) { + Fluttertoast.showToast( + msg: timeoutError, + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.black, + ); + throw (timeoutError); + } on FormatException catch (_) { + throw const FormatException(formatError); + } on HttpException catch (_) { + throw const HttpException(httpError); + } on Error catch (e) { + debugPrint("get request error: $e"); + Fluttertoast.showToast( + msg: onError, + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.black, + ); + throw (onError); + } + return response; + } + + Future postRequest( + {String? path, + Map? headers, + Map? body, + Map? param}) async { + Response response; + try { + response = await post(Uri.http(host, path!, param), headers: headers) + .timeout(Duration(seconds: requestTimeout)); + } on TimeoutException catch (_) { + Fluttertoast.showToast( + msg: timeoutError, + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.black, + ); + throw (timeoutError); + } on SocketException catch (_) { + Fluttertoast.showToast( + msg: timeoutError, + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.black, + ); + throw (timeoutError); + } on FormatException catch (_) { + throw const FormatException(formatError); + } on HttpException catch (_) { + throw const HttpException(httpError); + } on Error catch (e) { + debugPrint("post request error: $e"); + Fluttertoast.showToast( + msg: onError, + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.black, + ); + throw (onError); + } + return response; + } +} diff --git a/unit2/lib/utils/text_container.dart b/unit2/lib/utils/text_container.dart index e102dbb..9fe0563 100644 --- a/unit2/lib/utils/text_container.dart +++ b/unit2/lib/utils/text_container.dart @@ -5,7 +5,7 @@ const String mobileNumberRequired = "You must add atleast one mobile"; const String numericValidator = "Please a number only"; const String mobile1 = "Mobile number 1"; const String mobile2 = "Mobile number 2"; -const String currentLocation = "You current location"; +const String currentLocation = "Your current location"; const String noModule = "No Module Assign"; const String noModuleSubTitle = "Please contact the admin if you want to access a module."; @@ -57,13 +57,18 @@ const String tapToScanQR = "TAP TO SCAN QR CODE"; const String incoming = "INCOMING"; const String setQRScannerSettings = "Set QR Scanner Settings"; const String includeOtherInputs = "Include other inputs?"; -const String includeOtherInputsSubTitle = "Inputs such as body temperature, etc."; +const String includeOtherInputsSubTitle = + "Inputs such as body temperature, etc."; const String incomingORoutgoing = "Incoming or Outgoing?"; -const String incomingORoutgoingSubTitle = "incoming for entrance outgoing for exit."; +const String incomingORoutgoingSubTitle = + "incoming for entrance outgoing for exit."; const String fieldIsRequired = "This field is required"; const String selectLevel = "Select level"; const String selectEstablishment = "Select Establishment"; -// +const String timeoutError = "Internet timeout! Please Check your connection"; +const String formatError = "Invalid Error"; +const String httpError = "Error getting requested data"; +const String onError = "Something went wrong! Please try again."; // // diff --git a/unit2/lib/utils/urls.dart b/unit2/lib/utils/urls.dart new file mode 100644 index 0000000..77defa3 --- /dev/null +++ b/unit2/lib/utils/urls.dart @@ -0,0 +1,13 @@ +class Url{ + static final Url _instance = Url(); + static Url get instance => instance; + + + String host(){ + return '192.168.10.219:3000'; + } + + String authentication(){ + return '/api/account/auth/login'; + } +} \ No newline at end of file diff --git a/unit2/pubspec.lock b/unit2/pubspec.lock index d52d27b..fa07d08 100644 --- a/unit2/pubspec.lock +++ b/unit2/pubspec.lock @@ -49,7 +49,7 @@ packages: name: barcode_scan2 url: "https://pub.dartlang.org" source: hosted - version: "4.2.1" + version: "4.2.3" boolean_selector: dependency: transitive description: @@ -169,6 +169,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "6.1.4" + file_utils: + dependency: transitive + description: + name: file_utils + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" fixnum: dependency: transitive description: @@ -201,7 +208,7 @@ packages: name: flutter_custom_clippers url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.0" flutter_form_builder: dependency: "direct main" description: @@ -272,7 +279,7 @@ packages: name: fluttertoast url: "https://pub.dartlang.org" source: hosted - version: "8.1.1" + version: "8.1.2" form_builder_validators: dependency: "direct main" description: @@ -287,6 +294,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.2.0" + globbing: + dependency: transitive + description: + name: globbing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" go_router: dependency: "direct main" description: @@ -321,7 +335,7 @@ packages: name: image url: "https://pub.dartlang.org" source: hosted - version: "3.2.2" + version: "3.3.0" intl: dependency: "direct main" description: @@ -517,7 +531,7 @@ packages: name: provider url: "https://pub.dartlang.org" source: hosted - version: "6.0.4" + version: "6.0.5" qr: dependency: transitive description: @@ -580,14 +594,14 @@ packages: name: shared_preferences_linux url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "2.1.2" shared_preferences_macos: dependency: transitive description: name: shared_preferences_macos url: "https://pub.dartlang.org" source: hosted - version: "2.0.4" + version: "2.0.5" shared_preferences_platform_interface: dependency: transitive description: @@ -608,7 +622,7 @@ packages: name: shared_preferences_windows url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "2.1.2" signature: dependency: "direct main" description: @@ -634,7 +648,7 @@ packages: name: sqflite url: "https://pub.dartlang.org" source: hosted - version: "2.2.0+3" + version: "2.2.2" sqflite_common: dependency: transitive description: @@ -670,6 +684,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.0+3" + system_info2: + dependency: "direct main" + description: + name: system_info2 + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" term_glyph: dependency: transitive description: @@ -718,7 +739,7 @@ packages: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "3.1.2" + version: "3.1.3" xdg_directories: dependency: transitive description: diff --git a/unit2/pubspec.yaml b/unit2/pubspec.yaml index e6afd2a..19b17d2 100644 --- a/unit2/pubspec.yaml +++ b/unit2/pubspec.yaml @@ -59,6 +59,7 @@ dependencies: qr_flutter: ^4.0.0 signature: ^5.3.0 awesome_dialog: ^3.0.2 + system_info2: ^2.0.4 dev_dependencies: flutter_test: From 224b5ce7ac92964e6d7f428c82942df8eb83e855 Mon Sep 17 00:00:00 2001 From: rodolfobacuinjr Date: Wed, 18 Jan 2023 15:54:44 +0800 Subject: [PATCH 10/86] add userbloc and perform version info check --- unit2/lib/bloc/bloc/user_bloc.dart | 26 + unit2/lib/bloc/bloc/user_event.dart | 13 + unit2/lib/bloc/bloc/user_state.dart | 36 ++ unit2/lib/main.dart | 35 +- .../login_data/employee_info/department.dart | 41 ++ .../employee_info/employee_info.dart | 137 +++++ .../model/login_data/employee_info/head.dart | 44 ++ .../login_data/employee_info/office.dart | 47 ++ .../employee_info/position_class.dart | 65 +++ .../login_data/user_info/assigned_area.dart | 31 ++ .../login_data/user_info/login_user.dart | 48 ++ .../lib/model/login_data/user_info/modue.dart | 68 +++ .../lib/model/login_data/user_info/role.dart | 74 +++ .../model/login_data/user_info/user_data.dart | 71 +++ unit2/lib/model/login_data/version_info.dart | 48 ++ unit2/lib/screens/unit2/login/login.dart | 470 +++++++++--------- .../sevices/login_service/auth_service.dart | 39 ++ unit2/lib/utils/router.dart | 9 +- unit2/lib/utils/urls.dart | 8 +- unit2/lib/widgets/error_state.dart | 13 + unit2/lib/widgets/splash_screen.dart | 12 + unit2/pubspec.lock | 21 + unit2/pubspec.yaml | 2 + 23 files changed, 1118 insertions(+), 240 deletions(-) create mode 100644 unit2/lib/bloc/bloc/user_bloc.dart create mode 100644 unit2/lib/bloc/bloc/user_event.dart create mode 100644 unit2/lib/bloc/bloc/user_state.dart create mode 100644 unit2/lib/model/login_data/employee_info/department.dart create mode 100644 unit2/lib/model/login_data/employee_info/employee_info.dart create mode 100644 unit2/lib/model/login_data/employee_info/head.dart create mode 100644 unit2/lib/model/login_data/employee_info/office.dart create mode 100644 unit2/lib/model/login_data/employee_info/position_class.dart create mode 100644 unit2/lib/model/login_data/user_info/assigned_area.dart create mode 100644 unit2/lib/model/login_data/user_info/login_user.dart create mode 100644 unit2/lib/model/login_data/user_info/modue.dart create mode 100644 unit2/lib/model/login_data/user_info/role.dart create mode 100644 unit2/lib/model/login_data/user_info/user_data.dart create mode 100644 unit2/lib/model/login_data/version_info.dart create mode 100644 unit2/lib/sevices/login_service/auth_service.dart create mode 100644 unit2/lib/widgets/error_state.dart create mode 100644 unit2/lib/widgets/splash_screen.dart diff --git a/unit2/lib/bloc/bloc/user_bloc.dart b/unit2/lib/bloc/bloc/user_bloc.dart new file mode 100644 index 0000000..1d10614 --- /dev/null +++ b/unit2/lib/bloc/bloc/user_bloc.dart @@ -0,0 +1,26 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import 'package:unit2/model/login_data/version_info.dart'; +import 'package:unit2/sevices/login_service/auth_service.dart'; +import 'package:unit2/widgets/error_state.dart'; + +part 'user_event.dart'; +part 'user_state.dart'; + +class UserBloc extends Bloc { + UserBloc() : super(UserInitial()) { + on((event, emit) async { + debugPrint("getApkEvent is called"); + try { + emit(SplashScreen()); + VersionInfo versionInfo = await AuthService.instance.getVersionInfo(); + emit(VersionLoaded(versionInfo: versionInfo)); + } catch (e) { + emit(UserError( + message: e.toString(), + )); + } + }); + } +} diff --git a/unit2/lib/bloc/bloc/user_event.dart b/unit2/lib/bloc/bloc/user_event.dart new file mode 100644 index 0000000..1094d9f --- /dev/null +++ b/unit2/lib/bloc/bloc/user_event.dart @@ -0,0 +1,13 @@ +part of 'user_bloc.dart'; + +abstract class UserEvent extends Equatable { + const UserEvent(); + + @override + List get props => []; +} + +class GetApkVersion extends UserEvent{ + @override + List get props => []; +} diff --git a/unit2/lib/bloc/bloc/user_state.dart b/unit2/lib/bloc/bloc/user_state.dart new file mode 100644 index 0000000..c9b4833 --- /dev/null +++ b/unit2/lib/bloc/bloc/user_state.dart @@ -0,0 +1,36 @@ +part of 'user_bloc.dart'; + +abstract class UserState extends Equatable { + const UserState(); + + @override + List get props => []; +} + +class UserInitial extends UserState {} + +class UserLoading extends UserState { + final String? message; + const UserLoading({this.message}); + @override + List get props => [message!]; +} + +class SplashScreen extends UserState { + @override + List get props => []; +} + +class UserError extends UserState { + final String? message; + const UserError({this.message}); + @override + List get props => []; +} + +class VersionLoaded extends UserState { + final VersionInfo? versionInfo; + const VersionLoaded({this.versionInfo}); + @override + List get props => [versionInfo!]; +} diff --git a/unit2/lib/main.dart b/unit2/lib/main.dart index 9b80a31..af6b45d 100644 --- a/unit2/lib/main.dart +++ b/unit2/lib/main.dart @@ -2,6 +2,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:device_preview/device_preview.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:unit2/bloc/bloc/user_bloc.dart'; import './utils/router.dart'; import './utils/global.dart'; @@ -35,23 +37,26 @@ class MyApp extends StatelessWidget { safeBlockHorizontal = (screenWidth - safeAreaHorizontal) / 100; safeBlockVertical = (screenHeight - safeAreaVertical) / 100; - return MaterialApp.router( - // useInheritedMediaQuery: true, - // locale: DevicePreview.locale(context), - // builder: DevicePreview.appBuilder, - routeInformationParser: goRouter.routeInformationParser, - routerDelegate: goRouter.routerDelegate, - // routeInformationProvider: goRouter.routeInformationProvider, - title: 'uniT2 - Universal Tracker and Tracer', - theme: ThemeData( - appBarTheme: const AppBarTheme( - systemOverlayStyle: SystemUiOverlayStyle( - statusBarBrightness: Brightness.dark, - statusBarColor: Colors.black), + return BlocProvider( + create: (context) => UserBloc(), + child: MaterialApp.router( + // useInheritedMediaQuery: true, + // locale: DevicePreview.locale(context), + // builder: DevicePreview.appBuilder, + routeInformationParser: goRouter.routeInformationParser, + routerDelegate: goRouter.routerDelegate, + // routeInformationProvider: goRouter.routeInformationProvider, + title: 'uniT2 - Universal Tracker and Tracer', + theme: ThemeData( + appBarTheme: const AppBarTheme( + systemOverlayStyle: SystemUiOverlayStyle( + statusBarBrightness: Brightness.dark, + statusBarColor: Colors.black), + ), + fontFamily: 'LexendDeca', ), - fontFamily: 'LexendDeca', + debugShowCheckedModeBanner: false, ), - debugShowCheckedModeBanner: false, ); } } diff --git a/unit2/lib/model/login_data/employee_info/department.dart b/unit2/lib/model/login_data/employee_info/department.dart new file mode 100644 index 0000000..c6d6d78 --- /dev/null +++ b/unit2/lib/model/login_data/employee_info/department.dart @@ -0,0 +1,41 @@ +import 'package:unit2/model/login_data/employee_info/head.dart'; + +class Department { + Department({ + this.id, + this.code, + this.head, + this.name, + this.acronym, + this.parentStationId, + this.fullCode, + }); + + int? id; + String? code; + Head? head; + String? name; + int? acronym; + int? parentStationId; + String? fullCode; + + factory Department.fromJson(Map json) => Department( + id: json["id"], + code: json["code"], + head: json["head"], + name: json["name"], + acronym: json["acronym"], + parentStationId: json["parent_station_id"], + fullCode: json["full_code"], + ); + + Map toJson() => { + "id": id, + "code": code, + "head": head, + "name": name, + "acronym": acronym, + "parent_station_id": parentStationId, + "full_code": fullCode, + }; +} \ No newline at end of file diff --git a/unit2/lib/model/login_data/employee_info/employee_info.dart b/unit2/lib/model/login_data/employee_info/employee_info.dart new file mode 100644 index 0000000..5655fed --- /dev/null +++ b/unit2/lib/model/login_data/employee_info/employee_info.dart @@ -0,0 +1,137 @@ +import 'package:unit2/model/login_data/employee_info/office.dart'; + +class EmployeeInfo { + EmployeeInfo({ + this.employeeId, + this.empid, + this.classid, + this.uuid, + this.office, + this.profile, + }); + + String? employeeId; + int? empid; + String? classid; + String? uuid; + Office? office; + Profile? profile; + + factory EmployeeInfo.fromJson(Map json) => EmployeeInfo( + employeeId: json["employee_id"], + empid: json["empid"], + classid: json["classid"], + uuid: json["uuid"], + office: Office.fromJson(json["office"]), + profile: Profile.fromJson(json["profile"]), + ); + + Map toJson() => { + "employee_id": employeeId, + "empid": empid, + "classid": classid, + "uuid": uuid, + "office": office!.toJson(), + "profile": profile!.toJson(), + }; +} +class Profile { + Profile({ + this.id, + this.sex, + this.gender, + this.deceased, + this.heightM, + this.birthdate, + this.esigPath, + this.fullName, + this.lastName, + this.weightKg, + this.bloodType, + this.firstName, + this.photoPath, + this.maidenName, + this.middleName, + this.uuidQrcode, + this.civilStatus, + this.titlePrefix, + this.titleSuffix, + this.showTitleId, + this.lastFullName, + this.nameExtension, + }); + + int? id; + String? sex; + dynamic gender; + bool? deceased; + double? heightM; + DateTime? birthdate; + String? esigPath; + String? fullName; + String? lastName; + int? weightKg; + String? bloodType; + String? firstName; + String? photoPath; + String? maidenName; + String? middleName; + String? uuidQrcode; + String? civilStatus; + String? titlePrefix; + String? titleSuffix; + bool? showTitleId; + String? lastFullName; + String? nameExtension; + + factory Profile.fromJson(Map json) => Profile( + id: json["id"], + sex: json["sex"], + gender: json["gender"], + deceased: json["deceased"], + heightM: json["height_m"].toDouble(), + birthdate: DateTime.parse(json["birthdate"]), + esigPath: json["esig_path"], + fullName: json["full_name"], + lastName: json["last_name"], + weightKg: json["weight_kg"], + bloodType: json["blood_type"], + firstName: json["first_name"], + photoPath: json["photo_path"], + maidenName: json["maiden_name"], + middleName: json["middle_name"], + uuidQrcode: json["uuid_qrcode"], + civilStatus: json["civil_status"], + titlePrefix: json["title_prefix"], + titleSuffix: json["title_suffix"], + showTitleId: json["show_title_id"], + lastFullName: json["last_full_name"], + nameExtension: json["name_extension"], + ); + + Map toJson() => { + "id": id, + "sex": sex, + "gender": gender, + "deceased": deceased, + "height_m": heightM, + "birthdate": + "${birthdate!.year.toString().padLeft(4, '0')}-${birthdate!.month.toString().padLeft(2, '0')}-${birthdate!.day.toString().padLeft(2, '0')}", + "esig_path": esigPath, + "full_name": fullName, + "last_name": lastName, + "weight_kg": weightKg, + "blood_type": bloodType, + "first_name": firstName, + "photo_path": photoPath, + "maiden_name": maidenName, + "middle_name": middleName, + "uuid_qrcode": uuidQrcode, + "civil_status": civilStatus, + "title_prefix": titlePrefix, + "title_suffix": titleSuffix, + "show_title_id": showTitleId, + "last_full_name": lastFullName, + "name_extension": nameExtension, + }; +} diff --git a/unit2/lib/model/login_data/employee_info/head.dart b/unit2/lib/model/login_data/employee_info/head.dart new file mode 100644 index 0000000..afeee07 --- /dev/null +++ b/unit2/lib/model/login_data/employee_info/head.dart @@ -0,0 +1,44 @@ + +class Head { + Head({ + this.id, + this.title, + this.classid, + this.fullName, + this.personId, + this.employeeid, + this.officeposid, + this.lastFullName, + }); + + int? id; + String? title; + String? classid; + String? fullName; + int? personId; + String? employeeid; + int? officeposid; + String? lastFullName; + + factory Head.fromJson(Map json) => Head( + id: json["id"], + title: json["title"], + classid: json["classid"], + fullName: json["full_name"], + personId: json["person_id"], + employeeid: json["employeeid"], + officeposid: json["officeposid"], + lastFullName: json["last_full_name"], + ); + + Map toJson() => { + "id": id, + "title": title, + "classid": classid, + "full_name": fullName, + "person_id": personId, + "employeeid": employeeid, + "officeposid": officeposid, + "last_full_name": lastFullName, + }; +} \ No newline at end of file diff --git a/unit2/lib/model/login_data/employee_info/office.dart b/unit2/lib/model/login_data/employee_info/office.dart new file mode 100644 index 0000000..bd06e2e --- /dev/null +++ b/unit2/lib/model/login_data/employee_info/office.dart @@ -0,0 +1,47 @@ +import 'department.dart'; +import 'position_class.dart'; + +class Office { + Office({ + this.unit, + this.section, + this.division, + this.posstatid, + this.department, + this.stationId, + this.positionClass, + this.positionSpecificrole, + }); + + Department? unit; + Department? section; + Department? division; + int? posstatid; + Department? department; + int? stationId; + PositionClass? positionClass; + PositionSpecificrole? positionSpecificrole; + + factory Office.fromJson(Map json) => Office( + unit: Department.fromJson(json["unit"]), + section: Department.fromJson(json["section"]), + division: Department.fromJson(json["division"]), + posstatid: json["posstatid"], + department: Department.fromJson(json["department"]), + stationId: json["station_id"], + positionClass: PositionClass.fromJson(json["position_class"]), + positionSpecificrole: + PositionSpecificrole.fromJson(json["position_specificrole"]), + ); + + Map toJson() => { + "unit": unit!.toJson(), + "section": section!.toJson(), + "division": division!.toJson(), + "posstatid": posstatid, + "department": department!.toJson(), + "station_id": stationId, + "position_class": positionClass!.toJson(), + "position_specificrole": positionSpecificrole!.toJson(), + }; +} \ No newline at end of file diff --git a/unit2/lib/model/login_data/employee_info/position_class.dart b/unit2/lib/model/login_data/employee_info/position_class.dart new file mode 100644 index 0000000..878847f --- /dev/null +++ b/unit2/lib/model/login_data/employee_info/position_class.dart @@ -0,0 +1,65 @@ +class PositionClass { + PositionClass({ + this.level, + this.classid, + this.groupcode, + this.salarygrade, + this.classSuffix, + this.classTitleid, + this.qualificationid, + this.competencyModelId, + }); + + int? level; + String? classid; + String? groupcode; + int? salarygrade; + String? classSuffix; + PositionSpecificrole? classTitleid; + int? qualificationid; + int? competencyModelId; + + factory PositionClass.fromJson(Map json) => PositionClass( + level: json["level"], + classid: json["classid"], + groupcode: json["groupcode"], + salarygrade: json["salarygrade"], + classSuffix: json["class_suffix"], + classTitleid: PositionSpecificrole.fromJson(json["class_titleid"]), + qualificationid: json["qualificationid"], + competencyModelId: json["competency_model_id"], + ); + + Map toJson() => { + "level": level, + "classid": classid, + "groupcode": groupcode, + "salarygrade": salarygrade, + "class_suffix": classSuffix, + "class_titleid": classTitleid!.toJson(), + "qualificationid": qualificationid, + "competency_model_id": competencyModelId, + }; +} + + +class PositionSpecificrole { + PositionSpecificrole({ + this.title, + this.titleid, + }); + + String? title; + int? titleid; + + factory PositionSpecificrole.fromJson(Map json) => + PositionSpecificrole( + title: json["title"], + titleid: json["titleid"], + ); + + Map toJson() => { + "title": title, + "titleid": titleid, + }; +} \ No newline at end of file diff --git a/unit2/lib/model/login_data/user_info/assigned_area.dart b/unit2/lib/model/login_data/user_info/assigned_area.dart new file mode 100644 index 0000000..d67b215 --- /dev/null +++ b/unit2/lib/model/login_data/user_info/assigned_area.dart @@ -0,0 +1,31 @@ +class AssignedArea { + AssignedArea({ + this.id, + this.areaid, + this.isactive, + this.areaName, + this.areaTypeName, + }); + + int? id; + String? areaid; + bool? isactive; + String? areaName; + String? areaTypeName; + + factory AssignedArea.fromJson(Map json) => AssignedArea( + id: json["id"], + areaid: json["areaid"], + isactive: json["isactive"], + areaName: json["area_name"], + areaTypeName: json["area_type_name"], + ); + + Map toJson() => { + "id": id, + "areaid": areaid, + "isactive": isactive, + "area_name": areaName, + "area_type_name": areaTypeName, + }; +} \ No newline at end of file diff --git a/unit2/lib/model/login_data/user_info/login_user.dart b/unit2/lib/model/login_data/user_info/login_user.dart new file mode 100644 index 0000000..8d492b7 --- /dev/null +++ b/unit2/lib/model/login_data/user_info/login_user.dart @@ -0,0 +1,48 @@ +import 'role.dart'; +class LoginUser { + LoginUser({ + this.id, + this.profileId, + this.firstName, + this.lastName, + this.username, + this.email, + this.staff, + this.roles, + }); + + int? id; + int? profileId; + String? firstName; + String? lastName; + String? username; + String? email; + bool? staff; + List? roles; + + factory LoginUser.fromJson(Map json) => LoginUser( + id: json["id"], + profileId: json["profile_id"], + firstName: json["first_name"], + lastName: json["last_name"], + username: json["username"], + email: json["email"], + staff: json["staff"], + roles: json["roles"] == null + ? [] + : List.from(json["roles"]!.map((x) => Role.fromJson(x))), + ); + + Map toJson() => { + "id": id, + "profile_id": profileId, + "first_name": firstName, + "last_name": lastName, + "username": username, + "email": email, + "staff": staff, + "roles": roles == null + ? [] + : List.from(roles!.map((x) => x!.toJson())), + }; +} \ No newline at end of file diff --git a/unit2/lib/model/login_data/user_info/modue.dart b/unit2/lib/model/login_data/user_info/modue.dart new file mode 100644 index 0000000..700d9a5 --- /dev/null +++ b/unit2/lib/model/login_data/user_info/modue.dart @@ -0,0 +1,68 @@ +class Module { + Module({ + this.id, + this.icon, + this.name, + this.slug, + this.objects, + }); + + int? id; + String? icon; + String? name; + String? slug; + List? objects; + + factory Module.fromJson(Map json) => Module( + id: json["id"], + icon: json["icon"], + name: json["name"], + slug: json["slug"], + objects: json["objects"] == null + ? [] + : List.from( + json["objects"]!.map((x) => Object.fromJson(x))), + ); + + Map toJson() => { + "id": id, + "icon": icon, + "name": name, + "slug": slug, + "objects": objects == null + ? [] + : List.from(objects!.map((x) => x!.toJson())), + }; +} + +class Object { + Object({ + this.id, + this.name, + this.slug, + this.operations, + }); + + int? id; + String? name; + String? slug; + List? operations; + + factory Object.fromJson(Map json) => Object( + id: json["id"], + name: json["name"], + slug: json["slug"], + operations: json["operations"] == null + ? [] + : List.from(json["operations"]!.map((x) => x)), + ); + + Map toJson() => { + "id": id, + "name": name, + "slug": slug, + "operations": operations == null + ? [] + : List.from(operations!.map((x) => x)), + }; +} \ No newline at end of file diff --git a/unit2/lib/model/login_data/user_info/role.dart b/unit2/lib/model/login_data/user_info/role.dart new file mode 100644 index 0000000..5975d6d --- /dev/null +++ b/unit2/lib/model/login_data/user_info/role.dart @@ -0,0 +1,74 @@ +import 'assigned_area.dart'; +import 'modue.dart'; + +class Role { + Role({ + this.id, + this.name, + this.modules, + this.assignedArea, + }); + + int? id; + String? name; + List? modules; + List? assignedArea; + + factory Role.fromJson(Map json) => Role( + id: json["id"], + name: json["name"], + modules: json["modules"] == null + ? [] + : List.from( + json["modules"]!.map((x) => Module.fromJson(x))), + assignedArea: json["assigned_area"] == null + ? [] + : json["assigned_area"] == null + ? [] + : List.from(json["assigned_area"]! + .map((x) => AssignedArea.fromJson(x))), + ); + + Map toJson() => { + "id": id, + "name": name, + "modules": modules == null + ? [] + : List.from(modules!.map((x) => x!.toJson())), + "assigned_area": assignedArea == null + ? [] + : assignedArea == null + ? [] + : List.from(assignedArea!.map((x) => x!.toJson())), + }; +}class Object { + Object({ + this.id, + this.name, + this.slug, + this.operations, + }); + + int? id; + String? name; + String? slug; + List? operations; + + factory Object.fromJson(Map json) => Object( + id: json["id"], + name: json["name"], + slug: json["slug"], + operations: json["operations"] == null + ? [] + : List.from(json["operations"]!.map((x) => x)), + ); + + Map toJson() => { + "id": id, + "name": name, + "slug": slug, + "operations": operations == null + ? [] + : List.from(operations!.map((x) => x)), + }; +} \ No newline at end of file diff --git a/unit2/lib/model/login_data/user_info/user_data.dart b/unit2/lib/model/login_data/user_info/user_data.dart new file mode 100644 index 0000000..470404c --- /dev/null +++ b/unit2/lib/model/login_data/user_info/user_data.dart @@ -0,0 +1,71 @@ +import 'dart:convert'; + +import 'package:unit2/model/login_data/employee_info/employee_info.dart'; + +import 'login_user.dart'; + +UserData? userDataFromJson(String str) => UserData.fromJson(json.decode(str)); + +String userDataToJson(UserData? data) => json.encode(data!.toJson()); + +class UserData { + UserData({ + this.user, + this.employeeInfo, + }); + + UserDataUser? user; + EmployeeInfo? employeeInfo; + + factory UserData.fromJson(Map json) => UserData( + user: UserDataUser.fromJson(json["user"]), + employeeInfo: EmployeeInfo.fromJson(json["employee_info"]), + ); + + Map toJson() => { + "user": user!.toJson(), + "employee_info": employeeInfo!.toJson(), + }; +} + +class UserDataUser { + UserDataUser({ + this.login, + }); + + Login? login; + + factory UserDataUser.fromJson(Map json) => UserDataUser( + login: Login.fromJson(json["login"]), + ); + + Map toJson() => { + "login": login!.toJson(), + }; +} + +class Login { + Login({ + this.dateTime, + this.token, + this.user, + }); + + DateTime? dateTime; + String? token; + LoginUser? user; + + factory Login.fromJson(Map json) => Login( + dateTime: DateTime.parse(json["date_time"]), + token: json["token"], + user: LoginUser.fromJson(json["user"]), + ); + + Map toJson() => { + "date_time": dateTime?.toIso8601String(), + "token": token, + "user": user!.toJson(), + }; +} + + diff --git a/unit2/lib/model/login_data/version_info.dart b/unit2/lib/model/login_data/version_info.dart new file mode 100644 index 0000000..12ae4fb --- /dev/null +++ b/unit2/lib/model/login_data/version_info.dart @@ -0,0 +1,48 @@ +class VersionInfo { + VersionInfo({ + this.version, + this.versionInfo, + this.dateReleased, + this.development, + this.production, + this.downloadUrl, + this.arm64v8aDownloadUrl, + this.armeabiv7aDownloadUrl, + this.x8664DownloadUrl, + }); + + String? version; + String? versionInfo; + DateTime? dateReleased; + bool? development; + bool? production; + String? downloadUrl; + String? arm64v8aDownloadUrl; + String? armeabiv7aDownloadUrl; + String? x8664DownloadUrl; + + factory VersionInfo.fromJson(Map json) => VersionInfo( + version: json["version"], + versionInfo: json["version_info"], + dateReleased: DateTime.parse(json["date_released"]), + development: json["development"], + production: json["production"], + downloadUrl: json["download_url"], + arm64v8aDownloadUrl: json["arm64_v8a_download_url"], + armeabiv7aDownloadUrl: json["armeabi_v7a_download_url"], + x8664DownloadUrl: json["x86_64_down_download_url"], + ); + + Map toJson() => { + "version": version, + "version_info": versionInfo, + "date_released": + "${dateReleased!.year.toString().padLeft(4, '0')}-${dateReleased!.month.toString().padLeft(2, '0')}-${dateReleased!.day.toString().padLeft(2, '0')}", + "development": development, + "production": production, + "download_url": downloadUrl, + "arm64_v8a_download_url": arm64v8aDownloadUrl, + "armeabi_v7a_download_url": armeabiv7aDownloadUrl, + "x86_64_down_download_url": x8664DownloadUrl, + }; +} diff --git a/unit2/lib/screens/unit2/login/login.dart b/unit2/lib/screens/unit2/login/login.dart index 1ad705a..384a63e 100644 --- a/unit2/lib/screens/unit2/login/login.dart +++ b/unit2/lib/screens/unit2/login/login.dart @@ -1,5 +1,6 @@ import 'package:barcode_scan2/barcode_scan2.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; @@ -7,10 +8,13 @@ import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:go_router/go_router.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:unit2/bloc/bloc/user_bloc.dart'; import 'package:unit2/utils/alerts.dart'; import 'package:unit2/utils/scanner.dart'; import 'package:unit2/utils/text_container.dart'; +import 'package:unit2/widgets/error_state.dart'; +import '../../../widgets/splash_screen.dart'; import '../../../widgets/wave.dart'; import '../../../utils/global.dart'; import '../../../theme-data.dart/colors.dart'; @@ -36,236 +40,258 @@ class _UniT2LoginState extends State { onWillPop: pressAgainToExit, child: Scaffold( body: ProgressHUD( - child: Builder(builder: (context) { - return SizedBox( - child: SingleChildScrollView( - child: Stack( - children: [ - Positioned( - bottom: 0, - right: 0, - child: WaveReverse(height: blockSizeVertical * 7)), - SizedBox( - height: blockSizeVertical * 100, - child: FormBuilder( - key: _formKey, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 25), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox(height: blockSizeVertical * 7), - SvgPicture.asset( - 'assets/svgs/logo.svg', - height: blockSizeVertical * 16, - allowDrawingOutsideViewBox: true, - color: primary, - ), - - Text( - welcome, - style: TextStyle( - fontSize: blockSizeVertical * 5, - fontWeight: FontWeight.w600), - ), - Text(unitApp, - style: TextStyle( - fontSize: blockSizeVertical * 8, - fontWeight: FontWeight.w800, - letterSpacing: .2, - height: 1, - color: primary)), - Text( - loginToContinue, - style: TextStyle( - fontSize: blockSizeVertical * 2, - height: 1.5, - fontWeight: FontWeight.w600), - ), - SizedBox( - height: blockSizeVertical * 1.5, - ), - // USERNAME - FormBuilderTextField( - name: 'username', - validator: FormBuilderValidators.required( - errorText: usernameRequired), - autofocus: false, - style: const TextStyle( - fontWeight: FontWeight.bold, - color: Colors.black87), - decoration: loginTextFieldStyle().copyWith()), - SizedBox( - height: blockSizeVertical * 1.5, - ), - // PASSWORD - FormBuilderTextField( - name: 'password', - validator: FormBuilderValidators.required( - errorText: passwordRequired), - - // initialValue: state.password, - onChanged: (value) { - value!.isEmpty - ? setState(() { - showSuffixIcon = false; - }) - : setState(() { - showSuffixIcon = true; - }); - }, - autofocus: false, - style: const TextStyle( - fontWeight: FontWeight.bold, - color: Colors.black87), - decoration: loginTextFieldStyle().copyWith( - suffixIcon: Visibility( - visible: showSuffixIcon, - child: _showPassword - ? IconButton( - icon: Icon(FontAwesome5.eye_slash, - size: 24, - color: Theme.of(context) - .textTheme - .displayLarge - ?.color), - onPressed: () { - setState(() { - _showPassword = false; - }); - }, - ) - : IconButton( - onPressed: () { - setState(() { - _showPassword = true; - }); - }, - icon: Icon(FontAwesome5.eye, - size: 24, - color: Theme.of(context) - .textTheme - .displayLarge - ?.color)), - ), - prefixIcon: const Icon( - Icons.lock, - color: primary, - ), - labelText: "Password", - hintText: enterPassword), - obscureText: _showPassword ? true : false, - ), - SizedBox( - height: blockSizeVertical * 2, - ), - SizedBox( - height: blockSizeVertical * 7, - // Login Button - child: SizedBox( - width: MediaQuery.of(context).size.width, - child: ElevatedButton( - style: mainBtnStyle(second, - Colors.transparent, Colors.white54), - child: const Text( - login, - style: TextStyle(color: Colors.white), - ), - onPressed: () { - final progress = ProgressHUD.of(context); - progress?.showWithText( - 'Logging in...', - ); - FocusScope.of(context).unfocus(); - Future.delayed(const Duration(seconds: 5), - () { - progress!.dismiss(); - - context.goNamed("home"); - }); - // if (_formKey.currentState! - // .saveAndValidate()) { - // context.go(context.namedLocation('home')); - // } - - // if (_formKey.currentState.validate()) { - // _formKey.currentState.save(); - // BlocProvider.of(context) - // .add(UserWebLogin( - // password: password, - // username: username)); - // } - }, + child: BlocConsumer(listener: (context, state) { + if (state is UserLoading) { + final progress = ProgressHUD.of(context); + progress?.showWithText( + 'Logging in...', + ); + } + }, builder: (context, state) { + if (state is VersionLoaded) { + return Builder(builder: (context) { + return SizedBox( + child: SingleChildScrollView( + child: Stack( + children: [ + Positioned( + bottom: 0, + right: 0, + child: WaveReverse(height: blockSizeVertical * 7)), + SizedBox( + height: blockSizeVertical * 100, + child: FormBuilder( + key: _formKey, + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 25), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(height: blockSizeVertical * 7), + SvgPicture.asset( + 'assets/svgs/logo.svg', + height: blockSizeVertical * 16, + allowDrawingOutsideViewBox: true, + color: primary, ), - ), - ), - SizedBox( - height: blockSizeVertical * 1.5, - ), - SizedBox( - height: blockSizeVertical * 7, - child: SizedBox( + Text( + welcome, + style: TextStyle( + fontSize: blockSizeVertical * 5, + fontWeight: FontWeight.w600), + ), + Text(unitApp, + style: TextStyle( + fontSize: blockSizeVertical * 8, + fontWeight: FontWeight.w800, + letterSpacing: .2, + height: 1, + color: primary)), + Text( + loginToContinue, + style: TextStyle( + fontSize: blockSizeVertical * 2, + height: 1.5, + fontWeight: FontWeight.w600), + ), + SizedBox( + height: blockSizeVertical * 1.5, + ), + // USERNAME + FormBuilderTextField( + name: 'username', + validator: FormBuilderValidators.required( + errorText: usernameRequired), + autofocus: false, + style: const TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black87), + decoration: + loginTextFieldStyle().copyWith()), + SizedBox( + height: blockSizeVertical * 1.5, + ), + // PASSWORD + FormBuilderTextField( + name: 'password', + validator: FormBuilderValidators.required( + errorText: passwordRequired), + + // initialValue: state.password, + onChanged: (value) { + value!.isEmpty + ? setState(() { + showSuffixIcon = false; + }) + : setState(() { + showSuffixIcon = true; + }); + }, + autofocus: false, + style: const TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black87), + decoration: loginTextFieldStyle().copyWith( + suffixIcon: Visibility( + visible: showSuffixIcon, + child: _showPassword + ? IconButton( + icon: Icon( + FontAwesome5.eye_slash, + size: 24, + color: Theme.of(context) + .textTheme + .displayLarge + ?.color), + onPressed: () { + setState(() { + _showPassword = false; + }); + }, + ) + : IconButton( + onPressed: () { + setState(() { + _showPassword = true; + }); + }, + icon: Icon(FontAwesome5.eye, + size: 24, + color: Theme.of(context) + .textTheme + .displayLarge + ?.color)), + ), + prefixIcon: const Icon( + Icons.lock, + color: primary, + ), + labelText: "Password", + hintText: enterPassword), + obscureText: _showPassword ? true : false, + ), + SizedBox( + height: blockSizeVertical * 2, + ), + SizedBox( + height: blockSizeVertical * 7, + // Login Button + child: SizedBox( + width: MediaQuery.of(context).size.width, + child: ElevatedButton( + style: mainBtnStyle(second, + Colors.transparent, Colors.white54), + child: const Text( + login, + style: TextStyle(color: Colors.white), + ), + onPressed: () { + FocusScope.of(context).unfocus(); + // Future.delayed( + // const Duration(seconds: 5), () { + // progress!.dismiss(); + + // context.goNamed("home"); + // }); + // if (_formKey.currentState! + // .saveAndValidate()) { + // context.go(context.namedLocation('home')); + // } + + // if (_formKey.currentState.validate()) { + // _formKey.currentState.save(); + // BlocProvider.of(context) + // .add(UserWebLogin( + // password: password, + // username: username)); + // } + }, + ), + ), + ), + SizedBox( + height: blockSizeVertical * 1.5, + ), + + SizedBox( + height: blockSizeVertical * 7, + child: SizedBox( + width: + MediaQuery.of(context).size.width, + child: ElevatedButton.icon( + style: mainBtnStyle(Colors.white, + second, primary.withOpacity(.4)), + icon: const Icon( + Icons.qr_code, + color: second, + ), + label: const Text( + loginViaQr, + style: TextStyle(color: second), + ), + onPressed: () async { + context.goNamed('register'); + // ScanResult result = + // await QRCodeBarCodeScanner.instance + // .scanner(); + + // debugPrint(result.type.toString()); + // debugPrint( + // result.rawContent.toString()); + // BlocProvider.of(context) + // .add(QRCodelogin()); + }, + ), + )), + SizedBox( + height: blockSizeVertical * 1, + ), + const LoginViaQr(text: emergencyReponseLabel), + SizedBox( + height: blockSizeVertical * 1, + ), + // REQUEST SOS + SizedBox( + height: screenHeight * .07, width: MediaQuery.of(context).size.width, child: ElevatedButton.icon( - style: mainBtnStyle(Colors.white, second, - primary.withOpacity(.4)), - icon: const Icon( - Icons.qr_code, - color: second, - ), - label: const Text( - loginViaQr, - style: TextStyle(color: second), - ), - onPressed: () async { - context.goNamed('register'); - // ScanResult result = - // await QRCodeBarCodeScanner.instance - // .scanner(); - - // debugPrint(result.type.toString()); - // debugPrint( - // result.rawContent.toString()); - // BlocProvider.of(context) - // .add(QRCodelogin()); - }, - ), - )), - SizedBox( - height: blockSizeVertical * 1, + icon: const Icon( + FontAwesome5.life_ring, + color: Colors.white, + ), + style: mainBtnStyle(third, + Colors.transparent, Colors.white38), + onPressed: () { + context.goNamed('add-mobile'); + }, + label: const Text( + requestSOS, + style: TextStyle(color: Colors.white), + )), + ) + ], ), - const LoginViaQr(text: emergencyReponseLabel), - SizedBox( - height: blockSizeVertical * 1, - ), - // REQUEST SOS - SizedBox( - height: screenHeight * .07, - width: MediaQuery.of(context).size.width, - child: ElevatedButton.icon( - icon: const Icon( - FontAwesome5.life_ring, - color: Colors.white, - ), - style: mainBtnStyle(third, - Colors.transparent, Colors.white38), - onPressed: () { - context.goNamed('add-mobile'); - }, - label: const Text( - requestSOS, - style: TextStyle(color: Colors.white), - )), - ) - ], + ), ), ), - ), + ], ), - ], - ), - ), + ), + ); + }); + } + if (state is UserError) { + return ErrorState( + message: state.message, + ); + } + if (state is SplashScreen) { + return const UniTSplashScreen(); + } + return Center( + child: Text("Default"), ); }), ), diff --git a/unit2/lib/sevices/login_service/auth_service.dart b/unit2/lib/sevices/login_service/auth_service.dart new file mode 100644 index 0000000..487631c --- /dev/null +++ b/unit2/lib/sevices/login_service/auth_service.dart @@ -0,0 +1,39 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:unit2/model/login_data/version_info.dart'; +import 'package:http/http.dart' as http; +import '../../utils/text_container.dart'; +import '../../utils/urls.dart'; + +class AuthService { + static final AuthService _instance = AuthService(); + static AuthService get instance => _instance; + + Future getVersionInfo() async { + VersionInfo versionInfo = VersionInfo(); + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + HttpHeaders.authorizationHeader: 'UniT2', + 'X-User': "" + }; + try { + http.Response response = await http.get( + Uri.https('unitylb1.agusandelnorte.gov.ph', + '/unit2/api/sys/apk_version/latest/'), + headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + versionInfo = VersionInfo.fromJson(data['data']); + } + } on TimeoutException catch (e) { + throw (timeoutError); + } on SocketException catch (e) { + throw (timeoutError); + } catch (e) { + throw (e.toString()); + } + return versionInfo; + } +} diff --git a/unit2/lib/utils/router.dart b/unit2/lib/utils/router.dart index 97d210b..4d00ecb 100644 --- a/unit2/lib/utils/router.dart +++ b/unit2/lib/utils/router.dart @@ -1,4 +1,7 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; +import 'package:unit2/bloc/bloc/user_bloc.dart'; import 'package:unit2/screens/unit2/login/register.dart'; import 'package:unit2/screens/unit2/roles/qr_code_scanner.dart/scan.dart'; import 'package:unit2/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart'; @@ -17,7 +20,11 @@ final GoRouter goRouter = GoRouter(routes: [ GoRoute( path: '/', name: 'login', - builder: (context, state) => UniT2Login(), + builder: (context, state) { + debugPrint("login bloc called"); + BlocProvider.of(context).add(GetApkVersion()); + return const UniT2Login(); + }, routes: [ GoRoute( name: 'register', diff --git a/unit2/lib/utils/urls.dart b/unit2/lib/utils/urls.dart index 77defa3..2274a80 100644 --- a/unit2/lib/utils/urls.dart +++ b/unit2/lib/utils/urls.dart @@ -1,8 +1,7 @@ class Url{ static final Url _instance = Url(); - static Url get instance => instance; + static Url get instance => _instance; - String host(){ return '192.168.10.219:3000'; } @@ -10,4 +9,9 @@ class Url{ String authentication(){ return '/api/account/auth/login'; } + + String apkUrl(){ + return ""; + } + } \ No newline at end of file diff --git a/unit2/lib/widgets/error_state.dart b/unit2/lib/widgets/error_state.dart new file mode 100644 index 0000000..ede9ef3 --- /dev/null +++ b/unit2/lib/widgets/error_state.dart @@ -0,0 +1,13 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/src/widgets/container.dart'; +import 'package:flutter/src/widgets/framework.dart'; + +class ErrorState extends StatelessWidget { + final String? message; + const ErrorState({super.key,this.message}); + + @override + Widget build(BuildContext context) { + return Center(child: Text(message!)); + } +} \ No newline at end of file diff --git a/unit2/lib/widgets/splash_screen.dart b/unit2/lib/widgets/splash_screen.dart new file mode 100644 index 0000000..026a977 --- /dev/null +++ b/unit2/lib/widgets/splash_screen.dart @@ -0,0 +1,12 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/src/widgets/container.dart'; +import 'package:flutter/src/widgets/framework.dart'; + +class UniTSplashScreen extends StatelessWidget { + const UniTSplashScreen({super.key}); + + @override + Widget build(BuildContext context) { + return const Center(child: Text("Splash Screen"),); + } +} \ No newline at end of file diff --git a/unit2/pubspec.lock b/unit2/pubspec.lock index fa07d08..5ec7dd3 100644 --- a/unit2/pubspec.lock +++ b/unit2/pubspec.lock @@ -50,6 +50,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.2.3" + bloc: + dependency: transitive + description: + name: bloc + url: "https://pub.dartlang.org" + source: hosted + version: "8.1.0" boolean_selector: dependency: transitive description: @@ -148,6 +155,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.0" + equatable: + dependency: "direct main" + description: + name: equatable + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5" fake_async: dependency: transitive description: @@ -188,6 +202,13 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_bloc: + dependency: "direct main" + description: + name: flutter_bloc + url: "https://pub.dartlang.org" + source: hosted + version: "8.1.1" flutter_blurhash: dependency: transitive description: diff --git a/unit2/pubspec.yaml b/unit2/pubspec.yaml index 19b17d2..7257490 100644 --- a/unit2/pubspec.yaml +++ b/unit2/pubspec.yaml @@ -60,6 +60,8 @@ dependencies: signature: ^5.3.0 awesome_dialog: ^3.0.2 system_info2: ^2.0.4 + flutter_bloc: ^8.1.1 + equatable: ^2.0.5 dev_dependencies: flutter_test: From 0c0c6c1c909cb7a3bbb28848f852bfea1f14748b Mon Sep 17 00:00:00 2001 From: rodolfobacuinjr Date: Thu, 19 Jan 2023 13:21:12 +0800 Subject: [PATCH 11/86] Successfully integrated login api with userbloc --- unit2/lib/bloc/bloc/user_bloc.dart | 11 +++ unit2/lib/bloc/bloc/user_event.dart | 6 ++ unit2/lib/bloc/bloc/user_state.dart | 4 + .../login_data/employee_info/department.dart | 2 +- .../employee_info/employee_info.dart | 6 +- .../login_data/employee_info/office.dart | 12 +-- .../employee_info/position_class.dart | 2 +- .../user_info/{modue.dart => module.dart} | 0 .../lib/model/login_data/user_info/role.dart | 2 +- .../login/components/update_required.dart | 98 +++++++++++++++++++ unit2/lib/screens/unit2/login/login.dart | 46 +++++---- .../sevices/login_service/auth_service.dart | 26 ++++- unit2/lib/utils/request.dart | 65 ++++++------ unit2/lib/utils/urls.dart | 2 +- unit2/lib/widgets/splash_screen.dart | 40 +++++++- 15 files changed, 256 insertions(+), 66 deletions(-) rename unit2/lib/model/login_data/user_info/{modue.dart => module.dart} (100%) create mode 100644 unit2/lib/screens/unit2/login/components/update_required.dart diff --git a/unit2/lib/bloc/bloc/user_bloc.dart b/unit2/lib/bloc/bloc/user_bloc.dart index 1d10614..34b651b 100644 --- a/unit2/lib/bloc/bloc/user_bloc.dart +++ b/unit2/lib/bloc/bloc/user_bloc.dart @@ -1,6 +1,7 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; +import 'package:unit2/model/login_data/user_info/user_data.dart'; import 'package:unit2/model/login_data/version_info.dart'; import 'package:unit2/sevices/login_service/auth_service.dart'; import 'package:unit2/widgets/error_state.dart'; @@ -10,6 +11,8 @@ part 'user_state.dart'; class UserBloc extends Bloc { UserBloc() : super(UserInitial()) { + // this event is called when opening the app to check if + // there is new app version on((event, emit) async { debugPrint("getApkEvent is called"); try { @@ -21,6 +24,14 @@ class UserBloc extends Bloc { message: e.toString(), )); } + on((event, emit) async{ + try{ + UserData? userData = await AuthService.instance.webLogin(username: event.username,password: event.password); + emit(UserLoggedIn(userData: userData)); + }catch(e){ + emit(UserError(message: e.toString())); + } + }); }); } } diff --git a/unit2/lib/bloc/bloc/user_event.dart b/unit2/lib/bloc/bloc/user_event.dart index 1094d9f..85a66d2 100644 --- a/unit2/lib/bloc/bloc/user_event.dart +++ b/unit2/lib/bloc/bloc/user_event.dart @@ -11,3 +11,9 @@ class GetApkVersion extends UserEvent{ @override List get props => []; } + +class UserLogin extends UserEvent{ + final String? username; + final String? password; + const UserLogin({this.username,this.password}); +} diff --git a/unit2/lib/bloc/bloc/user_state.dart b/unit2/lib/bloc/bloc/user_state.dart index c9b4833..608f899 100644 --- a/unit2/lib/bloc/bloc/user_state.dart +++ b/unit2/lib/bloc/bloc/user_state.dart @@ -27,6 +27,10 @@ class UserError extends UserState { @override List get props => []; } +class UserLoggedIn extends UserState{ + final UserData? userData; + const UserLoggedIn({this.userData}); +} class VersionLoaded extends UserState { final VersionInfo? versionInfo; diff --git a/unit2/lib/model/login_data/employee_info/department.dart b/unit2/lib/model/login_data/employee_info/department.dart index c6d6d78..59755b6 100644 --- a/unit2/lib/model/login_data/employee_info/department.dart +++ b/unit2/lib/model/login_data/employee_info/department.dart @@ -22,7 +22,7 @@ class Department { factory Department.fromJson(Map json) => Department( id: json["id"], code: json["code"], - head: json["head"], + head:json["head"]==null?null:Head.fromJson(json["head"]), name: json["name"], acronym: json["acronym"], parentStationId: json["parent_station_id"], diff --git a/unit2/lib/model/login_data/employee_info/employee_info.dart b/unit2/lib/model/login_data/employee_info/employee_info.dart index 5655fed..da8b94a 100644 --- a/unit2/lib/model/login_data/employee_info/employee_info.dart +++ b/unit2/lib/model/login_data/employee_info/employee_info.dart @@ -22,8 +22,8 @@ class EmployeeInfo { empid: json["empid"], classid: json["classid"], uuid: json["uuid"], - office: Office.fromJson(json["office"]), - profile: Profile.fromJson(json["profile"]), + office: json["office"]==null?null:Office.fromJson(json["office"]), + profile: json["profile"]==null?null:Profile.fromJson(json["profile"]), ); Map toJson() => { @@ -63,7 +63,7 @@ class Profile { int? id; String? sex; - dynamic gender; + String? gender; bool? deceased; double? heightM; DateTime? birthdate; diff --git a/unit2/lib/model/login_data/employee_info/office.dart b/unit2/lib/model/login_data/employee_info/office.dart index bd06e2e..7d31f52 100644 --- a/unit2/lib/model/login_data/employee_info/office.dart +++ b/unit2/lib/model/login_data/employee_info/office.dart @@ -23,14 +23,14 @@ class Office { PositionSpecificrole? positionSpecificrole; factory Office.fromJson(Map json) => Office( - unit: Department.fromJson(json["unit"]), - section: Department.fromJson(json["section"]), - division: Department.fromJson(json["division"]), + unit: json["unit"]==null?null:Department.fromJson(json["unit"]), + section: json["section"]==null?null:Department.fromJson(json["section"]), + division: json["division"] ==null? null:Department.fromJson(json["division"]), posstatid: json["posstatid"], - department: Department.fromJson(json["department"]), + department: json["department"]==null?null:Department.fromJson(json["department"]), stationId: json["station_id"], - positionClass: PositionClass.fromJson(json["position_class"]), - positionSpecificrole: + positionClass: json["position_class"]==null?null:PositionClass.fromJson(json["position_class"]), + positionSpecificrole:json["position_specificrole"]==null?null: PositionSpecificrole.fromJson(json["position_specificrole"]), ); diff --git a/unit2/lib/model/login_data/employee_info/position_class.dart b/unit2/lib/model/login_data/employee_info/position_class.dart index 878847f..be7bff8 100644 --- a/unit2/lib/model/login_data/employee_info/position_class.dart +++ b/unit2/lib/model/login_data/employee_info/position_class.dart @@ -25,7 +25,7 @@ class PositionClass { groupcode: json["groupcode"], salarygrade: json["salarygrade"], classSuffix: json["class_suffix"], - classTitleid: PositionSpecificrole.fromJson(json["class_titleid"]), + classTitleid:json["class_titleid"]==null?null: PositionSpecificrole.fromJson(json["class_titleid"]), qualificationid: json["qualificationid"], competencyModelId: json["competency_model_id"], ); diff --git a/unit2/lib/model/login_data/user_info/modue.dart b/unit2/lib/model/login_data/user_info/module.dart similarity index 100% rename from unit2/lib/model/login_data/user_info/modue.dart rename to unit2/lib/model/login_data/user_info/module.dart diff --git a/unit2/lib/model/login_data/user_info/role.dart b/unit2/lib/model/login_data/user_info/role.dart index 5975d6d..ef371f7 100644 --- a/unit2/lib/model/login_data/user_info/role.dart +++ b/unit2/lib/model/login_data/user_info/role.dart @@ -1,5 +1,5 @@ import 'assigned_area.dart'; -import 'modue.dart'; +import 'module.dart'; class Role { Role({ diff --git a/unit2/lib/screens/unit2/login/components/update_required.dart b/unit2/lib/screens/unit2/login/components/update_required.dart new file mode 100644 index 0000000..9a0c4c4 --- /dev/null +++ b/unit2/lib/screens/unit2/login/components/update_required.dart @@ -0,0 +1,98 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/container.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:unit2/theme-data.dart/btn-style.dart'; + +import '../../../../theme-data.dart/colors.dart'; + +class Update extends StatefulWidget { + const Update({super.key}); + + @override + State createState() => _UpdateState(); +} + +class _UpdateState extends State { + @override + Widget build(BuildContext context) { + return SafeArea( + child: SingleChildScrollView( + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 25), + height: MediaQuery.of(context).size.height, + alignment: Alignment.center, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SvgPicture.asset( + 'assets/svgs/download.svg', + height: 200.0, + width: 200.0, + allowDrawingOutsideViewBox: true, + ), + const SizedBox( + height: 5, + ), + Text("UPDATE REQUIRED!", + textAlign: TextAlign.center, + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith(color: primary, fontSize: 28)), + ], + ), + const SizedBox( + height: 10, + ), + RichText( + textAlign: TextAlign.justify, + text: TextSpan( + text: 'Your app version ', + style: const TextStyle( + fontSize: 16, + color: Colors.black, + ), + children: [ + TextSpan( + text: "mobileVersion", + style: const TextStyle( + color: primary, fontWeight: FontWeight.bold)), + const TextSpan( + text: " did not match with the latest version ", + style: TextStyle(color: Colors.black)), + TextSpan( + text: + "widget.currentVersion.data.version".toString(), + style: const TextStyle( + color: primary, fontWeight: FontWeight.bold)), + const TextSpan( + text: + ". Download the app latest version to procceed using the uniT-App.", + style: TextStyle(color: Colors.black)), + ])), + const SizedBox( + height: 12.0, + ), + SizedBox( + height: 60, + width: MediaQuery.of(context).size.width, + child: ElevatedButton.icon( + icon: const Icon(Icons.download), + style: mainBtnStyle(primary, Colors.transparent, second), + onPressed: () async {}, + label: const Text("Download Latest App Version.")), + ), + ], + ), + ), + ), + ); + } +} diff --git a/unit2/lib/screens/unit2/login/login.dart b/unit2/lib/screens/unit2/login/login.dart index 384a63e..e527582 100644 --- a/unit2/lib/screens/unit2/login/login.dart +++ b/unit2/lib/screens/unit2/login/login.dart @@ -9,8 +9,6 @@ import 'package:go_router/go_router.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:unit2/bloc/bloc/user_bloc.dart'; -import 'package:unit2/utils/alerts.dart'; -import 'package:unit2/utils/scanner.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/error_state.dart'; @@ -41,11 +39,10 @@ class _UniT2LoginState extends State { child: Scaffold( body: ProgressHUD( child: BlocConsumer(listener: (context, state) { - if (state is UserLoading) { + if (state is UserLoggedIn) { final progress = ProgressHUD.of(context); - progress?.showWithText( - 'Logging in...', - ); + progress!.dismiss(); + debugPrint(state.userData!.user!.login!.user!.firstName); } }, builder: (context, state) { if (state is VersionLoaded) { @@ -188,17 +185,32 @@ class _UniT2LoginState extends State { style: TextStyle(color: Colors.white), ), onPressed: () { - FocusScope.of(context).unfocus(); - // Future.delayed( - // const Duration(seconds: 5), () { - // progress!.dismiss(); + final progress = + ProgressHUD.of(context); - // context.goNamed("home"); - // }); - // if (_formKey.currentState! - // .saveAndValidate()) { - // context.go(context.namedLocation('home')); - // } + FocusScope.of(context).unfocus(); + + if (_formKey.currentState! + .saveAndValidate()) { + progress?.showWithText( + 'Logging in...', + ); + + BlocProvider.of(context) + .add(UserLogin( + username: + "rjvincentlopeplopez", + password: "shesthequ33n" + // username: _formKey + // .currentState! + // .value['username'], + // password: _formKey + // .currentState! + // .value[ + // 'password'] + + )); + } // if (_formKey.currentState.validate()) { // _formKey.currentState.save(); @@ -290,7 +302,7 @@ class _UniT2LoginState extends State { if (state is SplashScreen) { return const UniTSplashScreen(); } - return Center( + return const Center( child: Text("Default"), ); }), diff --git a/unit2/lib/sevices/login_service/auth_service.dart b/unit2/lib/sevices/login_service/auth_service.dart index 487631c..c226746 100644 --- a/unit2/lib/sevices/login_service/auth_service.dart +++ b/unit2/lib/sevices/login_service/auth_service.dart @@ -2,8 +2,11 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:unit2/model/login_data/user_info/user_data.dart'; import 'package:unit2/model/login_data/version_info.dart'; import 'package:http/http.dart' as http; +import 'package:unit2/utils/request.dart'; import '../../utils/text_container.dart'; import '../../utils/urls.dart'; @@ -27,13 +30,32 @@ class AuthService { Map data = jsonDecode(response.body); versionInfo = VersionInfo.fromJson(data['data']); } - } on TimeoutException catch (e) { + } on TimeoutException catch (_) { throw (timeoutError); - } on SocketException catch (e) { + } on SocketException catch (_) { throw (timeoutError); } catch (e) { throw (e.toString()); } return versionInfo; } + + Future webLogin({String? username, String? password})async{ + Map body ={'username':username!,'password':password!}; + Map baseHeaders = { + 'Content-Type': 'application/json' + }; + String path = Url.instance.authentication(); + UserData? userData; + try{ + http.Response response = await Request.instance.postRequest(path: path,param: {},headers: baseHeaders,body: body); + if(response.statusCode == 200){ + Map data = jsonDecode(response.body); + userData = UserData.fromJson(data['data']); + } + }catch(e){ + throw (e.toString()); + } + return userData!; + } } diff --git a/unit2/lib/utils/request.dart b/unit2/lib/utils/request.dart index ef472e6..b956687 100644 --- a/unit2/lib/utils/request.dart +++ b/unit2/lib/utils/request.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; @@ -59,39 +60,39 @@ class Request { Map? body, Map? param}) async { Response response; - try { - response = await post(Uri.http(host, path!, param), headers: headers) + // try { + response = await post(Uri.http(host, path!, param), headers: headers,body: jsonEncode(body)) .timeout(Duration(seconds: requestTimeout)); - } on TimeoutException catch (_) { - Fluttertoast.showToast( - msg: timeoutError, - toastLength: Toast.LENGTH_LONG, - gravity: ToastGravity.BOTTOM, - backgroundColor: Colors.black, - ); - throw (timeoutError); - } on SocketException catch (_) { - Fluttertoast.showToast( - msg: timeoutError, - toastLength: Toast.LENGTH_LONG, - gravity: ToastGravity.BOTTOM, - backgroundColor: Colors.black, - ); - throw (timeoutError); - } on FormatException catch (_) { - throw const FormatException(formatError); - } on HttpException catch (_) { - throw const HttpException(httpError); - } on Error catch (e) { - debugPrint("post request error: $e"); - Fluttertoast.showToast( - msg: onError, - toastLength: Toast.LENGTH_LONG, - gravity: ToastGravity.BOTTOM, - backgroundColor: Colors.black, - ); - throw (onError); - } + // } on TimeoutException catch (_) { + // Fluttertoast.showToast( + // msg: timeoutError, + // toastLength: Toast.LENGTH_LONG, + // gravity: ToastGravity.BOTTOM, + // backgroundColor: Colors.black, + // ); + // throw (timeoutError); + // } on SocketException catch (_) { + // Fluttertoast.showToast( + // msg: timeoutError, + // toastLength: Toast.LENGTH_LONG, + // gravity: ToastGravity.BOTTOM, + // backgroundColor: Colors.black, + // ); + // throw (timeoutError); + // } on FormatException catch (_) { + // throw const FormatException(formatError); + // } on HttpException catch (_) { + // throw const HttpException(httpError); + // } on Error catch (e) { + // debugPrint("post request error: $e"); + // Fluttertoast.showToast( + // msg: onError, + // toastLength: Toast.LENGTH_LONG, + // gravity: ToastGravity.BOTTOM, + // backgroundColor: Colors.black, + // ); + // throw (e.toString()); + // } return response; } } diff --git a/unit2/lib/utils/urls.dart b/unit2/lib/utils/urls.dart index 2274a80..f44cc4d 100644 --- a/unit2/lib/utils/urls.dart +++ b/unit2/lib/utils/urls.dart @@ -7,7 +7,7 @@ class Url{ } String authentication(){ - return '/api/account/auth/login'; + return '/api/account/auth/login/'; } String apkUrl(){ diff --git a/unit2/lib/widgets/splash_screen.dart b/unit2/lib/widgets/splash_screen.dart index 026a977..ceb6ba3 100644 --- a/unit2/lib/widgets/splash_screen.dart +++ b/unit2/lib/widgets/splash_screen.dart @@ -1,12 +1,48 @@ +import 'package:animate_do/animate_do.dart'; import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; import 'package:flutter/src/widgets/container.dart'; import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/global.dart'; class UniTSplashScreen extends StatelessWidget { const UniTSplashScreen({super.key}); @override Widget build(BuildContext context) { - return const Center(child: Text("Splash Screen"),); + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SlideInUp( + from: 50, + duration: const Duration(milliseconds: 300), + child: SvgPicture.asset( + 'assets/svgs/logo.svg', + height: blockSizeVertical * 15.0, + allowDrawingOutsideViewBox: true, + color: primary, + ), + ), + const SizedBox( + height: 24, + ), + SlideInDown( + from: 100, + duration: const Duration(milliseconds: 200), + child: Text("uniT-App", + style: TextStyle( + fontSize: blockSizeVertical * 5, + fontWeight: FontWeight.w800, + letterSpacing: .2, + height: 1, + color: Colors.black)), + ), + ], + ), + ); } -} \ No newline at end of file +} From c088dbc2a2748f30a320c662d0a2115bdb3dc15c Mon Sep 17 00:00:00 2001 From: rodolfobacuinjr Date: Mon, 23 Jan 2023 11:02:59 +0800 Subject: [PATCH 12/86] with go router navigation --- unit2/lib/bloc/bloc/user_bloc.dart | 32 ++- unit2/lib/bloc/bloc/user_event.dart | 15 +- unit2/lib/bloc/bloc/user_state.dart | 21 +- unit2/lib/main.dart | 14 +- .../homepage.dart/components/dashboard.dart | 81 ++++++ .../components/drawer-screen.dart | 38 ++- .../homepage.dart/components/menu-screen.dart | 36 +-- .../unit2/homepage.dart/components/menu.dart | 5 +- .../unit2/homepage.dart/module-screen.dart | 121 +++++++-- unit2/lib/screens/unit2/login/login.dart | 56 ++-- unit2/lib/screens/unit2/login/qr_login.dart | 239 ++++++++++++++++++ unit2/lib/screens/unit2/login/register.dart | 213 ---------------- unit2/lib/screens/unit2/profile/profile.dart | 158 +++++++----- unit2/lib/test_data.dart | 21 +- unit2/lib/utils/app_router.dart | 14 + unit2/lib/utils/global_context.dart | 6 + unit2/lib/utils/router.dart | 36 ++- unit2/lib/utils/text_container.dart | 2 +- unit2/lib/widgets/splash_screen.dart | 62 ++--- unit2/pubspec.yaml | 2 +- 20 files changed, 745 insertions(+), 427 deletions(-) create mode 100644 unit2/lib/screens/unit2/homepage.dart/components/dashboard.dart create mode 100644 unit2/lib/screens/unit2/login/qr_login.dart delete mode 100644 unit2/lib/screens/unit2/login/register.dart create mode 100644 unit2/lib/utils/app_router.dart create mode 100644 unit2/lib/utils/global_context.dart diff --git a/unit2/lib/bloc/bloc/user_bloc.dart b/unit2/lib/bloc/bloc/user_bloc.dart index 34b651b..2a8c144 100644 --- a/unit2/lib/bloc/bloc/user_bloc.dart +++ b/unit2/lib/bloc/bloc/user_bloc.dart @@ -1,3 +1,4 @@ +import 'package:barcode_scan2/barcode_scan2.dart'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; @@ -6,15 +7,17 @@ import 'package:unit2/model/login_data/version_info.dart'; import 'package:unit2/sevices/login_service/auth_service.dart'; import 'package:unit2/widgets/error_state.dart'; +import '../../utils/scanner.dart'; + part 'user_event.dart'; part 'user_state.dart'; class UserBloc extends Bloc { + UserData? _userData; UserBloc() : super(UserInitial()) { // this event is called when opening the app to check if // there is new app version on((event, emit) async { - debugPrint("getApkEvent is called"); try { emit(SplashScreen()); VersionInfo versionInfo = await AuthService.instance.getVersionInfo(); @@ -24,14 +27,25 @@ class UserBloc extends Bloc { message: e.toString(), )); } - on((event, emit) async{ - try{ - UserData? userData = await AuthService.instance.webLogin(username: event.username,password: event.password); - emit(UserLoggedIn(userData: userData)); - }catch(e){ - emit(UserError(message: e.toString())); - } - }); + }); + on((event, emit) async { + try { + UserData? userData = await AuthService.instance + .webLogin(username: event.username, password: event.password); + _userData = userData; + emit(UserLoggedIn(userData: userData)); + } catch (e) { + emit(UserError(message: e.toString())); + } + }); + on((event, emit) { + emit(UserLoggedIn(userData: event.userData)); + }); + on((event, emit) async { + ScanResult result = await QRCodeBarCodeScanner.instance.scanner(); + if(result.rawContent.toString().isNotEmpty){ + emit(UuidLoaded(uuid: result.rawContent.toString())); + } }); } } diff --git a/unit2/lib/bloc/bloc/user_event.dart b/unit2/lib/bloc/bloc/user_event.dart index 85a66d2..019edbc 100644 --- a/unit2/lib/bloc/bloc/user_event.dart +++ b/unit2/lib/bloc/bloc/user_event.dart @@ -1,13 +1,12 @@ part of 'user_bloc.dart'; abstract class UserEvent extends Equatable { - const UserEvent(); - @override List get props => []; } class GetApkVersion extends UserEvent{ + GetApkVersion(); @override List get props => []; } @@ -15,5 +14,15 @@ class GetApkVersion extends UserEvent{ class UserLogin extends UserEvent{ final String? username; final String? password; - const UserLogin({this.username,this.password}); + UserLogin({this.username,this.password}); +} + +class LoadLoggedInUser extends UserEvent{ + final UserData? userData; + LoadLoggedInUser({this.userData}); +} + +class GetUuid extends UserEvent{ + + GetUuid(); } diff --git a/unit2/lib/bloc/bloc/user_state.dart b/unit2/lib/bloc/bloc/user_state.dart index 608f899..2c5b56b 100644 --- a/unit2/lib/bloc/bloc/user_state.dart +++ b/unit2/lib/bloc/bloc/user_state.dart @@ -1,17 +1,20 @@ part of 'user_bloc.dart'; abstract class UserState extends Equatable { - const UserState(); @override List get props => []; } -class UserInitial extends UserState {} +class UserInitial extends UserState { + UserInitial(); + @override + List get props => []; +} class UserLoading extends UserState { final String? message; - const UserLoading({this.message}); + UserLoading({this.message}); @override List get props => [message!]; } @@ -23,18 +26,24 @@ class SplashScreen extends UserState { class UserError extends UserState { final String? message; - const UserError({this.message}); + UserError({this.message}); @override List get props => []; } class UserLoggedIn extends UserState{ final UserData? userData; - const UserLoggedIn({this.userData}); + UserLoggedIn({this.userData}); } class VersionLoaded extends UserState { final VersionInfo? versionInfo; - const VersionLoaded({this.versionInfo}); + VersionLoaded({this.versionInfo}); @override List get props => [versionInfo!]; } +class UuidLoaded extends UserState{ + final String uuid; + UuidLoaded({required this.uuid}); + @override + List get props => [uuid]; +} diff --git a/unit2/lib/main.dart b/unit2/lib/main.dart index af6b45d..9ff8fb7 100644 --- a/unit2/lib/main.dart +++ b/unit2/lib/main.dart @@ -4,6 +4,8 @@ import 'package:device_preview/device_preview.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:unit2/bloc/bloc/user_bloc.dart'; +import 'package:unit2/utils/global_context.dart'; +import 'package:unit2/utils/global_context.dart'; import './utils/router.dart'; import './utils/global.dart'; @@ -24,6 +26,7 @@ class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { + BuildContext? parentContext = NavigationService.navigatorKey.currentContext; final mediaQueryData = MediaQueryData.fromWindow(WidgetsBinding.instance.window); screenWidth = mediaQueryData.size.width; @@ -36,10 +39,15 @@ class MyApp extends StatelessWidget { mediaQueryData.padding.top + mediaQueryData.padding.bottom; safeBlockHorizontal = (screenWidth - safeAreaHorizontal) / 100; safeBlockVertical = (screenHeight - safeAreaVertical) / 100; - - return BlocProvider( - create: (context) => UserBloc(), + + return MultiBlocProvider( + providers: [ + BlocProvider( + create: (parentContext) => UserBloc(), + ), + ], child: MaterialApp.router( + key: NavigationService.navigatorKey, // useInheritedMediaQuery: true, // locale: DevicePreview.locale(context), // builder: DevicePreview.appBuilder, diff --git a/unit2/lib/screens/unit2/homepage.dart/components/dashboard.dart b/unit2/lib/screens/unit2/homepage.dart/components/dashboard.dart new file mode 100644 index 0000000..c5c9ec9 --- /dev/null +++ b/unit2/lib/screens/unit2/homepage.dart/components/dashboard.dart @@ -0,0 +1,81 @@ +import 'package:flutter/material.dart'; +import 'package:unit2/screens/unit2/homepage.dart/module-screen.dart'; + +import 'package:unit2/test_data.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; + +class DashBoard extends StatelessWidget { + final List roles; + const DashBoard({super.key, required this.roles}); + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 24), + height: MediaQuery.of(context).size.height, + //listview builder + child: ListView.builder( + scrollDirection: Axis.vertical, + shrinkWrap: true, + itemCount: roles.length, + itemBuilder: (BuildContext context, int index) { + // gridview.count + return SizedBox( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + roles[index].name, + style: Theme.of(context) + .textTheme + .labelLarge! + .copyWith(fontSize: 16, fontWeight: FontWeight.w700), + ), + const Divider(), + const SizedBox( + height: 8, + ), + GridView.count( + shrinkWrap: true, + crossAxisCount: 4, + crossAxisSpacing: 5, + mainAxisSpacing: 2, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.symmetric( + vertical: 5, horizontal: 5), + children: roles[index].roles.map((role) { + return GestureDetector( + onTap: () { + print("Role is click"); + }, + child: Column(children: [ + Icon( + role.icon, + size: 24, + color: second, + ), + const SizedBox( + height: 5, + ), + Expanded( + child: Text( + role.role.name!, + textAlign: TextAlign.center, + style: Theme.of(context) + .textTheme + .button! + .copyWith( + fontSize: 11, + fontWeight: FontWeight.bold), + ), + ), + ]), + ); + }).toList()), + ], + ), + ); + }), + ); + } +} diff --git a/unit2/lib/screens/unit2/homepage.dart/components/drawer-screen.dart b/unit2/lib/screens/unit2/homepage.dart/components/drawer-screen.dart index 4ffd092..78d1014 100644 --- a/unit2/lib/screens/unit2/homepage.dart/components/drawer-screen.dart +++ b/unit2/lib/screens/unit2/homepage.dart/components/drawer-screen.dart @@ -1,7 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_zoom_drawer/config.dart'; import 'package:flutter_zoom_drawer/flutter_zoom_drawer.dart'; +import 'package:unit2/bloc/bloc/user_bloc.dart'; import 'package:unit2/theme-data.dart/colors.dart'; +import '../../../../widgets/splash_screen.dart'; import 'menu-screen.dart'; import '../module-screen.dart'; @@ -15,18 +18,29 @@ class _DrawerScreenState extends State { final zoomDrawerController = ZoomDrawerController(); @override Widget build(BuildContext context) { - return ZoomDrawer( - controller: zoomDrawerController, - menuScreen: const MenuScreen(), - mainScreen: const MainScreen(), - style: DrawerStyle.defaultStyle, - borderRadius: 24.0, - showShadow: false, - angle: -0.0, - slideWidth: MediaQuery.of(context).size.width * .90, - openCurve: Curves.fastOutSlowIn, - closeCurve: Curves.easeOut, - menuBackgroundColor: Colors.grey, + return BlocBuilder( + builder: (context, state) { + print("drawer screen"+state.toString()); + if(state is UserLoggedIn){ + return ZoomDrawer( + controller: zoomDrawerController, + menuScreen: MenuScreen(userData: state.userData,), + mainScreen: SizedBox( + height: MediaQuery.of(context).size.height, + child: const MainScreen()), + style: DrawerStyle.defaultStyle, + borderRadius: 24.0, + showShadow: false, + angle: -0.0, + slideWidth: MediaQuery.of(context).size.width * .90, + openCurve: Curves.fastOutSlowIn, + closeCurve: Curves.easeOut, + menuBackgroundColor: Colors.grey, + ); + } + return const UniTSplashScreen(); + + }, ); } } diff --git a/unit2/lib/screens/unit2/homepage.dart/components/menu-screen.dart b/unit2/lib/screens/unit2/homepage.dart/components/menu-screen.dart index e9f722a..1145405 100644 --- a/unit2/lib/screens/unit2/homepage.dart/components/menu-screen.dart +++ b/unit2/lib/screens/unit2/homepage.dart/components/menu-screen.dart @@ -3,11 +3,13 @@ import 'package:unit2/theme-data.dart/colors.dart'; import 'package:fluttericon/web_symbols_icons.dart'; import 'package:fluttericon/typicons_icons.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; +import '../../../../model/login_data/user_info/user_data.dart'; import 'menu.dart'; import '../../../../utils/global.dart'; class MenuScreen extends StatefulWidget { - const MenuScreen({Key? key}) : super(key: key); + final UserData? userData; + const MenuScreen({Key? key, required this.userData}) : super(key: key); @override State createState() => _MenuScreenState(); @@ -16,6 +18,10 @@ class MenuScreen extends StatefulWidget { class _MenuScreenState extends State { @override Widget build(BuildContext context) { + final String firstName = + widget.userData!.user!.login!.user!.firstName!.toUpperCase(); + final String lastname = + widget.userData!.user!.login!.user!.lastName!.toUpperCase(); return SafeArea( child: Drawer( child: SingleChildScrollView( @@ -24,13 +30,13 @@ class _MenuScreenState extends State { child: Column( // ignore: prefer_const_literals_to_create_immutables children: [ - const UserAccountsDrawerHeader( - decoration: BoxDecoration( + UserAccountsDrawerHeader( + decoration: const BoxDecoration( color: primary, image: DecorationImage( image: AssetImage('assets/pngs/bg.png'), fit: BoxFit.cover)), - accountName: Text("ACUIN" ", " "RODOLFO" " " "BERNALIS"), + accountName: Text("$firstName $lastname"), accountEmail: null, currentAccountPicture: CircleAvatar( radius: 40, @@ -40,30 +46,26 @@ class _MenuScreenState extends State { backgroundColor: third, child: //Icon(Icons.person, size: 40, color: fifth), Text( - "A", - style: TextStyle(fontSize: 45.0, color: fifth), + firstName[0].toUpperCase(), + style: const TextStyle(fontSize: 45.0, color: fifth), ), ), ), ), - getTile(Typicons.user_outline, "Profile", 'profile', context), + getTile(FontAwesome5.user, "Basic Info", 'profile', context, + widget.userData!), const Divider(), - getTile(Typicons.home_outline, "Address", '/SelfAddressScreen', - context), - const Divider(), - getTile(Typicons.contacts, "Contact Number", - '/SelfContactScreen', context), - const Divider(), - getTile(Typicons.mail, "Email Address", '/SelfEmailAddScreen', - context), + getTile(FontAwesome5.user_circle, "Profile", + '/SelfAddressScreen', context, widget.userData!), const Divider(), getTile(FontAwesome5.life_ring, "Request SOS", 'request-sos', - context), + context, widget.userData!), const Divider(), Expanded( child: Align( alignment: FractionalOffset.bottomLeft, - child: getTile(WebSymbols.logout, "Logout", 'login', context), + child: getTile(WebSymbols.logout, "Logout", 'login', context, + widget.userData!), )), ], ), diff --git a/unit2/lib/screens/unit2/homepage.dart/components/menu.dart b/unit2/lib/screens/unit2/homepage.dart/components/menu.dart index cdfbd44..7c2544d 100644 --- a/unit2/lib/screens/unit2/homepage.dart/components/menu.dart +++ b/unit2/lib/screens/unit2/homepage.dart/components/menu.dart @@ -1,11 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_zoom_drawer/flutter_zoom_drawer.dart'; import 'package:go_router/go_router.dart'; +import 'package:unit2/model/login_data/user_info/user_data.dart'; import 'package:unit2/utils/alerts.dart'; import '../../../../theme-data.dart/colors.dart'; Widget getTile( - IconData icondata, String title, String route, BuildContext context) { + IconData icondata, String title, String route, BuildContext context,UserData userData) { return ListTile( dense: true, leading: Icon( @@ -22,7 +23,7 @@ Widget getTile( context.goNamed("login"); }); } else { - context.goNamed(route); + context.goNamed(route,extra: userData); } }, ); diff --git a/unit2/lib/screens/unit2/homepage.dart/module-screen.dart b/unit2/lib/screens/unit2/homepage.dart/module-screen.dart index 96b7a99..f741589 100644 --- a/unit2/lib/screens/unit2/homepage.dart/module-screen.dart +++ b/unit2/lib/screens/unit2/homepage.dart/module-screen.dart @@ -1,8 +1,16 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_zoom_drawer/flutter_zoom_drawer.dart'; +import 'package:fluttericon/font_awesome5_icons.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:fluttericon/typicons_icons.dart'; +import 'package:unit2/screens/unit2/homepage.dart/components/dashboard.dart'; +import 'package:unit2/test_data.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; +import '../../../bloc/bloc/user_bloc.dart'; +import '../../../model/login_data/user_info/role.dart'; import 'components/empty_module.dart'; class MainScreen extends StatefulWidget { @@ -13,41 +21,98 @@ class MainScreen extends StatefulWidget { } class _MainScreenState extends State { - bool hasModule = false; + List roles = [ + Module(name: 'UniT2 roles', roles: []), + Module(name: 'DocSms roles', roles: []) + ]; @override Widget build(BuildContext context) { return WillPopScope( onWillPop: () async { return Future.value(true); }, - child: SafeArea( - child: Scaffold( - appBar: AppBar( - backgroundColor: primary, - leading: IconButton( - onPressed: () { - ZoomDrawer.of(context)!.toggle(); - }, - icon: const Icon( - Icons.menu, - color: Colors.white, + child: BlocBuilder(builder: (context, state) { + if (state is UserLoggedIn) { + state.userData!.user!.login!.user!.roles!.forEach((role) { + Role? getRole = role; + role!.modules!.forEach((module) { + if (module!.name!.toLowerCase() == 'unit2') { + IconData iconData = iconGenerator(getRole!.name!); + Roles newRole = Roles(role: getRole, icon: iconData); + roles[0].roles.add(newRole); + } + if (module.name!.toLowerCase() == 'document management') { + IconData iconData = iconGenerator(getRole!.name!); + Roles newRole = Roles(role: getRole, icon: iconData); + roles[1].roles.add(newRole); + } + }); + }); + return SafeArea( + child: Scaffold( + appBar: AppBar( + backgroundColor: primary, + leading: IconButton( + onPressed: () { + ZoomDrawer.of(context)!.toggle(); + }, + icon: const Icon( + Icons.menu, + color: Colors.white, + ), + ), + centerTitle: true, + title: const Text( + unit2ModuleScreen, + style: TextStyle( + fontSize: 18.0, + color: Colors.white, + ), + ), ), - ), - centerTitle: true, - title: const Text( - unit2ModuleScreen, - style: TextStyle( - fontSize: 18.0, - color: Colors.white, - ), - ), - ), - body: hasModule - ? const Center( - child: Text('Main Screen'), - ) - : const NoModule(), - )), + body: state.userData!.user!.login!.user!.roles!.isNotEmpty + ? DashBoard( + roles: roles, + ) + : const NoModule(), + )); + } + return Container(); + }), ); } + + IconData iconGenerator(String roleName) { + IconData? iconData; + switch (roleName.toLowerCase()) { + case 'qr code scanner': + iconData = FontAwesome.qrcode; + break; + case 'security guard': + iconData = FontAwesome5.user_shield; + break; + case 'establishment point-person': + iconData = FontAwesome.building_filled; + break; + case 'registration in-charge': + iconData = FontAwesome.user_plus; + break; + case 'process server': + iconData = Typicons.doc_text; + break; + } + return iconData!; + } +} + +class Module { + final String name; + final List roles; + Module({required this.name, required this.roles}); +} + +class Roles { + final IconData icon; + final Role role; + Roles({required this.role, required this.icon}); } diff --git a/unit2/lib/screens/unit2/login/login.dart b/unit2/lib/screens/unit2/login/login.dart index e527582..b1ff3ec 100644 --- a/unit2/lib/screens/unit2/login/login.dart +++ b/unit2/lib/screens/unit2/login/login.dart @@ -4,14 +4,18 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; +import 'package:fluttertoast/fluttertoast.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:go_router/go_router.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:unit2/bloc/bloc/user_bloc.dart'; +import 'package:unit2/model/login_data/user_info/user_data.dart'; +import 'package:unit2/screens/unit2/login/qr_login.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/error_state.dart'; +import '../../../utils/scanner.dart'; import '../../../widgets/splash_screen.dart'; import '../../../widgets/wave.dart'; import '../../../utils/global.dart'; @@ -40,9 +44,15 @@ class _UniT2LoginState extends State { body: ProgressHUD( child: BlocConsumer(listener: (context, state) { if (state is UserLoggedIn) { + print("login" + state.toString()); final progress = ProgressHUD.of(context); progress!.dismiss(); - debugPrint(state.userData!.user!.login!.user!.firstName); + context.goNamed('home', extra: state.userData); + } + if (state is UuidLoaded) { + Navigator.push(context, MaterialPageRoute(builder: (context) { + return QRLogin(); + })); } }, builder: (context, state) { if (state is VersionLoaded) { @@ -50,6 +60,7 @@ class _UniT2LoginState extends State { return SizedBox( child: SingleChildScrollView( child: Stack( + alignment: Alignment.center, children: [ Positioned( bottom: 0, @@ -64,11 +75,11 @@ class _UniT2LoginState extends State { const EdgeInsets.symmetric(horizontal: 25), child: Column( crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, children: [ - SizedBox(height: blockSizeVertical * 7), SvgPicture.asset( 'assets/svgs/logo.svg', - height: blockSizeVertical * 16, + height: blockSizeVertical * 12, allowDrawingOutsideViewBox: true, color: primary, ), @@ -76,25 +87,25 @@ class _UniT2LoginState extends State { Text( welcome, style: TextStyle( - fontSize: blockSizeVertical * 5, + fontSize: blockSizeVertical * 4, fontWeight: FontWeight.w600), ), Text(unitApp, style: TextStyle( - fontSize: blockSizeVertical * 8, + fontSize: blockSizeVertical * 6, fontWeight: FontWeight.w800, letterSpacing: .2, height: 1, color: primary)), - Text( - loginToContinue, - style: TextStyle( - fontSize: blockSizeVertical * 2, - height: 1.5, - fontWeight: FontWeight.w600), - ), + // Text( + // loginToContinue, + // style: TextStyle( + // fontSize: blockSizeVertical * 1.7, + // height: 1.5, + // fontWeight: FontWeight.w600), + // ), SizedBox( - height: blockSizeVertical * 1.5, + height: blockSizeVertical * 3, ), // USERNAME FormBuilderTextField( @@ -243,17 +254,10 @@ class _UniT2LoginState extends State { loginViaQr, style: TextStyle(color: second), ), - onPressed: () async { - context.goNamed('register'); - // ScanResult result = - // await QRCodeBarCodeScanner.instance - // .scanner(); - - // debugPrint(result.type.toString()); - // debugPrint( - // result.rawContent.toString()); - // BlocProvider.of(context) - // .add(QRCodelogin()); + onPressed: () { + context + .read() + .add(GetUuid()); }, ), )), @@ -302,9 +306,7 @@ class _UniT2LoginState extends State { if (state is SplashScreen) { return const UniTSplashScreen(); } - return const Center( - child: Text("Default"), - ); + return Container(); }), ), ), diff --git a/unit2/lib/screens/unit2/login/qr_login.dart b/unit2/lib/screens/unit2/login/qr_login.dart new file mode 100644 index 0000000..fbbd3ae --- /dev/null +++ b/unit2/lib/screens/unit2/login/qr_login.dart @@ -0,0 +1,239 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/container.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:fluttericon/font_awesome5_icons.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:go_router/go_router.dart'; +import 'package:unit2/theme-data.dart/btn-style.dart'; +import 'package:unit2/widgets/wave.dart'; + +import '../../../bloc/bloc/user_bloc.dart'; +import '../../../theme-data.dart/colors.dart'; +import '../../../theme-data.dart/form-style.dart'; +import '../../../utils/global.dart'; +import '../../../utils/text_container.dart'; +import '../../../utils/validators.dart'; + +class QRLogin extends StatefulWidget { + const QRLogin({super.key}); + + @override + State createState() => _QRLoginState(); +} + +class _QRLoginState extends State { + bool showSuffixIcon = false; + bool _showPassword = true; + final _formKey = GlobalKey(); + @override + Widget build(BuildContext context) { + return SafeArea( + child: Scaffold( + resizeToAvoidBottomInset: true, + appBar: AppBar( + backgroundColor: primary, + elevation: 0, + automaticallyImplyLeading: true, + title: const Text("Login via QR"), + centerTitle: true, + ), + body: BlocBuilder( + builder: (context, state) { + print(state); + if (state is UuidLoaded) { + return Center( + child: Text(state.uuid), + ); + } + return SingleChildScrollView( + child: Stack( + children: [ + Positioned( + bottom: 0, + child: WaveReverse(height: blockSizeVertical * 8)), + Container( + height: screenHeight * .90, + padding: const EdgeInsets.symmetric(horizontal: 15), + child: FormBuilder( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SvgPicture.asset( + 'assets/svgs/logo.svg', + height: blockSizeVertical * 12, + allowDrawingOutsideViewBox: true, + color: primary, + ), + const SizedBox( + height: 12, + ), + Text(unitApp, + style: TextStyle( + fontSize: blockSizeVertical * 5, + fontWeight: FontWeight.w800, + letterSpacing: .2, + height: 1, + color: Colors.black87)), + const SizedBox( + height: 15, + ), + Text( + "Enter your password", + style: TextStyle( + fontSize: blockSizeVertical * 1.5, + height: 1.5, + fontWeight: FontWeight.w600), + ), + const SizedBox( + height: 15, + ), + // Password + FormBuilderTextField( + name: 'password', + validator: registerPasswordValidator, + // initialValue: state.password, + onChanged: (value) { + value!.isEmpty + ? setState(() { + showSuffixIcon = false; + }) + : setState(() { + showSuffixIcon = true; + }); + }, + autofocus: false, + style: const TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black87), + decoration: loginTextFieldStyle().copyWith( + suffixIcon: Visibility( + visible: showSuffixIcon, + child: _showPassword + ? IconButton( + icon: Icon(FontAwesome5.eye_slash, + size: 24, + color: Theme.of(context) + .textTheme + .displayLarge + ?.color), + onPressed: () { + setState(() { + _showPassword = false; + }); + }, + ) + : IconButton( + onPressed: () { + setState(() { + _showPassword = true; + }); + }, + icon: Icon(FontAwesome5.eye, + size: 24, + color: Theme.of(context) + .textTheme + .displayLarge + ?.color)), + ), + prefixIcon: const Icon(Icons.lock), + labelText: "Password", + hintText: enterPassword), + obscureText: _showPassword ? true : false, + ), + const SizedBox( + height: 15, + ), + // FormBuilderTextField( + // name: 'confirmPassword', + // validator: registerPasswordValidator, + // // initialValue: state.password, + // onChanged: (value) { + // value!.isEmpty + // ? setState(() { + // showSuffixIcon = false; + // }) + // : setState(() { + // showSuffixIcon = true; + // }); + // }, + // autofocus: false, + // style: const TextStyle( + // fontWeight: FontWeight.bold, color: Colors.black87), + // decoration: loginTextFieldStyle().copyWith( + // suffixIcon: Visibility( + // visible: showSuffixIcon, + // child: _showPassword + // ? IconButton( + // icon: Icon(FontAwesome5.eye_slash, + // size: 24, + // color: Theme.of(context) + // .textTheme + // .displayLarge + // ?.color), + // onPressed: () { + // setState(() { + // _showPassword = false; + // }); + // }, + // ) + // : IconButton( + // onPressed: () { + // setState(() { + // _showPassword = true; + // }); + // }, + // icon: Icon(FontAwesome5.eye, + // size: 24, + // color: Theme.of(context) + // .textTheme + // .displayLarge + // ?.color)), + // ), + // prefixIcon: const Icon(Icons.lock), + // labelText: confirmPassword, + // hintText: enterConfirmPassword), + // obscureText: _showPassword ? true : false, + // ), + const SizedBox( + height: 15, + ), + SizedBox( + width: double.infinity, + height: blockSizeVertical * 6, + child: ElevatedButton( + style: secondaryBtnStyle( + second, Colors.transparent, primary), + onPressed: () { + if (_formKey.currentState! + .saveAndValidate()) { + BlocProvider.of(context) + .add(UserLogin( + username: _formKey + .currentState!.value['username'], + password: _formKey + .currentState!.value['password'], + )); + } + }, + child: const Text(submit)), + ) + ], + ), + ), + ), + ], + ), + ); + }, + ), + ), + ); + } +} diff --git a/unit2/lib/screens/unit2/login/register.dart b/unit2/lib/screens/unit2/login/register.dart deleted file mode 100644 index 0468000..0000000 --- a/unit2/lib/screens/unit2/login/register.dart +++ /dev/null @@ -1,213 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/container.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter_form_builder/flutter_form_builder.dart'; -import 'package:flutter_svg/svg.dart'; -import 'package:fluttericon/font_awesome5_icons.dart'; -import 'package:fluttericon/font_awesome_icons.dart'; -import 'package:form_builder_validators/form_builder_validators.dart'; -import 'package:go_router/go_router.dart'; -import 'package:unit2/theme-data.dart/btn-style.dart'; -import 'package:unit2/widgets/wave.dart'; - -import '../../../theme-data.dart/colors.dart'; -import '../../../theme-data.dart/form-style.dart'; -import '../../../utils/global.dart'; -import '../../../utils/text_container.dart'; -import '../../../utils/validators.dart'; - -class Register extends StatefulWidget { - const Register({super.key}); - - @override - State createState() => _RegisterState(); -} - -class _RegisterState extends State { - bool showSuffixIcon = false; - bool _showPassword = true; - final _formKey = GlobalKey(); - @override - Widget build(BuildContext context) { - return SafeArea( - child: Scaffold( - resizeToAvoidBottomInset: true, - appBar: AppBar( - backgroundColor: primary, - elevation: 0, - automaticallyImplyLeading: true, - title: const Text("Register password"), - centerTitle: true, - ), - body: SingleChildScrollView( - child: Stack( - children: [ - Wave(height: blockSizeVertical * 8), - Positioned( - bottom: 0, child: WaveReverse(height: blockSizeVertical * 8)), - Container( - height: screenHeight * .90, - padding: const EdgeInsets.symmetric(horizontal: 15), - child: FormBuilder( - key: _formKey, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SvgPicture.asset( - 'assets/svgs/logo.svg', - height: blockSizeVertical * 16, - allowDrawingOutsideViewBox: true, - color: primary, - ), - Text(unitApp, - style: TextStyle( - fontSize: blockSizeVertical * 8, - fontWeight: FontWeight.w800, - letterSpacing: .2, - height: 1, - color: Colors.black87)), - Text( - registerToContinue, - style: TextStyle( - fontSize: blockSizeVertical * 2, - height: 1.5, - fontWeight: FontWeight.w600), - ), - const SizedBox( - height: 15, - ), - // Password - FormBuilderTextField( - name: 'password', - validator: registerPasswordValidator, - // initialValue: state.password, - onChanged: (value) { - value!.isEmpty - ? setState(() { - showSuffixIcon = false; - }) - : setState(() { - showSuffixIcon = true; - }); - }, - autofocus: false, - style: const TextStyle( - fontWeight: FontWeight.bold, color: Colors.black87), - decoration: loginTextFieldStyle().copyWith( - suffixIcon: Visibility( - visible: showSuffixIcon, - child: _showPassword - ? IconButton( - icon: Icon(FontAwesome5.eye_slash, - size: 24, - color: Theme.of(context) - .textTheme - .displayLarge - ?.color), - onPressed: () { - setState(() { - _showPassword = false; - }); - }, - ) - : IconButton( - onPressed: () { - setState(() { - _showPassword = true; - }); - }, - icon: Icon(FontAwesome5.eye, - size: 24, - color: Theme.of(context) - .textTheme - .displayLarge - ?.color)), - ), - prefixIcon: const Icon(Icons.lock), - labelText: "Password", - hintText: enterPassword), - obscureText: _showPassword ? true : false, - ), - const SizedBox( - height: 15, - ), - FormBuilderTextField( - name: 'confirmPassword', - validator: registerPasswordValidator, - // initialValue: state.password, - onChanged: (value) { - value!.isEmpty - ? setState(() { - showSuffixIcon = false; - }) - : setState(() { - showSuffixIcon = true; - }); - }, - autofocus: false, - style: const TextStyle( - fontWeight: FontWeight.bold, color: Colors.black87), - decoration: loginTextFieldStyle().copyWith( - suffixIcon: Visibility( - visible: showSuffixIcon, - child: _showPassword - ? IconButton( - icon: Icon(FontAwesome5.eye_slash, - size: 24, - color: Theme.of(context) - .textTheme - .displayLarge - ?.color), - onPressed: () { - setState(() { - _showPassword = false; - }); - }, - ) - : IconButton( - onPressed: () { - setState(() { - _showPassword = true; - }); - }, - icon: Icon(FontAwesome5.eye, - size: 24, - color: Theme.of(context) - .textTheme - .displayLarge - ?.color)), - ), - prefixIcon: const Icon(Icons.lock), - labelText: confirmPassword, - hintText: enterConfirmPassword), - obscureText: _showPassword ? true : false, - ), - const SizedBox( - height: 15, - ), - SizedBox( - width: double.infinity, - height: blockSizeVertical * 6, - child: ElevatedButton( - style: secondaryBtnStyle( - second, Colors.transparent, primary), - onPressed: () { - if (_formKey.currentState!.saveAndValidate()) { - context.goNamed('home'); - } - }, - child: const Text(register)), - ) - ], - ), - ), - ), - ], - ), - ), - ), - ); - } -} diff --git a/unit2/lib/screens/unit2/profile/profile.dart b/unit2/lib/screens/unit2/profile/profile.dart index 33cfec9..b0004f5 100644 --- a/unit2/lib/screens/unit2/profile/profile.dart +++ b/unit2/lib/screens/unit2/profile/profile.dart @@ -1,14 +1,19 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_svg/svg.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; -import 'package:fluttericon/web_symbols_icons.dart'; import 'package:go_router/go_router.dart'; +import 'package:intl/intl.dart'; import 'package:qr_flutter/qr_flutter.dart'; +import 'package:unit2/bloc/bloc/user_bloc.dart'; +import 'package:unit2/model/login_data/user_info/user_data.dart'; import 'package:unit2/test_data.dart'; import 'package:unit2/theme-data.dart/btn-style.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; import '../../../theme-data.dart/colors.dart'; +import '../../../widgets/splash_screen.dart'; import './components/cover-image.dart'; class Profile extends StatelessWidget { @@ -20,69 +25,81 @@ class Profile extends StatelessWidget { onWillPop: () async { return Future.value(true); }, - child: SafeArea( - child: Scaffold( - body: SizedBox( - width: screenWidth, - child: Column( - children: [ - Stack( - clipBehavior: Clip.none, - alignment: Alignment.center, - children: [ - const CoverImage(), - Positioned( - top: blockSizeVertical * 17.5, - child: Stack( - alignment: Alignment.center, - children: [ - const CircleAvatar( - radius: 72, - backgroundColor: Colors.white, - ), - CircleAvatar( - radius: 69, - backgroundColor: Colors.grey.shade800, - child: SvgPicture.asset( - 'assets/svgs/male.svg', + child: ProgressHUD( + child: BlocBuilder( + builder: (context, state) { + if (state is UserLoggedIn) { + state.userData!.employeeInfo!.profile!.sex!.toUpperCase(); + return SafeArea( + child: Scaffold( + body: SizedBox( + width: screenWidth, + child: Column( + children: [ + Stack( + clipBehavior: Clip.none, + alignment: Alignment.center, + children: [ + const CoverImage(), + Positioned( + top: blockSizeVertical * 17.5, + child: Stack( + alignment: Alignment.center, + children: [ + const CircleAvatar( + radius: 72, + backgroundColor: Colors.white, + ), + CircleAvatar( + radius: 69, + backgroundColor: Colors.grey.shade800, + child: SvgPicture.asset( + 'assets/svgs/male.svg', + ), + ), + ], + ), ), - ), - ], - ), + Positioned( + top: 10, + left: 20, + child: IconButton( + onPressed: () { + Navigator.pop(context); + }, + icon: const Icon( + FontAwesome5.arrow_left, + size: 24, + color: Colors.white, + ), + )), + Positioned( + top: 10, + right: 20, + child: IconButton( + onPressed: () {}, + icon: const Icon( + Icons.edit, + size: 24, + color: Colors.white, + ), + )), + ], + ), + SizedBox( + height: blockSizeVertical * 5, + ), + BuildInformation( + userData: state.userData!, + ), + ], ), - Positioned( - top: 10, - left: 20, - child: IconButton( - onPressed: () { - context.go(context.namedLocation('home')); - }, - icon: const Icon( - FontAwesome5.arrow_left, - size: 24, - color: Colors.white, - ), - )), - Positioned( - top: 10, - right: 20, - child: IconButton( - onPressed: () {}, - icon: const Icon( - Icons.edit, - size: 24, - color: Colors.white, - ), - )), - ], + ), ), - SizedBox( - height: blockSizeVertical * 5, - ), - const BuildInformation(), - ], - ), - ), + ); + } + return const UniTSplashScreen(); + }, ), ), ); @@ -90,9 +107,19 @@ class Profile extends StatelessWidget { } class BuildInformation extends StatelessWidget { - const BuildInformation({super.key}); + final UserData userData; + const BuildInformation({super.key, required this.userData}); @override Widget build(BuildContext context) { + DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); + final String firstName = + userData.user!.login!.user!.firstName!.toUpperCase(); + final String lastname = userData.user!.login!.user!.lastName!.toUpperCase(); + final String middlename = + userData.employeeInfo!.profile!.middleName!.toUpperCase(); + final String sex = userData.employeeInfo!.profile!.sex!.toUpperCase(); + final DateTime? bday = userData.employeeInfo!.profile!.birthdate; + final uuid = userData.employeeInfo!.uuid; return Container( padding: const EdgeInsets.symmetric(horizontal: 25), width: screenWidth, @@ -102,7 +129,8 @@ class BuildInformation extends StatelessWidget { height: 25, ), Text( - "Rodolfo Bernales Acuin", + "$firstName $middlename $lastname", + textAlign: TextAlign.center, style: Theme.of(context) .textTheme .headline5! @@ -112,14 +140,14 @@ class BuildInformation extends StatelessWidget { height: 10, ), Text( - "july 14, 1994 | Male", + "${dteFormat2.format(bday!)} | $sex", style: Theme.of(context).textTheme.caption!.copyWith(fontSize: 18), ), const SizedBox( height: 15, ), QrImage( - data: uuid, + data: uuid!, size: blockSizeVertical * 30, ), const SizedBox( diff --git a/unit2/lib/test_data.dart b/unit2/lib/test_data.dart index 47714c3..566f446 100644 --- a/unit2/lib/test_data.dart +++ b/unit2/lib/test_data.dart @@ -1,5 +1,10 @@ import 'package:azlistview/azlistview.dart'; -String uuid = 'f68c3142-b939-11ec-9acb-3939f0cc109a'; +import 'package:flutter/cupertino.dart'; +import 'package:fluttericon/font_awesome5_icons.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:fluttericon/rpg_awesome_icons.dart'; +import 'package:unit2/model/login_data/user_info/role.dart'; + List levels = ['Establishments', 'Office']; List establishments = ['Provincial Government of Agusan del Norte']; List checkPointAreas = [ @@ -85,3 +90,17 @@ class Person extends ISuspensionBean { return lastname[0]; } } + +// List roles = [ +// Roles(name: 'QR scanner', icon: FontAwesome.qrcode), +// Roles(name: "Security", icon: FontAwesome5.user_shield), +// Roles(name: "Establishment point person", icon: FontAwesome.building_filled), +// Roles(name: "Registration in charge", icon: FontAwesome.user_plus), +// Roles(name: 'Super admin', icon: FontAwesome5.user_lock), +// Roles(name: "Purok president", icon: FontAwesome5.user_tie), +// Roles(name: "Check point inchage", icon: FontAwesome5.hand_paper), +// Roles(name: "Health officer", icon: RpgAwesome.health_decrease), + +// ]; + + diff --git a/unit2/lib/utils/app_router.dart b/unit2/lib/utils/app_router.dart new file mode 100644 index 0000000..cb7304f --- /dev/null +++ b/unit2/lib/utils/app_router.dart @@ -0,0 +1,14 @@ +// import 'package:flutter/cupertino.dart'; +// import 'package:flutter_bloc/flutter_bloc.dart'; + +// class AppRouter{ +// Route onGenerateRoute(RouteSettings routeSettings){ +// switch(routeSettings.name){ +// case '/': +// return BlocProvider( +// create: (context) => UserBloc()..add(GetApkVersion()), +// child: const UniT2Login(), +// ); +// } +// } +// } \ No newline at end of file diff --git a/unit2/lib/utils/global_context.dart b/unit2/lib/utils/global_context.dart new file mode 100644 index 0000000..6b7b74d --- /dev/null +++ b/unit2/lib/utils/global_context.dart @@ -0,0 +1,6 @@ +import 'package:flutter/material.dart'; + +class NavigationService { + static GlobalKey navigatorKey = + GlobalKey(); +} diff --git a/unit2/lib/utils/router.dart b/unit2/lib/utils/router.dart index 4d00ecb..d47138d 100644 --- a/unit2/lib/utils/router.dart +++ b/unit2/lib/utils/router.dart @@ -2,10 +2,12 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:unit2/bloc/bloc/user_bloc.dart'; -import 'package:unit2/screens/unit2/login/register.dart'; +import 'package:unit2/model/login_data/user_info/user_data.dart'; +import 'package:unit2/screens/unit2/login/qr_login.dart'; import 'package:unit2/screens/unit2/roles/qr_code_scanner.dart/scan.dart'; import 'package:unit2/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart'; import 'package:unit2/screens/unit2/signature/signature_pad.dart'; +import 'package:unit2/utils/global_context.dart'; import 'package:unit2/utils/scanner.dart'; import '../screens/docsms/components/doc_info_tile.dart'; import '../screens/docsms/request_receipt.dart'; @@ -21,24 +23,38 @@ final GoRouter goRouter = GoRouter(routes: [ path: '/', name: 'login', builder: (context, state) { - debugPrint("login bloc called"); - BlocProvider.of(context).add(GetApkVersion()); - return const UniT2Login(); + return BlocProvider( + create: (context) => UserBloc()..add(GetApkVersion()), + child: const UniT2Login(), + ); }, routes: [ - GoRoute( - name: 'register', - path: 'register', - builder: ((context, state) => const Register())), + // GoRoute( + // name: 'qr-login', + // path: 'qr-login', + // builder: ((context, state) => const QRLogin())), GoRoute( name: 'home', path: 'home', - builder: (context, state) => const DrawerScreen(), + builder: (context, state) { + UserData userData = state.extra as UserData; + return BlocProvider.value( + value: UserBloc()..add((LoadLoggedInUser(userData: userData))), + child: const DrawerScreen(), + ); + }, routes: [ GoRoute( name: 'profile', path: 'profile', - builder: (context, state) => const Profile(), + builder: (context, state) { + UserData userData = state.extra as UserData; + return BlocProvider.value( + value: UserBloc() + ..add((LoadLoggedInUser(userData: userData))), + child: const Profile(), + ); + }, routes: [ GoRoute( name: 'signature', diff --git a/unit2/lib/utils/text_container.dart b/unit2/lib/utils/text_container.dart index 9fe0563..8363228 100644 --- a/unit2/lib/utils/text_container.dart +++ b/unit2/lib/utils/text_container.dart @@ -14,7 +14,7 @@ const String login = "LOGIN"; const String sOSTitle = "Request SOS"; const String sOSReceivedMessage = "your SOS request has been received. Please wait for respondent's acknowledgement."; -const String unit2ModuleScreen = "uniT2 Modules"; +const String unit2ModuleScreen = "uniT Dashboard"; const String welcome = "Welcome to!"; const String unitApp = 'uniT-App'; const String loginToContinue = "Please login to continue."; diff --git a/unit2/lib/widgets/splash_screen.dart b/unit2/lib/widgets/splash_screen.dart index ceb6ba3..41a2a8d 100644 --- a/unit2/lib/widgets/splash_screen.dart +++ b/unit2/lib/widgets/splash_screen.dart @@ -12,36 +12,40 @@ class UniTSplashScreen extends StatelessWidget { @override Widget build(BuildContext context) { - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SlideInUp( - from: 50, - duration: const Duration(milliseconds: 300), - child: SvgPicture.asset( - 'assets/svgs/logo.svg', - height: blockSizeVertical * 15.0, - allowDrawingOutsideViewBox: true, - color: primary, + return Container( + height: MediaQuery.of(context).size.height, + color: Colors.white, + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SlideInUp( + from: 50, + duration: const Duration(milliseconds: 300), + child: SvgPicture.asset( + 'assets/svgs/logo.svg', + height: blockSizeVertical * 12.0, + allowDrawingOutsideViewBox: true, + color: primary, + ), ), - ), - const SizedBox( - height: 24, - ), - SlideInDown( - from: 100, - duration: const Duration(milliseconds: 200), - child: Text("uniT-App", - style: TextStyle( - fontSize: blockSizeVertical * 5, - fontWeight: FontWeight.w800, - letterSpacing: .2, - height: 1, - color: Colors.black)), - ), - ], + const SizedBox( + height: 12, + ), + SlideInDown( + from: 100, + duration: const Duration(milliseconds: 200), + child: Text("uniT-App", + style: TextStyle( + fontSize: blockSizeVertical * 4, + fontWeight: FontWeight.w600, + letterSpacing: .2, + height: 1, + color: Colors.black)), + ), + ], + ), ), ); } diff --git a/unit2/pubspec.yaml b/unit2/pubspec.yaml index 7257490..fa8c45e 100644 --- a/unit2/pubspec.yaml +++ b/unit2/pubspec.yaml @@ -60,7 +60,7 @@ dependencies: signature: ^5.3.0 awesome_dialog: ^3.0.2 system_info2: ^2.0.4 - flutter_bloc: ^8.1.1 + flutter_bloc: ^8.0.0 equatable: ^2.0.5 dev_dependencies: From 226899893f1be91aba5a05e9cc5d6bece4ccf0e3 Mon Sep 17 00:00:00 2001 From: rodolfobacuinjr Date: Mon, 23 Jan 2023 13:46:09 +0800 Subject: [PATCH 13/86] converted into non go router --- unit2/lib/bloc/bloc/user_bloc.dart | 6 +- unit2/lib/bloc/bloc/user_event.dart | 15 +- unit2/lib/main.dart | 20 ++- .../components/drawer-screen.dart | 39 ++--- unit2/lib/screens/unit2/login/login.dart | 2 +- unit2/lib/utils/app_router.dart | 44 +++-- unit2/lib/utils/router.dart | 154 +++++++++--------- 7 files changed, 150 insertions(+), 130 deletions(-) diff --git a/unit2/lib/bloc/bloc/user_bloc.dart b/unit2/lib/bloc/bloc/user_bloc.dart index 2a8c144..d6e8c26 100644 --- a/unit2/lib/bloc/bloc/user_bloc.dart +++ b/unit2/lib/bloc/bloc/user_bloc.dart @@ -33,17 +33,17 @@ class UserBloc extends Bloc { UserData? userData = await AuthService.instance .webLogin(username: event.username, password: event.password); _userData = userData; - emit(UserLoggedIn(userData: userData)); + emit(UserLoggedIn(userData: _userData)); } catch (e) { emit(UserError(message: e.toString())); } }); on((event, emit) { - emit(UserLoggedIn(userData: event.userData)); + emit(UserLoggedIn(userData: _userData)); }); on((event, emit) async { ScanResult result = await QRCodeBarCodeScanner.instance.scanner(); - if(result.rawContent.toString().isNotEmpty){ + if (result.rawContent.toString().isNotEmpty) { emit(UuidLoaded(uuid: result.rawContent.toString())); } }); diff --git a/unit2/lib/bloc/bloc/user_event.dart b/unit2/lib/bloc/bloc/user_event.dart index 019edbc..16c0a2f 100644 --- a/unit2/lib/bloc/bloc/user_event.dart +++ b/unit2/lib/bloc/bloc/user_event.dart @@ -5,24 +5,23 @@ abstract class UserEvent extends Equatable { List get props => []; } -class GetApkVersion extends UserEvent{ +class GetApkVersion extends UserEvent { GetApkVersion(); @override List get props => []; } -class UserLogin extends UserEvent{ +class UserLogin extends UserEvent { final String? username; final String? password; - UserLogin({this.username,this.password}); + UserLogin({this.username, this.password}); } -class LoadLoggedInUser extends UserEvent{ - final UserData? userData; - LoadLoggedInUser({this.userData}); +class LoadLoggedInUser extends UserEvent { + // final UserData? userData; + LoadLoggedInUser(); } -class GetUuid extends UserEvent{ - +class GetUuid extends UserEvent { GetUuid(); } diff --git a/unit2/lib/main.dart b/unit2/lib/main.dart index 9ff8fb7..be88b93 100644 --- a/unit2/lib/main.dart +++ b/unit2/lib/main.dart @@ -4,13 +4,14 @@ import 'package:device_preview/device_preview.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:unit2/bloc/bloc/user_bloc.dart'; +import 'package:unit2/utils/app_router.dart'; import 'package:unit2/utils/global_context.dart'; import 'package:unit2/utils/global_context.dart'; import './utils/router.dart'; import './utils/global.dart'; void main() { - runApp(const MyApp()); + runApp(MyApp()); } // void main() => runApp( @@ -21,12 +22,12 @@ void main() { // ); class MyApp extends StatelessWidget { - const MyApp({super.key}); + AppRouter? _appRouter; // This widget is the root of your application. @override Widget build(BuildContext context) { - BuildContext? parentContext = NavigationService.navigatorKey.currentContext; + _appRouter = AppRouter(); final mediaQueryData = MediaQueryData.fromWindow(WidgetsBinding.instance.window); screenWidth = mediaQueryData.size.width; @@ -39,20 +40,20 @@ class MyApp extends StatelessWidget { mediaQueryData.padding.top + mediaQueryData.padding.bottom; safeBlockHorizontal = (screenWidth - safeAreaHorizontal) / 100; safeBlockVertical = (screenHeight - safeAreaVertical) / 100; - + return MultiBlocProvider( providers: [ BlocProvider( - create: (parentContext) => UserBloc(), + create: (_) => UserBloc(), ), ], - child: MaterialApp.router( - key: NavigationService.navigatorKey, + child: MaterialApp( + navigatorKey: NavigationService.navigatorKey, // useInheritedMediaQuery: true, // locale: DevicePreview.locale(context), // builder: DevicePreview.appBuilder, - routeInformationParser: goRouter.routeInformationParser, - routerDelegate: goRouter.routerDelegate, + // routeInformationParser: goRouter.routeInformationParser, + // routerDelegate: goRouter.routerDelegate, // routeInformationProvider: goRouter.routeInformationProvider, title: 'uniT2 - Universal Tracker and Tracer', theme: ThemeData( @@ -64,6 +65,7 @@ class MyApp extends StatelessWidget { fontFamily: 'LexendDeca', ), debugShowCheckedModeBanner: false, + onGenerateRoute: _appRouter!.onGenerateRoute, ), ); } diff --git a/unit2/lib/screens/unit2/homepage.dart/components/drawer-screen.dart b/unit2/lib/screens/unit2/homepage.dart/components/drawer-screen.dart index 78d1014..54a4405 100644 --- a/unit2/lib/screens/unit2/homepage.dart/components/drawer-screen.dart +++ b/unit2/lib/screens/unit2/homepage.dart/components/drawer-screen.dart @@ -20,26 +20,27 @@ class _DrawerScreenState extends State { Widget build(BuildContext context) { return BlocBuilder( builder: (context, state) { - print("drawer screen"+state.toString()); - if(state is UserLoggedIn){ - return ZoomDrawer( - controller: zoomDrawerController, - menuScreen: MenuScreen(userData: state.userData,), - mainScreen: SizedBox( - height: MediaQuery.of(context).size.height, - child: const MainScreen()), - style: DrawerStyle.defaultStyle, - borderRadius: 24.0, - showShadow: false, - angle: -0.0, - slideWidth: MediaQuery.of(context).size.width * .90, - openCurve: Curves.fastOutSlowIn, - closeCurve: Curves.easeOut, - menuBackgroundColor: Colors.grey, - ); + print("drawer screen" + state.toString()); + if (state is UserLoggedIn) { + return ZoomDrawer( + controller: zoomDrawerController, + menuScreen: MenuScreen( + userData: state.userData, + ), + mainScreen: SizedBox( + height: MediaQuery.of(context).size.height, + child: const MainScreen()), + style: DrawerStyle.defaultStyle, + borderRadius: 24.0, + showShadow: false, + angle: -0.0, + slideWidth: MediaQuery.of(context).size.width * .90, + openCurve: Curves.fastOutSlowIn, + closeCurve: Curves.easeOut, + menuBackgroundColor: Colors.grey, + ); } - return const UniTSplashScreen(); - + return const UniTSplashScreen(); }, ); } diff --git a/unit2/lib/screens/unit2/login/login.dart b/unit2/lib/screens/unit2/login/login.dart index b1ff3ec..ba6faa3 100644 --- a/unit2/lib/screens/unit2/login/login.dart +++ b/unit2/lib/screens/unit2/login/login.dart @@ -47,7 +47,7 @@ class _UniT2LoginState extends State { print("login" + state.toString()); final progress = ProgressHUD.of(context); progress!.dismiss(); - context.goNamed('home', extra: state.userData); + Navigator.pushReplacementNamed(context, '/module-screen'); } if (state is UuidLoaded) { Navigator.push(context, MaterialPageRoute(builder: (context) { diff --git a/unit2/lib/utils/app_router.dart b/unit2/lib/utils/app_router.dart index cb7304f..bfe2ba0 100644 --- a/unit2/lib/utils/app_router.dart +++ b/unit2/lib/utils/app_router.dart @@ -1,14 +1,32 @@ -// import 'package:flutter/cupertino.dart'; -// import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:unit2/bloc/bloc/user_bloc.dart'; +import 'package:unit2/screens/unit2/login/login.dart'; +import 'package:unit2/utils/global_context.dart'; -// class AppRouter{ -// Route onGenerateRoute(RouteSettings routeSettings){ -// switch(routeSettings.name){ -// case '/': -// return BlocProvider( -// create: (context) => UserBloc()..add(GetApkVersion()), -// child: const UniT2Login(), -// ); -// } -// } -// } \ No newline at end of file +import '../screens/unit2/homepage.dart/components/drawer-screen.dart'; + +class AppRouter { + Route onGenerateRoute(RouteSettings routeSettings) { + switch (routeSettings.name) { + case '/': + BlocProvider.of( + NavigationService.navigatorKey.currentContext!) + .add(GetApkVersion()); + return MaterialPageRoute(builder: (_) { + return const UniT2Login(); + }); + case '/module-screen': + BlocProvider.of( NavigationService.navigatorKey.currentContext!).add(LoadLoggedInUser()); + return MaterialPageRoute(builder: (_) { + return const DrawerScreen(); + }); + + default: + return MaterialPageRoute(builder: (context) { + return Container(); + }); + } + } +} diff --git a/unit2/lib/utils/router.dart b/unit2/lib/utils/router.dart index d47138d..0f7fa3a 100644 --- a/unit2/lib/utils/router.dart +++ b/unit2/lib/utils/router.dart @@ -1,78 +1,78 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:go_router/go_router.dart'; -import 'package:unit2/bloc/bloc/user_bloc.dart'; -import 'package:unit2/model/login_data/user_info/user_data.dart'; -import 'package:unit2/screens/unit2/login/qr_login.dart'; -import 'package:unit2/screens/unit2/roles/qr_code_scanner.dart/scan.dart'; -import 'package:unit2/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart'; -import 'package:unit2/screens/unit2/signature/signature_pad.dart'; -import 'package:unit2/utils/global_context.dart'; -import 'package:unit2/utils/scanner.dart'; -import '../screens/docsms/components/doc_info_tile.dart'; -import '../screens/docsms/request_receipt.dart'; -import '../screens/sos/add_mobile.dart'; -import '../screens/sos/request_sos.dart'; -import '../screens/unit2/login/login.dart'; -import '../screens/unit2/homepage.dart/components/drawer-screen.dart'; -import '../screens/unit2/profile/profile.dart'; -import '../screens/unit2/roles/registration_in_charge/home.dart'; +// import 'package:flutter/cupertino.dart'; +// import 'package:flutter_bloc/flutter_bloc.dart'; +// import 'package:go_router/go_router.dart'; +// import 'package:unit2/bloc/bloc/user_bloc.dart'; +// import 'package:unit2/model/login_data/user_info/user_data.dart'; +// import 'package:unit2/screens/unit2/login/qr_login.dart'; +// import 'package:unit2/screens/unit2/roles/qr_code_scanner.dart/scan.dart'; +// import 'package:unit2/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart'; +// import 'package:unit2/screens/unit2/signature/signature_pad.dart'; +// import 'package:unit2/utils/global_context.dart'; +// import 'package:unit2/utils/scanner.dart'; +// import '../screens/docsms/components/doc_info_tile.dart'; +// import '../screens/docsms/request_receipt.dart'; +// import '../screens/sos/add_mobile.dart'; +// import '../screens/sos/request_sos.dart'; +// import '../screens/unit2/login/login.dart'; +// import '../screens/unit2/homepage.dart/components/drawer-screen.dart'; +// import '../screens/unit2/profile/profile.dart'; +// import '../screens/unit2/roles/registration_in_charge/home.dart'; -final GoRouter goRouter = GoRouter(routes: [ - GoRoute( - path: '/', - name: 'login', - builder: (context, state) { - return BlocProvider( - create: (context) => UserBloc()..add(GetApkVersion()), - child: const UniT2Login(), - ); - }, - routes: [ - // GoRoute( - // name: 'qr-login', - // path: 'qr-login', - // builder: ((context, state) => const QRLogin())), - GoRoute( - name: 'home', - path: 'home', - builder: (context, state) { - UserData userData = state.extra as UserData; - return BlocProvider.value( - value: UserBloc()..add((LoadLoggedInUser(userData: userData))), - child: const DrawerScreen(), - ); - }, - routes: [ - GoRoute( - name: 'profile', - path: 'profile', - builder: (context, state) { - UserData userData = state.extra as UserData; - return BlocProvider.value( - value: UserBloc() - ..add((LoadLoggedInUser(userData: userData))), - child: const Profile(), - ); - }, - routes: [ - GoRoute( - name: 'signature', - path: 'signature', - builder: (context, state) => const SignaturePad(), - ) - ]) - ]), - GoRoute( - name: 'add-mobile', - path: 'add-moble', - builder: (context, state) => AddMobile(), - routes: [ - GoRoute( - name: 'request-sos', - path: 'request-sos', - builder: (context, state) => const RequestSOS(), - ) - ]), - ]), -]); +// final GoRouter goRouter = GoRouter(routes: [ +// GoRoute( +// path: '/', +// name: 'login', +// builder: (context, state) { +// return BlocProvider( +// create: (context) => UserBloc()..add(GetApkVersion()), +// child: const UniT2Login(), +// ); +// }, +// routes: [ +// // GoRoute( +// // name: 'qr-login', +// // path: 'qr-login', +// // builder: ((context, state) => const QRLogin())), +// GoRoute( +// name: 'home', +// path: 'home', +// builder: (context, state) { +// UserData userData = state.extra as UserData; +// return BlocProvider.value( +// value: UserBloc()..add((LoadLoggedInUser(userData: userData))), +// child: const DrawerScreen(), +// ); +// }, +// routes: [ +// GoRoute( +// name: 'profile', +// path: 'profile', +// builder: (context, state) { +// UserData userData = state.extra as UserData; +// return BlocProvider.value( +// value: UserBloc() +// ..add((LoadLoggedInUser(userData: userData))), +// child: const Profile(), +// ); +// }, +// routes: [ +// GoRoute( +// name: 'signature', +// path: 'signature', +// builder: (context, state) => const SignaturePad(), +// ) +// ]) +// ]), +// GoRoute( +// name: 'add-mobile', +// path: 'add-moble', +// builder: (context, state) => AddMobile(), +// routes: [ +// GoRoute( +// name: 'request-sos', +// path: 'request-sos', +// builder: (context, state) => const RequestSOS(), +// ) +// ]), +// ]), +// ]); From 692d080e25a60c6fd6398a75d571d4aea3a0258b Mon Sep 17 00:00:00 2001 From: rodolfobacuinjr Date: Mon, 23 Jan 2023 16:23:20 +0800 Subject: [PATCH 14/86] integrated web and qr code login --- unit2/lib/bloc/bloc/user_bloc.dart | 17 +- unit2/lib/bloc/bloc/user_event.dart | 12 + .../basic-info.dart} | 4 +- .../components/cover-image.dart | 0 .../components/drawer-screen.dart | 1 - .../homepage.dart/components/menu-screen.dart | 6 +- .../unit2/homepage.dart/components/menu.dart | 2 +- unit2/lib/screens/unit2/login/login.dart | 9 +- unit2/lib/screens/unit2/login/qr_login.dart | 413 +++++++++--------- .../sevices/login_service/auth_service.dart | 18 + unit2/lib/utils/app_router.dart | 15 +- 11 files changed, 282 insertions(+), 215 deletions(-) rename unit2/lib/screens/unit2/{profile/profile.dart => basic-info/basic-info.dart} (98%) rename unit2/lib/screens/unit2/{profile => basic-info}/components/cover-image.dart (100%) diff --git a/unit2/lib/bloc/bloc/user_bloc.dart b/unit2/lib/bloc/bloc/user_bloc.dart index d6e8c26..7bd4eb5 100644 --- a/unit2/lib/bloc/bloc/user_bloc.dart +++ b/unit2/lib/bloc/bloc/user_bloc.dart @@ -14,6 +14,7 @@ part 'user_state.dart'; class UserBloc extends Bloc { UserData? _userData; + VersionInfo? _versionInfo; UserBloc() : super(UserInitial()) { // this event is called when opening the app to check if // there is new app version @@ -21,13 +22,17 @@ class UserBloc extends Bloc { try { emit(SplashScreen()); VersionInfo versionInfo = await AuthService.instance.getVersionInfo(); - emit(VersionLoaded(versionInfo: versionInfo)); + _versionInfo = versionInfo; + emit(VersionLoaded(versionInfo: _versionInfo)); } catch (e) { emit(UserError( message: e.toString(), )); } }); + on((event, emit) { + emit(VersionLoaded(versionInfo: _versionInfo)); + }); on((event, emit) async { try { UserData? userData = await AuthService.instance @@ -38,6 +43,16 @@ class UserBloc extends Bloc { emit(UserError(message: e.toString())); } }); + on((event, emit) async { + try { + UserData? userData = await AuthService.instance + .qrLogin(uuid: event.uuid, password: event.password); + _userData = userData; + emit(UserLoggedIn(userData: _userData)); + } catch (e) { + emit(UserError(message: e.toString())); + } + }); on((event, emit) { emit(UserLoggedIn(userData: _userData)); }); diff --git a/unit2/lib/bloc/bloc/user_event.dart b/unit2/lib/bloc/bloc/user_event.dart index 16c0a2f..6aca194 100644 --- a/unit2/lib/bloc/bloc/user_event.dart +++ b/unit2/lib/bloc/bloc/user_event.dart @@ -15,6 +15,8 @@ class UserLogin extends UserEvent { final String? username; final String? password; UserLogin({this.username, this.password}); + @override + List get props => [username!, password!]; } class LoadLoggedInUser extends UserEvent { @@ -25,3 +27,13 @@ class LoadLoggedInUser extends UserEvent { class GetUuid extends UserEvent { GetUuid(); } + +class LoadVersion extends UserEvent {} + +class UuidLogin extends UserEvent { + final String? uuid; + final String? password; + UuidLogin({this.uuid, this.password}); + @override + List get props => [uuid!, password!]; +} diff --git a/unit2/lib/screens/unit2/profile/profile.dart b/unit2/lib/screens/unit2/basic-info/basic-info.dart similarity index 98% rename from unit2/lib/screens/unit2/profile/profile.dart rename to unit2/lib/screens/unit2/basic-info/basic-info.dart index b0004f5..b6b7d3b 100644 --- a/unit2/lib/screens/unit2/profile/profile.dart +++ b/unit2/lib/screens/unit2/basic-info/basic-info.dart @@ -16,8 +16,8 @@ import '../../../theme-data.dart/colors.dart'; import '../../../widgets/splash_screen.dart'; import './components/cover-image.dart'; -class Profile extends StatelessWidget { - const Profile({super.key}); +class BasicInfo extends StatelessWidget { + const BasicInfo({super.key}); @override Widget build(BuildContext context) { diff --git a/unit2/lib/screens/unit2/profile/components/cover-image.dart b/unit2/lib/screens/unit2/basic-info/components/cover-image.dart similarity index 100% rename from unit2/lib/screens/unit2/profile/components/cover-image.dart rename to unit2/lib/screens/unit2/basic-info/components/cover-image.dart diff --git a/unit2/lib/screens/unit2/homepage.dart/components/drawer-screen.dart b/unit2/lib/screens/unit2/homepage.dart/components/drawer-screen.dart index 54a4405..c355f90 100644 --- a/unit2/lib/screens/unit2/homepage.dart/components/drawer-screen.dart +++ b/unit2/lib/screens/unit2/homepage.dart/components/drawer-screen.dart @@ -20,7 +20,6 @@ class _DrawerScreenState extends State { Widget build(BuildContext context) { return BlocBuilder( builder: (context, state) { - print("drawer screen" + state.toString()); if (state is UserLoggedIn) { return ZoomDrawer( controller: zoomDrawerController, diff --git a/unit2/lib/screens/unit2/homepage.dart/components/menu-screen.dart b/unit2/lib/screens/unit2/homepage.dart/components/menu-screen.dart index 1145405..c8d3ba4 100644 --- a/unit2/lib/screens/unit2/homepage.dart/components/menu-screen.dart +++ b/unit2/lib/screens/unit2/homepage.dart/components/menu-screen.dart @@ -52,13 +52,13 @@ class _MenuScreenState extends State { ), ), ), - getTile(FontAwesome5.user, "Basic Info", 'profile', context, + getTile(FontAwesome5.user, "Basic Info", '/basic-info', context, widget.userData!), const Divider(), getTile(FontAwesome5.user_circle, "Profile", - '/SelfAddressScreen', context, widget.userData!), + '/profile', context, widget.userData!), const Divider(), - getTile(FontAwesome5.life_ring, "Request SOS", 'request-sos', + getTile(FontAwesome5.life_ring, "Request SOS", '/request-sos', context, widget.userData!), const Divider(), Expanded( diff --git a/unit2/lib/screens/unit2/homepage.dart/components/menu.dart b/unit2/lib/screens/unit2/homepage.dart/components/menu.dart index 7c2544d..2a20438 100644 --- a/unit2/lib/screens/unit2/homepage.dart/components/menu.dart +++ b/unit2/lib/screens/unit2/homepage.dart/components/menu.dart @@ -23,7 +23,7 @@ Widget getTile( context.goNamed("login"); }); } else { - context.goNamed(route,extra: userData); + Navigator.pushNamed(context, route); } }, ); diff --git a/unit2/lib/screens/unit2/login/login.dart b/unit2/lib/screens/unit2/login/login.dart index ba6faa3..93b3a03 100644 --- a/unit2/lib/screens/unit2/login/login.dart +++ b/unit2/lib/screens/unit2/login/login.dart @@ -44,15 +44,12 @@ class _UniT2LoginState extends State { body: ProgressHUD( child: BlocConsumer(listener: (context, state) { if (state is UserLoggedIn) { - print("login" + state.toString()); final progress = ProgressHUD.of(context); progress!.dismiss(); - Navigator.pushReplacementNamed(context, '/module-screen'); + Navigator.pushReplacementNamed(context, '/module-screen'); } if (state is UuidLoaded) { - Navigator.push(context, MaterialPageRoute(builder: (context) { - return QRLogin(); - })); + Navigator.pushNamed(context, '/qr-login'); } }, builder: (context, state) { if (state is VersionLoaded) { @@ -126,8 +123,6 @@ class _UniT2LoginState extends State { name: 'password', validator: FormBuilderValidators.required( errorText: passwordRequired), - - // initialValue: state.password, onChanged: (value) { value!.isEmpty ? setState(() { diff --git a/unit2/lib/screens/unit2/login/qr_login.dart b/unit2/lib/screens/unit2/login/qr_login.dart index fbbd3ae..59b9512 100644 --- a/unit2/lib/screens/unit2/login/qr_login.dart +++ b/unit2/lib/screens/unit2/login/qr_login.dart @@ -4,12 +4,14 @@ import 'package:flutter/src/widgets/container.dart'; import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_svg/svg.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:fluttericon/font_awesome_icons.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:go_router/go_router.dart'; import 'package:unit2/theme-data.dart/btn-style.dart'; +import 'package:unit2/widgets/error_state.dart'; import 'package:unit2/widgets/wave.dart'; import '../../../bloc/bloc/user_bloc.dart'; @@ -32,206 +34,225 @@ class _QRLoginState extends State { final _formKey = GlobalKey(); @override Widget build(BuildContext context) { - return SafeArea( - child: Scaffold( - resizeToAvoidBottomInset: true, - appBar: AppBar( - backgroundColor: primary, - elevation: 0, - automaticallyImplyLeading: true, - title: const Text("Login via QR"), - centerTitle: true, - ), - body: BlocBuilder( - builder: (context, state) { - print(state); - if (state is UuidLoaded) { - return Center( - child: Text(state.uuid), - ); + return WillPopScope( + onWillPop: ()async{ + context.read().add(LoadVersion()); + return true; + }, + child: SafeArea( + child: Scaffold( + resizeToAvoidBottomInset: true, + appBar: AppBar( + backgroundColor: primary, + elevation: 0, + title: const Text("Login via QR"), + centerTitle: true, + ), + body: ProgressHUD( + child: BlocConsumer( + listener: (context,state){ + if (state is UserLoggedIn) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + Navigator.pushReplacementNamed(context, '/module-screen'); } - return SingleChildScrollView( - child: Stack( - children: [ - Positioned( - bottom: 0, - child: WaveReverse(height: blockSizeVertical * 8)), - Container( - height: screenHeight * .90, - padding: const EdgeInsets.symmetric(horizontal: 15), - child: FormBuilder( - key: _formKey, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SvgPicture.asset( - 'assets/svgs/logo.svg', - height: blockSizeVertical * 12, - allowDrawingOutsideViewBox: true, - color: primary, - ), - const SizedBox( - height: 12, - ), - Text(unitApp, - style: TextStyle( - fontSize: blockSizeVertical * 5, - fontWeight: FontWeight.w800, - letterSpacing: .2, - height: 1, - color: Colors.black87)), - const SizedBox( - height: 15, - ), - Text( - "Enter your password", - style: TextStyle( - fontSize: blockSizeVertical * 1.5, - height: 1.5, - fontWeight: FontWeight.w600), - ), - const SizedBox( - height: 15, - ), - // Password - FormBuilderTextField( - name: 'password', - validator: registerPasswordValidator, - // initialValue: state.password, - onChanged: (value) { - value!.isEmpty - ? setState(() { - showSuffixIcon = false; - }) - : setState(() { - showSuffixIcon = true; - }); - }, - autofocus: false, - style: const TextStyle( - fontWeight: FontWeight.bold, - color: Colors.black87), - decoration: loginTextFieldStyle().copyWith( - suffixIcon: Visibility( - visible: showSuffixIcon, - child: _showPassword - ? IconButton( - icon: Icon(FontAwesome5.eye_slash, - size: 24, - color: Theme.of(context) - .textTheme - .displayLarge - ?.color), - onPressed: () { - setState(() { - _showPassword = false; - }); - }, - ) - : IconButton( - onPressed: () { - setState(() { - _showPassword = true; - }); - }, - icon: Icon(FontAwesome5.eye, - size: 24, - color: Theme.of(context) - .textTheme - .displayLarge - ?.color)), + }, + builder: (context, state) { + if (state is UuidLoaded) { + return SingleChildScrollView( + child: Stack( + children: [ + Positioned( + bottom: 0, + child: WaveReverse(height: blockSizeVertical * 8)), + Container( + height: screenHeight * .90, + padding: const EdgeInsets.symmetric(horizontal: 15), + child: FormBuilder( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SvgPicture.asset( + 'assets/svgs/logo.svg', + height: blockSizeVertical * 12, + allowDrawingOutsideViewBox: true, + color: primary, ), - prefixIcon: const Icon(Icons.lock), - labelText: "Password", - hintText: enterPassword), - obscureText: _showPassword ? true : false, + const SizedBox( + height: 12, + ), + Text(unitApp, + style: TextStyle( + fontSize: blockSizeVertical * 5, + fontWeight: FontWeight.w800, + letterSpacing: .2, + height: 1, + color: Colors.black87)), + const SizedBox( + height: 15, + ), + Text( + "Enter your password", + style: TextStyle( + fontSize: blockSizeVertical * 1.5, + height: 1.5, + fontWeight: FontWeight.w600), + ), + const SizedBox( + height: 15, + ), + // Password + FormBuilderTextField( + name: 'password', + validator: registerPasswordValidator, + // initialValue: state.password, + onChanged: (value) { + value!.isEmpty + ? setState(() { + showSuffixIcon = false; + }) + : setState(() { + showSuffixIcon = true; + }); + }, + autofocus: false, + style: const TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black87), + decoration: loginTextFieldStyle().copyWith( + suffixIcon: Visibility( + visible: showSuffixIcon, + child: _showPassword + ? IconButton( + icon: Icon(FontAwesome5.eye_slash, + size: 24, + color: Theme.of(context) + .textTheme + .displayLarge + ?.color), + onPressed: () { + setState(() { + _showPassword = false; + }); + }, + ) + : IconButton( + onPressed: () { + setState(() { + _showPassword = true; + }); + }, + icon: Icon(FontAwesome5.eye, + size: 24, + color: Theme.of(context) + .textTheme + .displayLarge + ?.color)), + ), + prefixIcon: const Icon(Icons.lock), + labelText: "Password", + hintText: enterPassword), + obscureText: _showPassword ? true : false, + ), + const SizedBox( + height: 15, + ), + // FormBuilderTextField( + // name: 'confirmPassword', + // validator: registerPasswordValidator, + // // initialValue: state.password, + // onChanged: (value) { + // value!.isEmpty + // ? setState(() { + // showSuffixIcon = false; + // }) + // : setState(() { + // showSuffixIcon = true; + // }); + // }, + // autofocus: false, + // style: const TextStyle( + // fontWeight: FontWeight.bold, color: Colors.black87), + // decoration: loginTextFieldStyle().copyWith( + // suffixIcon: Visibility( + // visible: showSuffixIcon, + // child: _showPassword + // ? IconButton( + // icon: Icon(FontAwesome5.eye_slash, + // size: 24, + // color: Theme.of(context) + // .textTheme + // .displayLarge + // ?.color), + // onPressed: () { + // setState(() { + // _showPassword = false; + // }); + // }, + // ) + // : IconButton( + // onPressed: () { + // setState(() { + // _showPassword = true; + // }); + // }, + // icon: Icon(FontAwesome5.eye, + // size: 24, + // color: Theme.of(context) + // .textTheme + // .displayLarge + // ?.color)), + // ), + // prefixIcon: const Icon(Icons.lock), + // labelText: confirmPassword, + // hintText: enterConfirmPassword), + // obscureText: _showPassword ? true : false, + // ), + const SizedBox( + height: 15, + ), + SizedBox( + width: double.infinity, + height: blockSizeVertical * 6, + child: ElevatedButton( + style: secondaryBtnStyle( + second, Colors.transparent, primary), + onPressed: () { + if (_formKey.currentState! + .saveAndValidate()) { + final progress = + ProgressHUD.of(context); + progress?.showWithText( + 'Logging in...', + ); + context.read().add(UuidLogin(uuid: state.uuid,password: _formKey.currentState!.value['password'])); + // BlocProvider.of(context) + // .add(UserLogin( + // username: _formKey + // .currentState!.value['username'], + // password: _formKey + // .currentState!.value['password'], + // )); + } + }, + child: const Text(submit)), + ) + ], + ), ), - const SizedBox( - height: 15, - ), - // FormBuilderTextField( - // name: 'confirmPassword', - // validator: registerPasswordValidator, - // // initialValue: state.password, - // onChanged: (value) { - // value!.isEmpty - // ? setState(() { - // showSuffixIcon = false; - // }) - // : setState(() { - // showSuffixIcon = true; - // }); - // }, - // autofocus: false, - // style: const TextStyle( - // fontWeight: FontWeight.bold, color: Colors.black87), - // decoration: loginTextFieldStyle().copyWith( - // suffixIcon: Visibility( - // visible: showSuffixIcon, - // child: _showPassword - // ? IconButton( - // icon: Icon(FontAwesome5.eye_slash, - // size: 24, - // color: Theme.of(context) - // .textTheme - // .displayLarge - // ?.color), - // onPressed: () { - // setState(() { - // _showPassword = false; - // }); - // }, - // ) - // : IconButton( - // onPressed: () { - // setState(() { - // _showPassword = true; - // }); - // }, - // icon: Icon(FontAwesome5.eye, - // size: 24, - // color: Theme.of(context) - // .textTheme - // .displayLarge - // ?.color)), - // ), - // prefixIcon: const Icon(Icons.lock), - // labelText: confirmPassword, - // hintText: enterConfirmPassword), - // obscureText: _showPassword ? true : false, - // ), - const SizedBox( - height: 15, - ), - SizedBox( - width: double.infinity, - height: blockSizeVertical * 6, - child: ElevatedButton( - style: secondaryBtnStyle( - second, Colors.transparent, primary), - onPressed: () { - if (_formKey.currentState! - .saveAndValidate()) { - BlocProvider.of(context) - .add(UserLogin( - username: _formKey - .currentState!.value['username'], - password: _formKey - .currentState!.value['password'], - )); - } - }, - child: const Text(submit)), - ) - ], - ), + ), + ], ), - ), - ], - ), - ); - }, + ); + }if(state is UserError){ + return ErrorState(message: state.message,); + } + return Container(); + }, + ), + ), ), ), ); diff --git a/unit2/lib/sevices/login_service/auth_service.dart b/unit2/lib/sevices/login_service/auth_service.dart index c226746..82b47a5 100644 --- a/unit2/lib/sevices/login_service/auth_service.dart +++ b/unit2/lib/sevices/login_service/auth_service.dart @@ -58,4 +58,22 @@ class AuthService { } return userData!; } + Future qrLogin({String? uuid, String? password})async{ + Map body ={'uuid':uuid!,'password':password!}; + Map baseHeaders = { + 'Content-Type': 'application/json' + }; + String path = Url.instance.authentication(); + UserData? userData; + try{ + http.Response response = await Request.instance.postRequest(path: path,param: {},headers: baseHeaders,body: body); + if(response.statusCode == 200){ + Map data = jsonDecode(response.body); + userData = UserData.fromJson(data['data']); + } + }catch(e){ + throw (e.toString()); + } + return userData!; + } } diff --git a/unit2/lib/utils/app_router.dart b/unit2/lib/utils/app_router.dart index bfe2ba0..97750b2 100644 --- a/unit2/lib/utils/app_router.dart +++ b/unit2/lib/utils/app_router.dart @@ -1,11 +1,11 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:unit2/bloc/bloc/user_bloc.dart'; import 'package:unit2/screens/unit2/login/login.dart'; import 'package:unit2/utils/global_context.dart'; - +import '../screens/unit2/basic-info/basic-info.dart'; import '../screens/unit2/homepage.dart/components/drawer-screen.dart'; +import '../screens/unit2/login/qr_login.dart'; class AppRouter { Route onGenerateRoute(RouteSettings routeSettings) { @@ -18,11 +18,18 @@ class AppRouter { return const UniT2Login(); }); case '/module-screen': - BlocProvider.of( NavigationService.navigatorKey.currentContext!).add(LoadLoggedInUser()); + // BlocProvider.of( NavigationService.navigatorKey.currentContext!).add(LoadLoggedInUser()); return MaterialPageRoute(builder: (_) { return const DrawerScreen(); }); - + case '/basic-info': + return MaterialPageRoute(builder: (_){ + return const BasicInfo(); + }); + case '/qr-login': + return MaterialPageRoute(builder: (_){ + return const QRLogin(); + }); default: return MaterialPageRoute(builder: (context) { return Container(); From 9c5239d6b39aaa9e43d747340d85d6f63b155f35 Mon Sep 17 00:00:00 2001 From: rodolfobacuinjr Date: Mon, 23 Jan 2023 16:27:44 +0800 Subject: [PATCH 15/86] add logout function --- unit2/lib/screens/unit2/homepage.dart/components/menu.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit2/lib/screens/unit2/homepage.dart/components/menu.dart b/unit2/lib/screens/unit2/homepage.dart/components/menu.dart index 2a20438..8757065 100644 --- a/unit2/lib/screens/unit2/homepage.dart/components/menu.dart +++ b/unit2/lib/screens/unit2/homepage.dart/components/menu.dart @@ -20,7 +20,7 @@ Widget getTile( onTap: () async { if (title.toLowerCase() == "logout") { confirmAlert(context, () { - context.goNamed("login"); + Navigator.pushReplacementNamed (context,"/"); }); } else { Navigator.pushNamed(context, route); From 9714099af389b7bf9adecd663c01230cae179c4e Mon Sep 17 00:00:00 2001 From: rodolfobacuinjr Date: Mon, 23 Jan 2023 16:43:02 +0800 Subject: [PATCH 16/86] remove comment and hardcoded states --- unit2/lib/bloc/bloc/user_bloc.dart | 2 - unit2/lib/bloc/bloc/user_event.dart | 1 - unit2/lib/screens/sos/add_mobile.dart | 6 +- .../screens/unit2/basic-info/basic-info.dart | 3 - .../homepage.dart/components/dashboard.dart | 1 - .../homepage.dart/components/menu-screen.dart | 1 - .../unit2/homepage.dart/components/menu.dart | 2 - .../unit2/homepage.dart/module-screen.dart | 1 - .../login/components/update_required.dart | 8 +-- .../login/functions/press-again-to-exit.dart | 3 +- unit2/lib/screens/unit2/login/login.dart | 22 +------ unit2/lib/screens/unit2/login/qr_login.dart | 63 +------------------ unit2/pubspec.lock | 21 ------- unit2/pubspec.yaml | 2 - 14 files changed, 9 insertions(+), 127 deletions(-) diff --git a/unit2/lib/bloc/bloc/user_bloc.dart b/unit2/lib/bloc/bloc/user_bloc.dart index 7bd4eb5..00b0336 100644 --- a/unit2/lib/bloc/bloc/user_bloc.dart +++ b/unit2/lib/bloc/bloc/user_bloc.dart @@ -1,11 +1,9 @@ import 'package:barcode_scan2/barcode_scan2.dart'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; -import 'package:flutter/material.dart'; import 'package:unit2/model/login_data/user_info/user_data.dart'; import 'package:unit2/model/login_data/version_info.dart'; import 'package:unit2/sevices/login_service/auth_service.dart'; -import 'package:unit2/widgets/error_state.dart'; import '../../utils/scanner.dart'; diff --git a/unit2/lib/bloc/bloc/user_event.dart b/unit2/lib/bloc/bloc/user_event.dart index 6aca194..5f3b49c 100644 --- a/unit2/lib/bloc/bloc/user_event.dart +++ b/unit2/lib/bloc/bloc/user_event.dart @@ -20,7 +20,6 @@ class UserLogin extends UserEvent { } class LoadLoggedInUser extends UserEvent { - // final UserData? userData; LoadLoggedInUser(); } diff --git a/unit2/lib/screens/sos/add_mobile.dart b/unit2/lib/screens/sos/add_mobile.dart index 128aaa8..8abe496 100644 --- a/unit2/lib/screens/sos/add_mobile.dart +++ b/unit2/lib/screens/sos/add_mobile.dart @@ -1,9 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:fluttericon/font_awesome_icons.dart'; -import 'package:form_builder_validators/form_builder_validators.dart'; -import 'package:go_router/go_router.dart'; import 'package:unit2/theme-data.dart/text-styles.dart'; import 'package:unit2/utils/screen_info.dart'; import '../../theme-data.dart/btn-style.dart'; @@ -112,8 +109,7 @@ class AddMobile extends StatelessWidget { onPressed: () { if (_formKey.currentState! .saveAndValidate()) { - context.go(context - .namedLocation('request-sos')); + } // } diff --git a/unit2/lib/screens/unit2/basic-info/basic-info.dart b/unit2/lib/screens/unit2/basic-info/basic-info.dart index b6b7d3b..7374720 100644 --- a/unit2/lib/screens/unit2/basic-info/basic-info.dart +++ b/unit2/lib/screens/unit2/basic-info/basic-info.dart @@ -3,12 +3,10 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_svg/svg.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; -import 'package:go_router/go_router.dart'; import 'package:intl/intl.dart'; import 'package:qr_flutter/qr_flutter.dart'; import 'package:unit2/bloc/bloc/user_bloc.dart'; import 'package:unit2/model/login_data/user_info/user_data.dart'; -import 'package:unit2/test_data.dart'; import 'package:unit2/theme-data.dart/btn-style.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; @@ -161,7 +159,6 @@ class BuildInformation extends StatelessWidget { style: mainBtnStyle(third, Colors.transparent, Colors.white54), onPressed: () { - context.goNamed('signature'); }, icon: const Icon( FontAwesome5.signature, diff --git a/unit2/lib/screens/unit2/homepage.dart/components/dashboard.dart b/unit2/lib/screens/unit2/homepage.dart/components/dashboard.dart index c5c9ec9..b1c591a 100644 --- a/unit2/lib/screens/unit2/homepage.dart/components/dashboard.dart +++ b/unit2/lib/screens/unit2/homepage.dart/components/dashboard.dart @@ -46,7 +46,6 @@ class DashBoard extends StatelessWidget { children: roles[index].roles.map((role) { return GestureDetector( onTap: () { - print("Role is click"); }, child: Column(children: [ Icon( diff --git a/unit2/lib/screens/unit2/homepage.dart/components/menu-screen.dart b/unit2/lib/screens/unit2/homepage.dart/components/menu-screen.dart index c8d3ba4..7a059ba 100644 --- a/unit2/lib/screens/unit2/homepage.dart/components/menu-screen.dart +++ b/unit2/lib/screens/unit2/homepage.dart/components/menu-screen.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:fluttericon/web_symbols_icons.dart'; -import 'package:fluttericon/typicons_icons.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; import '../../../../model/login_data/user_info/user_data.dart'; import 'menu.dart'; diff --git a/unit2/lib/screens/unit2/homepage.dart/components/menu.dart b/unit2/lib/screens/unit2/homepage.dart/components/menu.dart index 8757065..5bf5982 100644 --- a/unit2/lib/screens/unit2/homepage.dart/components/menu.dart +++ b/unit2/lib/screens/unit2/homepage.dart/components/menu.dart @@ -1,6 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_zoom_drawer/flutter_zoom_drawer.dart'; -import 'package:go_router/go_router.dart'; import 'package:unit2/model/login_data/user_info/user_data.dart'; import 'package:unit2/utils/alerts.dart'; import '../../../../theme-data.dart/colors.dart'; diff --git a/unit2/lib/screens/unit2/homepage.dart/module-screen.dart b/unit2/lib/screens/unit2/homepage.dart/module-screen.dart index f741589..96ed319 100644 --- a/unit2/lib/screens/unit2/homepage.dart/module-screen.dart +++ b/unit2/lib/screens/unit2/homepage.dart/module-screen.dart @@ -5,7 +5,6 @@ import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:fluttericon/font_awesome_icons.dart'; import 'package:fluttericon/typicons_icons.dart'; import 'package:unit2/screens/unit2/homepage.dart/components/dashboard.dart'; -import 'package:unit2/test_data.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; diff --git a/unit2/lib/screens/unit2/login/components/update_required.dart b/unit2/lib/screens/unit2/login/components/update_required.dart index 9a0c4c4..b1b33d5 100644 --- a/unit2/lib/screens/unit2/login/components/update_required.dart +++ b/unit2/lib/screens/unit2/login/components/update_required.dart @@ -1,7 +1,5 @@ -import 'package:flutter/cupertino.dart'; + import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/container.dart'; -import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter_svg/svg.dart'; import 'package:unit2/theme-data.dart/btn-style.dart'; @@ -60,9 +58,9 @@ class _UpdateState extends State { color: Colors.black, ), children: [ - TextSpan( + const TextSpan( text: "mobileVersion", - style: const TextStyle( + style: TextStyle( color: primary, fontWeight: FontWeight.bold)), const TextSpan( text: " did not match with the latest version ", diff --git a/unit2/lib/screens/unit2/login/functions/press-again-to-exit.dart b/unit2/lib/screens/unit2/login/functions/press-again-to-exit.dart index d430674..0b1e58d 100644 --- a/unit2/lib/screens/unit2/login/functions/press-again-to-exit.dart +++ b/unit2/lib/screens/unit2/login/functions/press-again-to-exit.dart @@ -1,7 +1,6 @@ -import 'dart:io'; + import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:fluttertoast/fluttertoast.dart'; Future pressAgainToExit() async { diff --git a/unit2/lib/screens/unit2/login/login.dart b/unit2/lib/screens/unit2/login/login.dart index 93b3a03..3149ade 100644 --- a/unit2/lib/screens/unit2/login/login.dart +++ b/unit2/lib/screens/unit2/login/login.dart @@ -6,7 +6,6 @@ import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; -import 'package:go_router/go_router.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:unit2/bloc/bloc/user_bloc.dart'; @@ -205,26 +204,10 @@ class _UniT2LoginState extends State { BlocProvider.of(context) .add(UserLogin( username: - "rjvincentlopeplopez", - password: "shesthequ33n" - // username: _formKey - // .currentState! - // .value['username'], - // password: _formKey - // .currentState! - // .value[ - // 'password'] - + _formKey.currentState!.value['username'], + password:_formKey.currentState!.value['password'] )); } - - // if (_formKey.currentState.validate()) { - // _formKey.currentState.save(); - // BlocProvider.of(context) - // .add(UserWebLogin( - // password: password, - // username: username)); - // } }, ), ), @@ -275,7 +258,6 @@ class _UniT2LoginState extends State { style: mainBtnStyle(third, Colors.transparent, Colors.white38), onPressed: () { - context.goNamed('add-mobile'); }, label: const Text( requestSOS, diff --git a/unit2/lib/screens/unit2/login/qr_login.dart b/unit2/lib/screens/unit2/login/qr_login.dart index 59b9512..9d2f70b 100644 --- a/unit2/lib/screens/unit2/login/qr_login.dart +++ b/unit2/lib/screens/unit2/login/qr_login.dart @@ -7,9 +7,6 @@ import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_svg/svg.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; -import 'package:fluttericon/font_awesome_icons.dart'; -import 'package:form_builder_validators/form_builder_validators.dart'; -import 'package:go_router/go_router.dart'; import 'package:unit2/theme-data.dart/btn-style.dart'; import 'package:unit2/widgets/error_state.dart'; import 'package:unit2/widgets/wave.dart'; @@ -159,57 +156,7 @@ class _QRLoginState extends State { const SizedBox( height: 15, ), - // FormBuilderTextField( - // name: 'confirmPassword', - // validator: registerPasswordValidator, - // // initialValue: state.password, - // onChanged: (value) { - // value!.isEmpty - // ? setState(() { - // showSuffixIcon = false; - // }) - // : setState(() { - // showSuffixIcon = true; - // }); - // }, - // autofocus: false, - // style: const TextStyle( - // fontWeight: FontWeight.bold, color: Colors.black87), - // decoration: loginTextFieldStyle().copyWith( - // suffixIcon: Visibility( - // visible: showSuffixIcon, - // child: _showPassword - // ? IconButton( - // icon: Icon(FontAwesome5.eye_slash, - // size: 24, - // color: Theme.of(context) - // .textTheme - // .displayLarge - // ?.color), - // onPressed: () { - // setState(() { - // _showPassword = false; - // }); - // }, - // ) - // : IconButton( - // onPressed: () { - // setState(() { - // _showPassword = true; - // }); - // }, - // icon: Icon(FontAwesome5.eye, - // size: 24, - // color: Theme.of(context) - // .textTheme - // .displayLarge - // ?.color)), - // ), - // prefixIcon: const Icon(Icons.lock), - // labelText: confirmPassword, - // hintText: enterConfirmPassword), - // obscureText: _showPassword ? true : false, - // ), + const SizedBox( height: 15, ), @@ -228,13 +175,7 @@ class _QRLoginState extends State { 'Logging in...', ); context.read().add(UuidLogin(uuid: state.uuid,password: _formKey.currentState!.value['password'])); - // BlocProvider.of(context) - // .add(UserLogin( - // username: _formKey - // .currentState!.value['username'], - // password: _formKey - // .currentState!.value['password'], - // )); + } }, child: const Text(submit)), diff --git a/unit2/pubspec.lock b/unit2/pubspec.lock index 5ec7dd3..f387d29 100644 --- a/unit2/pubspec.lock +++ b/unit2/pubspec.lock @@ -127,13 +127,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.2" - cupertino_icons: - dependency: "direct main" - description: - name: cupertino_icons - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.5" date_time_picker: dependency: "direct main" description: @@ -322,13 +315,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.0" - go_router: - dependency: "direct main" - description: - name: go_router - url: "https://pub.dartlang.org" - source: hosted - version: "3.1.1" graphs: dependency: transitive description: @@ -385,13 +371,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.1" - logging: - dependency: transitive - description: - name: logging - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" matcher: dependency: transitive description: diff --git a/unit2/pubspec.yaml b/unit2/pubspec.yaml index fa8c45e..3d40e54 100644 --- a/unit2/pubspec.yaml +++ b/unit2/pubspec.yaml @@ -35,8 +35,6 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 - go_router: ^3.0.6 flutter_custom_clippers: ^2.0.0 flutter_svg: ^1.1.6 flutter_form_builder: ^7.7.0 From 9aea59e5a2ff984bbcde6155f9bc548da090678d Mon Sep 17 00:00:00 2001 From: rodolfobacuinjr Date: Tue, 24 Jan 2023 09:36:51 +0800 Subject: [PATCH 17/86] add package info plus and getApkVersion function --- unit2/lib/bloc/bloc/user_bloc.dart | 4 +++- unit2/lib/bloc/bloc/user_state.dart | 3 ++- .../unit2/login/components/update_required.dart | 12 +++++++----- .../unit2/login/functions/get_app_version.dart | 13 +++++++++++++ unit2/lib/screens/unit2/login/login.dart | 16 ++++++++-------- .../Flutter/GeneratedPluginRegistrant.swift | 2 ++ unit2/pubspec.lock | 14 ++++++++++++++ unit2/pubspec.yaml | 1 + 8 files changed, 50 insertions(+), 15 deletions(-) create mode 100644 unit2/lib/screens/unit2/login/functions/get_app_version.dart diff --git a/unit2/lib/bloc/bloc/user_bloc.dart b/unit2/lib/bloc/bloc/user_bloc.dart index 00b0336..71dd9eb 100644 --- a/unit2/lib/bloc/bloc/user_bloc.dart +++ b/unit2/lib/bloc/bloc/user_bloc.dart @@ -3,6 +3,7 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:unit2/model/login_data/user_info/user_data.dart'; import 'package:unit2/model/login_data/version_info.dart'; +import 'package:unit2/screens/unit2/login/functions/get_app_version.dart'; import 'package:unit2/sevices/login_service/auth_service.dart'; import '../../utils/scanner.dart'; @@ -21,7 +22,8 @@ class UserBloc extends Bloc { emit(SplashScreen()); VersionInfo versionInfo = await AuthService.instance.getVersionInfo(); _versionInfo = versionInfo; - emit(VersionLoaded(versionInfo: _versionInfo)); + String apkVersion = await getAppVersion(); + emit(VersionLoaded(versionInfo: _versionInfo,apkVersion: apkVersion)); } catch (e) { emit(UserError( message: e.toString(), diff --git a/unit2/lib/bloc/bloc/user_state.dart b/unit2/lib/bloc/bloc/user_state.dart index 2c5b56b..551c9e2 100644 --- a/unit2/lib/bloc/bloc/user_state.dart +++ b/unit2/lib/bloc/bloc/user_state.dart @@ -37,7 +37,8 @@ class UserLoggedIn extends UserState{ class VersionLoaded extends UserState { final VersionInfo? versionInfo; - VersionLoaded({this.versionInfo}); + final String? apkVersion; + VersionLoaded({this.versionInfo,this.apkVersion}); @override List get props => [versionInfo!]; } diff --git a/unit2/lib/screens/unit2/login/components/update_required.dart b/unit2/lib/screens/unit2/login/components/update_required.dart index b1b33d5..e0ca1e9 100644 --- a/unit2/lib/screens/unit2/login/components/update_required.dart +++ b/unit2/lib/screens/unit2/login/components/update_required.dart @@ -6,7 +6,9 @@ import 'package:unit2/theme-data.dart/btn-style.dart'; import '../../../../theme-data.dart/colors.dart'; class Update extends StatefulWidget { - const Update({super.key}); + final String apkVersion; + final String currenVersion; + const Update({super.key,required this.apkVersion,required this.currenVersion}); @override State createState() => _UpdateState(); @@ -58,16 +60,16 @@ class _UpdateState extends State { color: Colors.black, ), children: [ - const TextSpan( - text: "mobileVersion", - style: TextStyle( + TextSpan( + text: widget.apkVersion, + style: const TextStyle( color: primary, fontWeight: FontWeight.bold)), const TextSpan( text: " did not match with the latest version ", style: TextStyle(color: Colors.black)), TextSpan( text: - "widget.currentVersion.data.version".toString(), + widget.currenVersion, style: const TextStyle( color: primary, fontWeight: FontWeight.bold)), const TextSpan( diff --git a/unit2/lib/screens/unit2/login/functions/get_app_version.dart b/unit2/lib/screens/unit2/login/functions/get_app_version.dart new file mode 100644 index 0000000..7ebd85b --- /dev/null +++ b/unit2/lib/screens/unit2/login/functions/get_app_version.dart @@ -0,0 +1,13 @@ +import 'package:package_info_plus/package_info_plus.dart'; + +Future getAppVersion() async{ + String appVersion; + try{ +PackageInfo packageInfo = await PackageInfo.fromPlatform(); +appVersion = packageInfo.version; + }catch(e){ + throw(e.toString()); + } + return appVersion; + +} \ No newline at end of file diff --git a/unit2/lib/screens/unit2/login/login.dart b/unit2/lib/screens/unit2/login/login.dart index 3149ade..e3e7e3a 100644 --- a/unit2/lib/screens/unit2/login/login.dart +++ b/unit2/lib/screens/unit2/login/login.dart @@ -4,17 +4,12 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; -import 'package:fluttertoast/fluttertoast.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; - import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:unit2/bloc/bloc/user_bloc.dart'; -import 'package:unit2/model/login_data/user_info/user_data.dart'; -import 'package:unit2/screens/unit2/login/qr_login.dart'; +import 'package:unit2/screens/unit2/login/components/update_required.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/error_state.dart'; - -import '../../../utils/scanner.dart'; import '../../../widgets/splash_screen.dart'; import '../../../widgets/wave.dart'; import '../../../utils/global.dart'; @@ -53,10 +48,11 @@ class _UniT2LoginState extends State { }, builder: (context, state) { if (state is VersionLoaded) { return Builder(builder: (context) { - return SizedBox( + if(state.versionInfo!.version == state.apkVersion){ + return SizedBox( child: SingleChildScrollView( child: Stack( - alignment: Alignment.center, + alignment: Alignment.center, children: [ Positioned( bottom: 0, @@ -273,6 +269,10 @@ class _UniT2LoginState extends State { ), ), ); + }else{ + return Update(apkVersion: state.apkVersion!,currenVersion: state.versionInfo!.version!,); + } + }); } if (state is UserError) { diff --git a/unit2/macos/Flutter/GeneratedPluginRegistrant.swift b/unit2/macos/Flutter/GeneratedPluginRegistrant.swift index e9cf192..46d0540 100644 --- a/unit2/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/unit2/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,11 +5,13 @@ import FlutterMacOS import Foundation +import package_info_plus import path_provider_macos import shared_preferences_macos import sqflite func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) diff --git a/unit2/pubspec.lock b/unit2/pubspec.lock index f387d29..c278856 100644 --- a/unit2/pubspec.lock +++ b/unit2/pubspec.lock @@ -406,6 +406,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.2" + package_info_plus: + dependency: "direct main" + description: + name: package_info_plus + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" path: dependency: transitive description: diff --git a/unit2/pubspec.yaml b/unit2/pubspec.yaml index 3d40e54..720e69c 100644 --- a/unit2/pubspec.yaml +++ b/unit2/pubspec.yaml @@ -60,6 +60,7 @@ dependencies: system_info2: ^2.0.4 flutter_bloc: ^8.0.0 equatable: ^2.0.5 + package_info_plus: ^3.0.2 dev_dependencies: flutter_test: From 0868ae7ee022eec380c934ccf5adf7489825dca4 Mon Sep 17 00:00:00 2001 From: rodolfobacuinjr Date: Wed, 25 Jan 2023 09:18:30 +0800 Subject: [PATCH 18/86] Integrated download new version feature --- unit2/android/app/build.gradle | 1 + .../android/app/src/main/AndroidManifest.xml | 15 + .../app/FlutterMultiDexApplication.java | 25 ++ .../src/main/res/xml/file_provider_path.xml | 24 ++ unit2/lib/bloc/bloc/user_event.dart | 1 + .../unit2/login/components/showAlert.dart | 17 + .../login/components/update_required.dart | 298 +++++++++++++----- unit2/lib/utils/cpu_architecture.dart | 44 +++ unit2/lib/utils/request.dart | 66 ++-- unit2/lib/utils/urls.dart | 4 +- unit2/pubspec.lock | 79 ++++- unit2/pubspec.yaml | 5 + .../flutter/generated_plugin_registrant.cc | 3 + unit2/windows/flutter/generated_plugins.cmake | 1 + 14 files changed, 474 insertions(+), 109 deletions(-) create mode 100644 unit2/android/app/src/main/java/io/flutter/app/FlutterMultiDexApplication.java create mode 100644 unit2/android/app/src/main/res/xml/file_provider_path.xml create mode 100644 unit2/lib/screens/unit2/login/components/showAlert.dart create mode 100644 unit2/lib/utils/cpu_architecture.dart diff --git a/unit2/android/app/build.gradle b/unit2/android/app/build.gradle index 149c584..e896139 100644 --- a/unit2/android/app/build.gradle +++ b/unit2/android/app/build.gradle @@ -51,6 +51,7 @@ android { targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName + } buildTypes { diff --git a/unit2/android/app/src/main/AndroidManifest.xml b/unit2/android/app/src/main/AndroidManifest.xml index 1e8a920..390ee63 100644 --- a/unit2/android/app/src/main/AndroidManifest.xml +++ b/unit2/android/app/src/main/AndroidManifest.xml @@ -1,10 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/unit2/lib/bloc/bloc/user_event.dart b/unit2/lib/bloc/bloc/user_event.dart index 5f3b49c..cf529fb 100644 --- a/unit2/lib/bloc/bloc/user_event.dart +++ b/unit2/lib/bloc/bloc/user_event.dart @@ -27,6 +27,7 @@ class GetUuid extends UserEvent { GetUuid(); } + class LoadVersion extends UserEvent {} class UuidLogin extends UserEvent { diff --git a/unit2/lib/screens/unit2/login/components/showAlert.dart b/unit2/lib/screens/unit2/login/components/showAlert.dart new file mode 100644 index 0000000..615a19e --- /dev/null +++ b/unit2/lib/screens/unit2/login/components/showAlert.dart @@ -0,0 +1,17 @@ +import 'package:cool_alert/cool_alert.dart'; +import 'package:flutter/material.dart'; + +import '../../../../theme-data.dart/colors.dart'; + +showAlert(context,Function confirm) { + CoolAlert.show( + context: context, + type: CoolAlertType.error, + title: 'Download Failed!', + text: 'Make sure you have internet connection. Please try again.', + loopAnimation: false, + confirmBtnText: "Try again", + confirmBtnColor: primary, + onConfirmBtnTap: confirm(), + backgroundColor: Colors.black); +} diff --git a/unit2/lib/screens/unit2/login/components/update_required.dart b/unit2/lib/screens/unit2/login/components/update_required.dart index e0ca1e9..593169e 100644 --- a/unit2/lib/screens/unit2/login/components/update_required.dart +++ b/unit2/lib/screens/unit2/login/components/update_required.dart @@ -1,98 +1,248 @@ +import 'dart:async'; +import 'dart:io'; +import 'package:dio/dio.dart'; +import 'package:easy_app_installer/easy_app_installer.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:unit2/bloc/bloc/user_bloc.dart'; +import 'package:unit2/screens/unit2/login/components/showAlert.dart'; import 'package:unit2/theme-data.dart/btn-style.dart'; import '../../../../theme-data.dart/colors.dart'; +import '../../../../utils/cpu_architecture.dart'; +import '../../../../utils/text_container.dart'; +import '../../../../widgets/wave.dart'; class Update extends StatefulWidget { final String apkVersion; final String currenVersion; - const Update({super.key,required this.apkVersion,required this.currenVersion}); + const Update( + {super.key, required this.apkVersion, required this.currenVersion}); @override State createState() => _UpdateState(); } class _UpdateState extends State { + String progressRating = '0'; + bool downloading = false; + bool isDownloaded = false; + bool asyncCall = false; + Dio dio = Dio(); @override Widget build(BuildContext context) { return SafeArea( - child: SingleChildScrollView( - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 25), - height: MediaQuery.of(context).size.height, - alignment: Alignment.center, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SvgPicture.asset( - 'assets/svgs/download.svg', - height: 200.0, - width: 200.0, - allowDrawingOutsideViewBox: true, + child: Stack(children: [ + BlocBuilder( + builder: (context, state) { + if (state is VersionLoaded) { + return ProgressHUD( + child: SingleChildScrollView( + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 40, vertical: 25), + height: MediaQuery.of(context).size.height, + alignment: Alignment.center, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SvgPicture.asset( + 'assets/svgs/download.svg', + height: 200.0, + width: 200.0, + allowDrawingOutsideViewBox: true, + ), + const SizedBox( + height: 5, + ), + Text("UPDATE REQUIRED!", + textAlign: TextAlign.center, + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith(color: primary, fontSize: 28)), + ], + ), + const SizedBox( + height: 10, + ), + RichText( + textAlign: TextAlign.justify, + text: TextSpan( + text: 'Your app version ', + style: const TextStyle( + fontSize: 16, + color: Colors.black, + ), + children: [ + TextSpan( + text: widget.apkVersion, + style: const TextStyle( + color: primary, + fontWeight: FontWeight.bold)), + const TextSpan( + text: + " did not match with the latest version ", + style: TextStyle(color: Colors.black)), + TextSpan( + text: widget.currenVersion.toString(), + style: const TextStyle( + color: primary, + fontWeight: FontWeight.bold)), + const TextSpan( + text: + ". Download the app latest version to procceed using the uniT-App.", + style: TextStyle(color: Colors.black)), + ])), + const SizedBox( + height: 12.0, + ), + Container( + child: downloading + ? FittedBox( + child: Text( + 'Downloading application $progressRating%', + textAlign: TextAlign.center, + style: Theme.of(context) + .textTheme + .headlineSmall! + .copyWith(fontWeight: FontWeight.bold), + ), + ) + : Container(), + ), + SizedBox( + height: 60, + width: MediaQuery.of(context).size.width, + child: downloading + ? Container() + : ElevatedButton.icon( + icon: const Icon(Icons.download), + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () async { + final progress = ProgressHUD.of(context); + progress?.showWithText( + 'Please wait...', + ); + setState(() { + downloading = true; + progressRating = '0'; + }); + await openFile(); + }, + label: const Text( + "Download Latest App Version.")), + ), + ], + ), ), - const SizedBox( - height: 5, - ), - Text("UPDATE REQUIRED!", - textAlign: TextAlign.center, - style: Theme.of(context) - .textTheme - .displaySmall! - .copyWith(color: primary, fontSize: 28)), - ], - ), - const SizedBox( - height: 10, - ), - RichText( - textAlign: TextAlign.justify, - text: TextSpan( - text: 'Your app version ', - style: const TextStyle( - fontSize: 16, - color: Colors.black, - ), - children: [ - TextSpan( - text: widget.apkVersion, - style: const TextStyle( - color: primary, fontWeight: FontWeight.bold)), - const TextSpan( - text: " did not match with the latest version ", - style: TextStyle(color: Colors.black)), - TextSpan( - text: - widget.currenVersion, - style: const TextStyle( - color: primary, fontWeight: FontWeight.bold)), - const TextSpan( - text: - ". Download the app latest version to procceed using the uniT-App.", - style: TextStyle(color: Colors.black)), - ])), - const SizedBox( - height: 12.0, - ), - SizedBox( - height: 60, - width: MediaQuery.of(context).size.width, - child: ElevatedButton.icon( - icon: const Icon(Icons.download), - style: mainBtnStyle(primary, Colors.transparent, second), - onPressed: () async {}, - label: const Text("Download Latest App Version.")), - ), - ], - ), + ), + ); + } + if (state is UserError) { + showAlert(context, () { + setState(() { + downloading = false; + }); + }); + } + return Container(); + }, ), - ), + const Positioned(bottom: 0, child: WaveReverse(height: 80)) + ]), ); } + + Future openFile() async { + try { + final filePath = await downloadFile(); + await openAPK(filePath); + } catch (e) { + print(e.toString()); + } + } + + Future openAPK(String path) async { + PermissionStatus result = await Permission.storage.request(); + if (result.isGranted) { + await EasyAppInstaller.instance.installApk(path); + } + } + + Future downloadFile() async { + final progress = ProgressHUD.of(context); + final appStorage = await getApplicationDocumentsDirectory(); + try { + String url = await getCPUArchitecture(); + final response = await dio.download(url, '${appStorage.path}/uniT.apk', + deleteOnError: true, + options: Options( + receiveTimeout: 20000, + sendTimeout: 20000, + receiveDataWhenStatusError: true, + responseType: ResponseType.bytes, + followRedirects: false, + validateStatus: (status) { + return status! < 500; + }), onReceiveProgress: (recv, total) { + setState(() { + progressRating = ((recv / total) * 100).toStringAsFixed(0); + downloading = true; + }); + if (progressRating == '100') { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + setState(() { + downloading = false; + isDownloaded = true; + }); + } + }); + } on TimeoutException catch (_) { + progress!.dismiss(); + showAlert(context, () { + setState(() { + downloading = false; + }); + }); + throw TimeoutException(timeoutError); + } on SocketException catch (_) { + progress!.dismiss(); + showAlert(context, () { + setState(() { + downloading = false; + }); + }); + throw const SocketException(timeoutError); + } on DioError catch (_) { + progress!.dismiss(); + showAlert(context, () { + setState(() { + downloading = false; + }); + }); + throw TimeoutException(timeoutError); + } catch (_) { + progress!.dismiss(); + showAlert(context, () { + setState(() { + downloading = false; + }); + }); + throw TimeoutException(timeoutError); + } + return '${appStorage.path}/uniT.apk'; + } } diff --git a/unit2/lib/utils/cpu_architecture.dart b/unit2/lib/utils/cpu_architecture.dart new file mode 100644 index 0000000..d0245da --- /dev/null +++ b/unit2/lib/utils/cpu_architecture.dart @@ -0,0 +1,44 @@ + import 'dart:async'; +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:unit2/model/login_data/version_info.dart'; +import 'package:system_info2/system_info2.dart'; +Future getCPUArchitecture() async { + String downloadURL = ""; + String cpuArchitecture = SysInfo.kernelArchitecture; + VersionInfo? apkVersion; + Dio dio = Dio(); + const String apkUrl = 'https://unitylb1.agusandelnorte.gov.ph/unit2/api/sys/apk_version/latest/'; + try { + Response response = await dio.get(apkUrl, + options: Options( + receiveTimeout: 20000, + receiveDataWhenStatusError: true, + responseType: ResponseType.json, + followRedirects: false, + validateStatus: (status) { + return status! < 500; + })); + if (response.statusCode == 200) { + apkVersion = VersionInfo.fromJson(response.data['data']); + if (cpuArchitecture.toUpperCase() == 'ARM' || + cpuArchitecture.toUpperCase() == 'MIPS') { + downloadURL = apkVersion.armeabiv7aDownloadUrl!; + } else if (cpuArchitecture.toUpperCase() == 'I686' || + cpuArchitecture.toUpperCase() == 'UNKNOWN' || + cpuArchitecture.toUpperCase() == 'X86_64') { + downloadURL = apkVersion.x8664DownloadUrl!; + } else if (cpuArchitecture.toUpperCase() == 'AARCH64') { + downloadURL = apkVersion.arm64v8aDownloadUrl!; + } + } + } on TimeoutException catch (_) { + throw TimeoutException("Connection timeout"); + } on SocketException catch (_) { + throw const SocketException("Connection timeout"); + } on DioError catch (_) { + throw DioErrorType.connectTimeout; + } + return downloadURL; + } \ No newline at end of file diff --git a/unit2/lib/utils/request.dart b/unit2/lib/utils/request.dart index b956687..d698418 100644 --- a/unit2/lib/utils/request.dart +++ b/unit2/lib/utils/request.dart @@ -19,7 +19,7 @@ class Request { Map? param}) async { Response response; try { - response = await get(Uri.http(host, path!, param), headers: headers) + response = await get(Uri.https(host, path!, param), headers: headers) .timeout(Duration(seconds: requestTimeout)); } on TimeoutException catch (_) { Fluttertoast.showToast( @@ -60,39 +60,39 @@ class Request { Map? body, Map? param}) async { Response response; - // try { - response = await post(Uri.http(host, path!, param), headers: headers,body: jsonEncode(body)) + try { + response = await post(Uri.https(host, path!, param), headers: headers,body: jsonEncode(body)) .timeout(Duration(seconds: requestTimeout)); - // } on TimeoutException catch (_) { - // Fluttertoast.showToast( - // msg: timeoutError, - // toastLength: Toast.LENGTH_LONG, - // gravity: ToastGravity.BOTTOM, - // backgroundColor: Colors.black, - // ); - // throw (timeoutError); - // } on SocketException catch (_) { - // Fluttertoast.showToast( - // msg: timeoutError, - // toastLength: Toast.LENGTH_LONG, - // gravity: ToastGravity.BOTTOM, - // backgroundColor: Colors.black, - // ); - // throw (timeoutError); - // } on FormatException catch (_) { - // throw const FormatException(formatError); - // } on HttpException catch (_) { - // throw const HttpException(httpError); - // } on Error catch (e) { - // debugPrint("post request error: $e"); - // Fluttertoast.showToast( - // msg: onError, - // toastLength: Toast.LENGTH_LONG, - // gravity: ToastGravity.BOTTOM, - // backgroundColor: Colors.black, - // ); - // throw (e.toString()); - // } + } on TimeoutException catch (_) { + Fluttertoast.showToast( + msg: timeoutError, + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.black, + ); + throw (timeoutError); + } on SocketException catch (_) { + Fluttertoast.showToast( + msg: timeoutError, + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.black, + ); + throw (timeoutError); + } on FormatException catch (_) { + throw const FormatException(formatError); + } on HttpException catch (_) { + throw const HttpException(httpError); + } on Error catch (e) { + debugPrint("post request error: $e"); + Fluttertoast.showToast( + msg: onError, + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.black, + ); + throw (e.toString()); + } return response; } } diff --git a/unit2/lib/utils/urls.dart b/unit2/lib/utils/urls.dart index f44cc4d..438d113 100644 --- a/unit2/lib/utils/urls.dart +++ b/unit2/lib/utils/urls.dart @@ -3,8 +3,10 @@ class Url{ static Url get instance => _instance; String host(){ - return '192.168.10.219:3000'; + // return '192.168.10.219:3000'; + return 'agusandelnorte.gov.ph'; } + String authentication(){ return '/api/account/auth/login/'; diff --git a/unit2/pubspec.lock b/unit2/pubspec.lock index c278856..e894f88 100644 --- a/unit2/pubspec.lock +++ b/unit2/pubspec.lock @@ -120,6 +120,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.1.0+1" + cool_alert: + dependency: "direct main" + description: + name: cool_alert + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" crypto: dependency: transitive description: @@ -148,6 +155,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.0" + dio: + dependency: "direct main" + description: + name: dio + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.6" + easy_app_installer: + dependency: "direct main" + description: + name: easy_app_installer + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" equatable: dependency: "direct main" description: @@ -190,6 +211,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.1" + flare_flutter: + dependency: transitive + description: + name: flare_flutter + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" flutter: dependency: "direct main" description: flutter @@ -242,6 +270,13 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.7" flutter_progress_hud: dependency: "direct main" description: @@ -371,6 +406,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.1" + lottie: + dependency: transitive + description: + name: lottie + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.3" matcher: dependency: transitive description: @@ -442,7 +484,7 @@ packages: source: hosted version: "1.0.1" path_provider: - dependency: transitive + dependency: "direct main" description: name: path_provider url: "https://pub.dartlang.org" @@ -497,6 +539,41 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.11.1" + permission_handler: + dependency: "direct main" + description: + name: permission_handler + url: "https://pub.dartlang.org" + source: hosted + version: "10.2.0" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + url: "https://pub.dartlang.org" + source: hosted + version: "10.2.0" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + url: "https://pub.dartlang.org" + source: hosted + version: "9.0.7" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "3.9.0" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.2" petitparser: dependency: transitive description: diff --git a/unit2/pubspec.yaml b/unit2/pubspec.yaml index 720e69c..8f5fcac 100644 --- a/unit2/pubspec.yaml +++ b/unit2/pubspec.yaml @@ -61,6 +61,11 @@ dependencies: flutter_bloc: ^8.0.0 equatable: ^2.0.5 package_info_plus: ^3.0.2 + easy_app_installer: ^1.0.0 + path_provider: ^2.0.11 + dio: ^4.0.6 + cool_alert: ^1.1.0 + permission_handler: ^10.2.0 dev_dependencies: flutter_test: diff --git a/unit2/windows/flutter/generated_plugin_registrant.cc b/unit2/windows/flutter/generated_plugin_registrant.cc index 8b6d468..48de52b 100644 --- a/unit2/windows/flutter/generated_plugin_registrant.cc +++ b/unit2/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,9 @@ #include "generated_plugin_registrant.h" +#include void RegisterPlugins(flutter::PluginRegistry* registry) { + PermissionHandlerWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); } diff --git a/unit2/windows/flutter/generated_plugins.cmake b/unit2/windows/flutter/generated_plugins.cmake index b93c4c3..0e69e40 100644 --- a/unit2/windows/flutter/generated_plugins.cmake +++ b/unit2/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + permission_handler_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST From 142bd6e6a24816b8fa8723f63c77a63658aabfaf Mon Sep 17 00:00:00 2001 From: rodolfobacuinjr Date: Wed, 25 Jan 2023 13:13:21 +0800 Subject: [PATCH 19/86] move git folder into app directory --- unit2/.gitignore => .gitignore | 0 unit2/.metadata => .metadata | 0 README.md | 16 + ...ysis_options.yaml => analysis_options.yaml | 0 {unit2/android => android}/.gitignore | 0 {unit2/android => android}/app/build.gradle | 0 .../app/src/debug/AndroidManifest.xml | 0 .../app/src/main/AndroidManifest.xml | 0 .../app/FlutterMultiDexApplication.java | 0 .../kotlin/com/example/unit2/MainActivity.kt | 0 .../res/drawable-v21/launch_background.xml | 0 .../main/res/drawable/launch_background.xml | 0 .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin .../app/src/main/res/values-night/styles.xml | 0 .../app/src/main/res/values/styles.xml | 0 .../src/main/res/xml/file_provider_path.xml | 0 .../app/src/profile/AndroidManifest.xml | 0 {unit2/android => android}/build.gradle | 0 {unit2/android => android}/gradle.properties | 0 .../gradle/wrapper/gradle-wrapper.properties | 0 {unit2/android => android}/settings.gradle | 0 .../fonts/LexendDeca-Bold.ttf | Bin .../fonts/LexendDeca-Light.ttf | Bin .../fonts/LexendDeca-Medium.ttf | Bin .../fonts/LexendDeca-Regular.ttf | Bin .../fonts/LexendDeca-SemiBold.ttf | Bin .../fonts/LexendDeca-VariableFont_wght.ttf | Bin {unit2/assets => assets}/pngs/bg.png | Bin {unit2/assets => assets}/pngs/qr-scan.png | Bin {unit2/assets => assets}/svgs/add_mobile.svg | 0 {unit2/assets => assets}/svgs/assign.svg | 0 {unit2/assets => assets}/svgs/download.svg | 0 {unit2/assets => assets}/svgs/emergency.png | Bin {unit2/assets => assets}/svgs/empty.svg | 0 {unit2/assets => assets}/svgs/error.svg | 0 {unit2/assets => assets}/svgs/female.svg | 0 {unit2/assets => assets}/svgs/logo.svg | 0 {unit2/assets => assets}/svgs/male.svg | 0 {unit2/assets => assets}/svgs/no_module.svg | 0 {unit2/assets => assets}/svgs/request_sos.svg | 0 {unit2/assets => assets}/svgs/settings.svg | 0 {unit2/assets => assets}/svgs/sos.svg | 0 {unit2/assets => assets}/svgs/switch.svg | 0 {unit2/assets => assets}/svgs/timeout.svg | 0 {unit2/assets => assets}/svgs/welcome.svg | 0 {unit2/assets => assets}/svgs/workspace.svg | 0 {unit2/ios => ios}/.gitignore | 0 .../Flutter/AppFrameworkInfo.plist | 0 {unit2/ios => ios}/Flutter/Debug.xcconfig | 0 {unit2/ios => ios}/Flutter/Release.xcconfig | 0 .../Runner.xcodeproj/project.pbxproj | 0 .../contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcshareddata/WorkspaceSettings.xcsettings | 0 .../xcshareddata/xcschemes/Runner.xcscheme | 0 .../contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcshareddata/WorkspaceSettings.xcsettings | 0 {unit2/ios => ios}/Runner/AppDelegate.swift | 0 .../AppIcon.appiconset/Contents.json | 0 .../Icon-App-1024x1024@1x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin .../Icon-App-83.5x83.5@2x.png | Bin .../LaunchImage.imageset/Contents.json | 0 .../LaunchImage.imageset/LaunchImage.png | Bin .../LaunchImage.imageset/LaunchImage@2x.png | Bin .../LaunchImage.imageset/LaunchImage@3x.png | Bin .../LaunchImage.imageset/README.md | 0 .../Runner/Base.lproj/LaunchScreen.storyboard | 0 .../Runner/Base.lproj/Main.storyboard | 0 {unit2/ios => ios}/Runner/Info.plist | 0 .../Runner/Runner-Bridging-Header.h | 0 {unit2/lib => lib}/bloc/bloc/user_bloc.dart | 0 {unit2/lib => lib}/bloc/bloc/user_event.dart | 0 {unit2/lib => lib}/bloc/bloc/user_state.dart | 0 {unit2/lib => lib}/main.dart | 0 .../login_data/employee_info/department.dart | 0 .../employee_info/employee_info.dart | 0 .../model/login_data/employee_info/head.dart | 0 .../login_data/employee_info/office.dart | 0 .../employee_info/position_class.dart | 0 .../login_data/user_info/assigned_area.dart | 0 .../login_data/user_info/login_user.dart | 0 .../model/login_data/user_info/module.dart | 0 .../model/login_data/user_info/role.dart | 0 .../model/login_data/user_info/user_data.dart | 0 .../model/login_data/version_info.dart | 0 .../docsms/components/doc_info_tile.dart | 0 .../screens/docsms/request_receipt.dart | 0 .../lib => lib}/screens/sos/add_mobile.dart | 0 .../screens/sos/components/mobile.dart | 0 .../lib => lib}/screens/sos/request_sos.dart | 0 .../lib => lib}/screens/sos/sos_received.dart | 0 .../screens/unit2/basic-info/basic-info.dart | 0 .../basic-info/components/cover-image.dart | 0 .../homepage.dart/components/dashboard.dart | 0 .../components/drawer-screen.dart | 0 .../components/empty_module.dart | 0 .../homepage.dart/components/menu-screen.dart | 0 .../unit2/homepage.dart/components/menu.dart | 0 .../unit2/homepage.dart/module-screen.dart | 0 .../login/components/login-via-qr-label.dart | 0 .../unit2/login/components/showAlert.dart | 0 .../login/components/update_required.dart | 0 .../login/functions/get_app_version.dart | 0 .../login/functions/press-again-to-exit.dart | 0 lib/screens/unit2/login/login.dart | 314 ++++++++++++++++++ .../screens/unit2/login/qr_login.dart | 0 .../components/custom_switch.dart | 0 .../components/save_settings.dart | 0 .../roles/qr_code_scanner.dart/scan.dart | 0 .../qr_code_scanner.dart/settings_screen.dart | 0 .../components/add.dart | 0 .../components/request_qr.dart | 0 .../components/sync.dart | 0 .../components/view.dart | 0 .../roles/registration_in_charge/home.dart | 0 .../unit2/signature/signature_pad.dart | 0 .../sevices/login_service/auth_service.dart | 0 {unit2/lib => lib}/test_data.dart | 0 .../theme-data.dart/btn-style.dart | 0 .../lib => lib}/theme-data.dart/colors.dart | 0 .../theme-data.dart/form-style.dart | 0 .../theme-data.dart/text-styles.dart | 0 {unit2/lib => lib}/utils/alerts.dart | 0 {unit2/lib => lib}/utils/app_router.dart | 0 .../lib => lib}/utils/cpu_architecture.dart | 0 {unit2/lib => lib}/utils/global.dart | 0 {unit2/lib => lib}/utils/global_context.dart | 0 {unit2/lib => lib}/utils/request.dart | 0 {unit2/lib => lib}/utils/router.dart | 0 {unit2/lib => lib}/utils/scanner.dart | 0 {unit2/lib => lib}/utils/screen_info.dart | 0 {unit2/lib => lib}/utils/text_container.dart | 0 {unit2/lib => lib}/utils/urls.dart | 0 {unit2/lib => lib}/utils/validators.dart | 0 .../lib => lib}/widgets/costum_divider.dart | 0 {unit2/lib => lib}/widgets/error_state.dart | 0 {unit2/lib => lib}/widgets/label.dart | 0 {unit2/lib => lib}/widgets/splash_screen.dart | 0 {unit2/lib => lib}/widgets/text_icon.dart | 0 {unit2/lib => lib}/widgets/wave.dart | 0 {unit2/linux => linux}/.gitignore | 0 {unit2/linux => linux}/CMakeLists.txt | 0 {unit2/linux => linux}/flutter/CMakeLists.txt | 0 .../flutter/generated_plugin_registrant.cc | 0 .../flutter/generated_plugin_registrant.h | 0 .../flutter/generated_plugins.cmake | 0 {unit2/linux => linux}/main.cc | 0 {unit2/linux => linux}/my_application.cc | 0 {unit2/linux => linux}/my_application.h | 0 {unit2/macos => macos}/.gitignore | 0 .../Flutter/Flutter-Debug.xcconfig | 0 .../Flutter/Flutter-Release.xcconfig | 0 .../Flutter/GeneratedPluginRegistrant.swift | 0 .../Runner.xcodeproj/project.pbxproj | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcshareddata/xcschemes/Runner.xcscheme | 0 .../contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../macos => macos}/Runner/AppDelegate.swift | 0 .../AppIcon.appiconset/Contents.json | 0 .../AppIcon.appiconset/app_icon_1024.png | Bin .../AppIcon.appiconset/app_icon_128.png | Bin .../AppIcon.appiconset/app_icon_16.png | Bin .../AppIcon.appiconset/app_icon_256.png | Bin .../AppIcon.appiconset/app_icon_32.png | Bin .../AppIcon.appiconset/app_icon_512.png | Bin .../AppIcon.appiconset/app_icon_64.png | Bin .../Runner/Base.lproj/MainMenu.xib | 0 .../Runner/Configs/AppInfo.xcconfig | 0 .../Runner/Configs/Debug.xcconfig | 0 .../Runner/Configs/Release.xcconfig | 0 .../Runner/Configs/Warnings.xcconfig | 0 .../Runner/DebugProfile.entitlements | 0 {unit2/macos => macos}/Runner/Info.plist | 0 .../Runner/MainFlutterWindow.swift | 0 .../Runner/Release.entitlements | 0 unit2/notes.txt => notes.txt | 0 unit2/pubspec.lock => pubspec.lock | 0 unit2/pubspec.yaml => pubspec.yaml | 0 {unit2/test => test}/widget_test.dart | 0 unit2/README.md | 16 - unit2/lib/screens/unit2/login/login.dart | 292 ---------------- {unit2/web => web}/favicon.png | Bin {unit2/web => web}/icons/Icon-192.png | Bin {unit2/web => web}/icons/Icon-512.png | Bin .../web => web}/icons/Icon-maskable-192.png | Bin .../web => web}/icons/Icon-maskable-512.png | Bin {unit2/web => web}/index.html | 0 {unit2/web => web}/manifest.json | 0 {unit2/windows => windows}/.gitignore | 0 {unit2/windows => windows}/CMakeLists.txt | 0 .../flutter/CMakeLists.txt | 0 .../flutter/generated_plugin_registrant.cc | 0 .../flutter/generated_plugin_registrant.h | 0 .../flutter/generated_plugins.cmake | 0 .../windows => windows}/runner/CMakeLists.txt | 0 {unit2/windows => windows}/runner/Runner.rc | 0 .../runner/flutter_window.cpp | 0 .../runner/flutter_window.h | 0 {unit2/windows => windows}/runner/main.cpp | 0 {unit2/windows => windows}/runner/resource.h | 0 .../runner/resources/app_icon.ico | Bin .../runner/runner.exe.manifest | 0 {unit2/windows => windows}/runner/utils.cpp | 0 {unit2/windows => windows}/runner/utils.h | 0 .../runner/win32_window.cpp | 0 .../windows => windows}/runner/win32_window.h | 0 225 files changed, 330 insertions(+), 308 deletions(-) rename unit2/.gitignore => .gitignore (100%) rename unit2/.metadata => .metadata (100%) rename unit2/analysis_options.yaml => analysis_options.yaml (100%) rename {unit2/android => android}/.gitignore (100%) rename {unit2/android => android}/app/build.gradle (100%) rename {unit2/android => android}/app/src/debug/AndroidManifest.xml (100%) rename {unit2/android => android}/app/src/main/AndroidManifest.xml (100%) rename {unit2/android => android}/app/src/main/java/io/flutter/app/FlutterMultiDexApplication.java (100%) rename {unit2/android => android}/app/src/main/kotlin/com/example/unit2/MainActivity.kt (100%) rename {unit2/android => android}/app/src/main/res/drawable-v21/launch_background.xml (100%) rename {unit2/android => android}/app/src/main/res/drawable/launch_background.xml (100%) rename {unit2/android => android}/app/src/main/res/mipmap-hdpi/ic_launcher.png (100%) rename {unit2/android => android}/app/src/main/res/mipmap-mdpi/ic_launcher.png (100%) rename {unit2/android => android}/app/src/main/res/mipmap-xhdpi/ic_launcher.png (100%) rename {unit2/android => android}/app/src/main/res/mipmap-xxhdpi/ic_launcher.png (100%) rename {unit2/android => android}/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png (100%) rename {unit2/android => android}/app/src/main/res/values-night/styles.xml (100%) rename {unit2/android => android}/app/src/main/res/values/styles.xml (100%) rename {unit2/android => android}/app/src/main/res/xml/file_provider_path.xml (100%) rename {unit2/android => android}/app/src/profile/AndroidManifest.xml (100%) rename {unit2/android => android}/build.gradle (100%) rename {unit2/android => android}/gradle.properties (100%) rename {unit2/android => android}/gradle/wrapper/gradle-wrapper.properties (100%) rename {unit2/android => android}/settings.gradle (100%) rename {unit2/assets => assets}/fonts/LexendDeca-Bold.ttf (100%) rename {unit2/assets => assets}/fonts/LexendDeca-Light.ttf (100%) rename {unit2/assets => assets}/fonts/LexendDeca-Medium.ttf (100%) rename {unit2/assets => assets}/fonts/LexendDeca-Regular.ttf (100%) rename {unit2/assets => assets}/fonts/LexendDeca-SemiBold.ttf (100%) rename {unit2/assets => assets}/fonts/LexendDeca-VariableFont_wght.ttf (100%) rename {unit2/assets => assets}/pngs/bg.png (100%) rename {unit2/assets => assets}/pngs/qr-scan.png (100%) rename {unit2/assets => assets}/svgs/add_mobile.svg (100%) rename {unit2/assets => assets}/svgs/assign.svg (100%) rename {unit2/assets => assets}/svgs/download.svg (100%) rename {unit2/assets => assets}/svgs/emergency.png (100%) rename {unit2/assets => assets}/svgs/empty.svg (100%) rename {unit2/assets => assets}/svgs/error.svg (100%) rename {unit2/assets => assets}/svgs/female.svg (100%) rename {unit2/assets => assets}/svgs/logo.svg (100%) rename {unit2/assets => assets}/svgs/male.svg (100%) rename {unit2/assets => assets}/svgs/no_module.svg (100%) rename {unit2/assets => assets}/svgs/request_sos.svg (100%) rename {unit2/assets => assets}/svgs/settings.svg (100%) rename {unit2/assets => assets}/svgs/sos.svg (100%) rename {unit2/assets => assets}/svgs/switch.svg (100%) rename {unit2/assets => assets}/svgs/timeout.svg (100%) rename {unit2/assets => assets}/svgs/welcome.svg (100%) rename {unit2/assets => assets}/svgs/workspace.svg (100%) rename {unit2/ios => ios}/.gitignore (100%) rename {unit2/ios => ios}/Flutter/AppFrameworkInfo.plist (100%) rename {unit2/ios => ios}/Flutter/Debug.xcconfig (100%) rename {unit2/ios => ios}/Flutter/Release.xcconfig (100%) rename {unit2/ios => ios}/Runner.xcodeproj/project.pbxproj (100%) rename {unit2/ios => ios}/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata (100%) rename {unit2/ios => ios}/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename {unit2/ios => ios}/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings (100%) rename {unit2/ios => ios}/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme (100%) rename {unit2/ios => ios}/Runner.xcworkspace/contents.xcworkspacedata (100%) rename {unit2/ios => ios}/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename {unit2/ios => ios}/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings (100%) rename {unit2/ios => ios}/Runner/AppDelegate.swift (100%) rename {unit2/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename {unit2/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png (100%) rename {unit2/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png (100%) rename {unit2/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png (100%) rename {unit2/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png (100%) rename {unit2/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png (100%) rename {unit2/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png (100%) rename {unit2/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png (100%) rename {unit2/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png (100%) rename {unit2/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png (100%) rename {unit2/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png (100%) rename {unit2/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png (100%) rename {unit2/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png (100%) rename {unit2/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png (100%) rename {unit2/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png (100%) rename {unit2/ios => ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png (100%) rename {unit2/ios => ios}/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json (100%) rename {unit2/ios => ios}/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png (100%) rename {unit2/ios => ios}/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png (100%) rename {unit2/ios => ios}/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png (100%) rename {unit2/ios => ios}/Runner/Assets.xcassets/LaunchImage.imageset/README.md (100%) rename {unit2/ios => ios}/Runner/Base.lproj/LaunchScreen.storyboard (100%) rename {unit2/ios => ios}/Runner/Base.lproj/Main.storyboard (100%) rename {unit2/ios => ios}/Runner/Info.plist (100%) rename {unit2/ios => ios}/Runner/Runner-Bridging-Header.h (100%) rename {unit2/lib => lib}/bloc/bloc/user_bloc.dart (100%) rename {unit2/lib => lib}/bloc/bloc/user_event.dart (100%) rename {unit2/lib => lib}/bloc/bloc/user_state.dart (100%) rename {unit2/lib => lib}/main.dart (100%) rename {unit2/lib => lib}/model/login_data/employee_info/department.dart (100%) rename {unit2/lib => lib}/model/login_data/employee_info/employee_info.dart (100%) rename {unit2/lib => lib}/model/login_data/employee_info/head.dart (100%) rename {unit2/lib => lib}/model/login_data/employee_info/office.dart (100%) rename {unit2/lib => lib}/model/login_data/employee_info/position_class.dart (100%) rename {unit2/lib => lib}/model/login_data/user_info/assigned_area.dart (100%) rename {unit2/lib => lib}/model/login_data/user_info/login_user.dart (100%) rename {unit2/lib => lib}/model/login_data/user_info/module.dart (100%) rename {unit2/lib => lib}/model/login_data/user_info/role.dart (100%) rename {unit2/lib => lib}/model/login_data/user_info/user_data.dart (100%) rename {unit2/lib => lib}/model/login_data/version_info.dart (100%) rename {unit2/lib => lib}/screens/docsms/components/doc_info_tile.dart (100%) rename {unit2/lib => lib}/screens/docsms/request_receipt.dart (100%) rename {unit2/lib => lib}/screens/sos/add_mobile.dart (100%) rename {unit2/lib => lib}/screens/sos/components/mobile.dart (100%) rename {unit2/lib => lib}/screens/sos/request_sos.dart (100%) rename {unit2/lib => lib}/screens/sos/sos_received.dart (100%) rename {unit2/lib => lib}/screens/unit2/basic-info/basic-info.dart (100%) rename {unit2/lib => lib}/screens/unit2/basic-info/components/cover-image.dart (100%) rename {unit2/lib => lib}/screens/unit2/homepage.dart/components/dashboard.dart (100%) rename {unit2/lib => lib}/screens/unit2/homepage.dart/components/drawer-screen.dart (100%) rename {unit2/lib => lib}/screens/unit2/homepage.dart/components/empty_module.dart (100%) rename {unit2/lib => lib}/screens/unit2/homepage.dart/components/menu-screen.dart (100%) rename {unit2/lib => lib}/screens/unit2/homepage.dart/components/menu.dart (100%) rename {unit2/lib => lib}/screens/unit2/homepage.dart/module-screen.dart (100%) rename {unit2/lib => lib}/screens/unit2/login/components/login-via-qr-label.dart (100%) rename {unit2/lib => lib}/screens/unit2/login/components/showAlert.dart (100%) rename {unit2/lib => lib}/screens/unit2/login/components/update_required.dart (100%) rename {unit2/lib => lib}/screens/unit2/login/functions/get_app_version.dart (100%) rename {unit2/lib => lib}/screens/unit2/login/functions/press-again-to-exit.dart (100%) create mode 100644 lib/screens/unit2/login/login.dart rename {unit2/lib => lib}/screens/unit2/login/qr_login.dart (100%) rename {unit2/lib => lib}/screens/unit2/roles/qr_code_scanner.dart/components/custom_switch.dart (100%) rename {unit2/lib => lib}/screens/unit2/roles/qr_code_scanner.dart/components/save_settings.dart (100%) rename {unit2/lib => lib}/screens/unit2/roles/qr_code_scanner.dart/scan.dart (100%) rename {unit2/lib => lib}/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart (100%) rename {unit2/lib => lib}/screens/unit2/roles/registration_in_charge/components/add.dart (100%) rename {unit2/lib => lib}/screens/unit2/roles/registration_in_charge/components/request_qr.dart (100%) rename {unit2/lib => lib}/screens/unit2/roles/registration_in_charge/components/sync.dart (100%) rename {unit2/lib => lib}/screens/unit2/roles/registration_in_charge/components/view.dart (100%) rename {unit2/lib => lib}/screens/unit2/roles/registration_in_charge/home.dart (100%) rename {unit2/lib => lib}/screens/unit2/signature/signature_pad.dart (100%) rename {unit2/lib => lib}/sevices/login_service/auth_service.dart (100%) rename {unit2/lib => lib}/test_data.dart (100%) rename {unit2/lib => lib}/theme-data.dart/btn-style.dart (100%) rename {unit2/lib => lib}/theme-data.dart/colors.dart (100%) rename {unit2/lib => lib}/theme-data.dart/form-style.dart (100%) rename {unit2/lib => lib}/theme-data.dart/text-styles.dart (100%) rename {unit2/lib => lib}/utils/alerts.dart (100%) rename {unit2/lib => lib}/utils/app_router.dart (100%) rename {unit2/lib => lib}/utils/cpu_architecture.dart (100%) rename {unit2/lib => lib}/utils/global.dart (100%) rename {unit2/lib => lib}/utils/global_context.dart (100%) rename {unit2/lib => lib}/utils/request.dart (100%) rename {unit2/lib => lib}/utils/router.dart (100%) rename {unit2/lib => lib}/utils/scanner.dart (100%) rename {unit2/lib => lib}/utils/screen_info.dart (100%) rename {unit2/lib => lib}/utils/text_container.dart (100%) rename {unit2/lib => lib}/utils/urls.dart (100%) rename {unit2/lib => lib}/utils/validators.dart (100%) rename {unit2/lib => lib}/widgets/costum_divider.dart (100%) rename {unit2/lib => lib}/widgets/error_state.dart (100%) rename {unit2/lib => lib}/widgets/label.dart (100%) rename {unit2/lib => lib}/widgets/splash_screen.dart (100%) rename {unit2/lib => lib}/widgets/text_icon.dart (100%) rename {unit2/lib => lib}/widgets/wave.dart (100%) rename {unit2/linux => linux}/.gitignore (100%) rename {unit2/linux => linux}/CMakeLists.txt (100%) rename {unit2/linux => linux}/flutter/CMakeLists.txt (100%) rename {unit2/linux => linux}/flutter/generated_plugin_registrant.cc (100%) rename {unit2/linux => linux}/flutter/generated_plugin_registrant.h (100%) rename {unit2/linux => linux}/flutter/generated_plugins.cmake (100%) rename {unit2/linux => linux}/main.cc (100%) rename {unit2/linux => linux}/my_application.cc (100%) rename {unit2/linux => linux}/my_application.h (100%) rename {unit2/macos => macos}/.gitignore (100%) rename {unit2/macos => macos}/Flutter/Flutter-Debug.xcconfig (100%) rename {unit2/macos => macos}/Flutter/Flutter-Release.xcconfig (100%) rename {unit2/macos => macos}/Flutter/GeneratedPluginRegistrant.swift (100%) rename {unit2/macos => macos}/Runner.xcodeproj/project.pbxproj (100%) rename {unit2/macos => macos}/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename {unit2/macos => macos}/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme (100%) rename {unit2/macos => macos}/Runner.xcworkspace/contents.xcworkspacedata (100%) rename {unit2/macos => macos}/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename {unit2/macos => macos}/Runner/AppDelegate.swift (100%) rename {unit2/macos => macos}/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename {unit2/macos => macos}/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png (100%) rename {unit2/macos => macos}/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png (100%) rename {unit2/macos => macos}/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png (100%) rename {unit2/macos => macos}/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png (100%) rename {unit2/macos => macos}/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png (100%) rename {unit2/macos => macos}/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png (100%) rename {unit2/macos => macos}/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png (100%) rename {unit2/macos => macos}/Runner/Base.lproj/MainMenu.xib (100%) rename {unit2/macos => macos}/Runner/Configs/AppInfo.xcconfig (100%) rename {unit2/macos => macos}/Runner/Configs/Debug.xcconfig (100%) rename {unit2/macos => macos}/Runner/Configs/Release.xcconfig (100%) rename {unit2/macos => macos}/Runner/Configs/Warnings.xcconfig (100%) rename {unit2/macos => macos}/Runner/DebugProfile.entitlements (100%) rename {unit2/macos => macos}/Runner/Info.plist (100%) rename {unit2/macos => macos}/Runner/MainFlutterWindow.swift (100%) rename {unit2/macos => macos}/Runner/Release.entitlements (100%) rename unit2/notes.txt => notes.txt (100%) rename unit2/pubspec.lock => pubspec.lock (100%) rename unit2/pubspec.yaml => pubspec.yaml (100%) rename {unit2/test => test}/widget_test.dart (100%) delete mode 100644 unit2/README.md delete mode 100644 unit2/lib/screens/unit2/login/login.dart rename {unit2/web => web}/favicon.png (100%) rename {unit2/web => web}/icons/Icon-192.png (100%) rename {unit2/web => web}/icons/Icon-512.png (100%) rename {unit2/web => web}/icons/Icon-maskable-192.png (100%) rename {unit2/web => web}/icons/Icon-maskable-512.png (100%) rename {unit2/web => web}/index.html (100%) rename {unit2/web => web}/manifest.json (100%) rename {unit2/windows => windows}/.gitignore (100%) rename {unit2/windows => windows}/CMakeLists.txt (100%) rename {unit2/windows => windows}/flutter/CMakeLists.txt (100%) rename {unit2/windows => windows}/flutter/generated_plugin_registrant.cc (100%) rename {unit2/windows => windows}/flutter/generated_plugin_registrant.h (100%) rename {unit2/windows => windows}/flutter/generated_plugins.cmake (100%) rename {unit2/windows => windows}/runner/CMakeLists.txt (100%) rename {unit2/windows => windows}/runner/Runner.rc (100%) rename {unit2/windows => windows}/runner/flutter_window.cpp (100%) rename {unit2/windows => windows}/runner/flutter_window.h (100%) rename {unit2/windows => windows}/runner/main.cpp (100%) rename {unit2/windows => windows}/runner/resource.h (100%) rename {unit2/windows => windows}/runner/resources/app_icon.ico (100%) rename {unit2/windows => windows}/runner/runner.exe.manifest (100%) rename {unit2/windows => windows}/runner/utils.cpp (100%) rename {unit2/windows => windows}/runner/utils.h (100%) rename {unit2/windows => windows}/runner/win32_window.cpp (100%) rename {unit2/windows => windows}/runner/win32_window.h (100%) diff --git a/unit2/.gitignore b/.gitignore similarity index 100% rename from unit2/.gitignore rename to .gitignore diff --git a/unit2/.metadata b/.metadata similarity index 100% rename from unit2/.metadata rename to .metadata diff --git a/README.md b/README.md index e69de29..9a223c4 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,16 @@ +# unit2 + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/unit2/analysis_options.yaml b/analysis_options.yaml similarity index 100% rename from unit2/analysis_options.yaml rename to analysis_options.yaml diff --git a/unit2/android/.gitignore b/android/.gitignore similarity index 100% rename from unit2/android/.gitignore rename to android/.gitignore diff --git a/unit2/android/app/build.gradle b/android/app/build.gradle similarity index 100% rename from unit2/android/app/build.gradle rename to android/app/build.gradle diff --git a/unit2/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml similarity index 100% rename from unit2/android/app/src/debug/AndroidManifest.xml rename to android/app/src/debug/AndroidManifest.xml diff --git a/unit2/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml similarity index 100% rename from unit2/android/app/src/main/AndroidManifest.xml rename to android/app/src/main/AndroidManifest.xml diff --git a/unit2/android/app/src/main/java/io/flutter/app/FlutterMultiDexApplication.java b/android/app/src/main/java/io/flutter/app/FlutterMultiDexApplication.java similarity index 100% rename from unit2/android/app/src/main/java/io/flutter/app/FlutterMultiDexApplication.java rename to android/app/src/main/java/io/flutter/app/FlutterMultiDexApplication.java diff --git a/unit2/android/app/src/main/kotlin/com/example/unit2/MainActivity.kt b/android/app/src/main/kotlin/com/example/unit2/MainActivity.kt similarity index 100% rename from unit2/android/app/src/main/kotlin/com/example/unit2/MainActivity.kt rename to android/app/src/main/kotlin/com/example/unit2/MainActivity.kt diff --git a/unit2/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml similarity index 100% rename from unit2/android/app/src/main/res/drawable-v21/launch_background.xml rename to android/app/src/main/res/drawable-v21/launch_background.xml diff --git a/unit2/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml similarity index 100% rename from unit2/android/app/src/main/res/drawable/launch_background.xml rename to android/app/src/main/res/drawable/launch_background.xml diff --git a/unit2/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from unit2/android/app/src/main/res/mipmap-hdpi/ic_launcher.png rename to android/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/unit2/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from unit2/android/app/src/main/res/mipmap-mdpi/ic_launcher.png rename to android/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/unit2/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from unit2/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to android/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/unit2/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from unit2/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/unit2/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from unit2/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/unit2/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml similarity index 100% rename from unit2/android/app/src/main/res/values-night/styles.xml rename to android/app/src/main/res/values-night/styles.xml diff --git a/unit2/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml similarity index 100% rename from unit2/android/app/src/main/res/values/styles.xml rename to android/app/src/main/res/values/styles.xml diff --git a/unit2/android/app/src/main/res/xml/file_provider_path.xml b/android/app/src/main/res/xml/file_provider_path.xml similarity index 100% rename from unit2/android/app/src/main/res/xml/file_provider_path.xml rename to android/app/src/main/res/xml/file_provider_path.xml diff --git a/unit2/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml similarity index 100% rename from unit2/android/app/src/profile/AndroidManifest.xml rename to android/app/src/profile/AndroidManifest.xml diff --git a/unit2/android/build.gradle b/android/build.gradle similarity index 100% rename from unit2/android/build.gradle rename to android/build.gradle diff --git a/unit2/android/gradle.properties b/android/gradle.properties similarity index 100% rename from unit2/android/gradle.properties rename to android/gradle.properties diff --git a/unit2/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from unit2/android/gradle/wrapper/gradle-wrapper.properties rename to android/gradle/wrapper/gradle-wrapper.properties diff --git a/unit2/android/settings.gradle b/android/settings.gradle similarity index 100% rename from unit2/android/settings.gradle rename to android/settings.gradle diff --git a/unit2/assets/fonts/LexendDeca-Bold.ttf b/assets/fonts/LexendDeca-Bold.ttf similarity index 100% rename from unit2/assets/fonts/LexendDeca-Bold.ttf rename to assets/fonts/LexendDeca-Bold.ttf diff --git a/unit2/assets/fonts/LexendDeca-Light.ttf b/assets/fonts/LexendDeca-Light.ttf similarity index 100% rename from unit2/assets/fonts/LexendDeca-Light.ttf rename to assets/fonts/LexendDeca-Light.ttf diff --git a/unit2/assets/fonts/LexendDeca-Medium.ttf b/assets/fonts/LexendDeca-Medium.ttf similarity index 100% rename from unit2/assets/fonts/LexendDeca-Medium.ttf rename to assets/fonts/LexendDeca-Medium.ttf diff --git a/unit2/assets/fonts/LexendDeca-Regular.ttf b/assets/fonts/LexendDeca-Regular.ttf similarity index 100% rename from unit2/assets/fonts/LexendDeca-Regular.ttf rename to assets/fonts/LexendDeca-Regular.ttf diff --git a/unit2/assets/fonts/LexendDeca-SemiBold.ttf b/assets/fonts/LexendDeca-SemiBold.ttf similarity index 100% rename from unit2/assets/fonts/LexendDeca-SemiBold.ttf rename to assets/fonts/LexendDeca-SemiBold.ttf diff --git a/unit2/assets/fonts/LexendDeca-VariableFont_wght.ttf b/assets/fonts/LexendDeca-VariableFont_wght.ttf similarity index 100% rename from unit2/assets/fonts/LexendDeca-VariableFont_wght.ttf rename to assets/fonts/LexendDeca-VariableFont_wght.ttf diff --git a/unit2/assets/pngs/bg.png b/assets/pngs/bg.png similarity index 100% rename from unit2/assets/pngs/bg.png rename to assets/pngs/bg.png diff --git a/unit2/assets/pngs/qr-scan.png b/assets/pngs/qr-scan.png similarity index 100% rename from unit2/assets/pngs/qr-scan.png rename to assets/pngs/qr-scan.png diff --git a/unit2/assets/svgs/add_mobile.svg b/assets/svgs/add_mobile.svg similarity index 100% rename from unit2/assets/svgs/add_mobile.svg rename to assets/svgs/add_mobile.svg diff --git a/unit2/assets/svgs/assign.svg b/assets/svgs/assign.svg similarity index 100% rename from unit2/assets/svgs/assign.svg rename to assets/svgs/assign.svg diff --git a/unit2/assets/svgs/download.svg b/assets/svgs/download.svg similarity index 100% rename from unit2/assets/svgs/download.svg rename to assets/svgs/download.svg diff --git a/unit2/assets/svgs/emergency.png b/assets/svgs/emergency.png similarity index 100% rename from unit2/assets/svgs/emergency.png rename to assets/svgs/emergency.png diff --git a/unit2/assets/svgs/empty.svg b/assets/svgs/empty.svg similarity index 100% rename from unit2/assets/svgs/empty.svg rename to assets/svgs/empty.svg diff --git a/unit2/assets/svgs/error.svg b/assets/svgs/error.svg similarity index 100% rename from unit2/assets/svgs/error.svg rename to assets/svgs/error.svg diff --git a/unit2/assets/svgs/female.svg b/assets/svgs/female.svg similarity index 100% rename from unit2/assets/svgs/female.svg rename to assets/svgs/female.svg diff --git a/unit2/assets/svgs/logo.svg b/assets/svgs/logo.svg similarity index 100% rename from unit2/assets/svgs/logo.svg rename to assets/svgs/logo.svg diff --git a/unit2/assets/svgs/male.svg b/assets/svgs/male.svg similarity index 100% rename from unit2/assets/svgs/male.svg rename to assets/svgs/male.svg diff --git a/unit2/assets/svgs/no_module.svg b/assets/svgs/no_module.svg similarity index 100% rename from unit2/assets/svgs/no_module.svg rename to assets/svgs/no_module.svg diff --git a/unit2/assets/svgs/request_sos.svg b/assets/svgs/request_sos.svg similarity index 100% rename from unit2/assets/svgs/request_sos.svg rename to assets/svgs/request_sos.svg diff --git a/unit2/assets/svgs/settings.svg b/assets/svgs/settings.svg similarity index 100% rename from unit2/assets/svgs/settings.svg rename to assets/svgs/settings.svg diff --git a/unit2/assets/svgs/sos.svg b/assets/svgs/sos.svg similarity index 100% rename from unit2/assets/svgs/sos.svg rename to assets/svgs/sos.svg diff --git a/unit2/assets/svgs/switch.svg b/assets/svgs/switch.svg similarity index 100% rename from unit2/assets/svgs/switch.svg rename to assets/svgs/switch.svg diff --git a/unit2/assets/svgs/timeout.svg b/assets/svgs/timeout.svg similarity index 100% rename from unit2/assets/svgs/timeout.svg rename to assets/svgs/timeout.svg diff --git a/unit2/assets/svgs/welcome.svg b/assets/svgs/welcome.svg similarity index 100% rename from unit2/assets/svgs/welcome.svg rename to assets/svgs/welcome.svg diff --git a/unit2/assets/svgs/workspace.svg b/assets/svgs/workspace.svg similarity index 100% rename from unit2/assets/svgs/workspace.svg rename to assets/svgs/workspace.svg diff --git a/unit2/ios/.gitignore b/ios/.gitignore similarity index 100% rename from unit2/ios/.gitignore rename to ios/.gitignore diff --git a/unit2/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist similarity index 100% rename from unit2/ios/Flutter/AppFrameworkInfo.plist rename to ios/Flutter/AppFrameworkInfo.plist diff --git a/unit2/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig similarity index 100% rename from unit2/ios/Flutter/Debug.xcconfig rename to ios/Flutter/Debug.xcconfig diff --git a/unit2/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig similarity index 100% rename from unit2/ios/Flutter/Release.xcconfig rename to ios/Flutter/Release.xcconfig diff --git a/unit2/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj similarity index 100% rename from unit2/ios/Runner.xcodeproj/project.pbxproj rename to ios/Runner.xcodeproj/project.pbxproj diff --git a/unit2/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from unit2/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/unit2/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from unit2/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/unit2/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from unit2/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/unit2/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme similarity index 100% rename from unit2/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme diff --git a/unit2/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata similarity index 100% rename from unit2/ios/Runner.xcworkspace/contents.xcworkspacedata rename to ios/Runner.xcworkspace/contents.xcworkspacedata diff --git a/unit2/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from unit2/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/unit2/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from unit2/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/unit2/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift similarity index 100% rename from unit2/ios/Runner/AppDelegate.swift rename to ios/Runner/AppDelegate.swift diff --git a/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png similarity index 100% rename from unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png diff --git a/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png similarity index 100% rename from unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png diff --git a/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png similarity index 100% rename from unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png diff --git a/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png similarity index 100% rename from unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png diff --git a/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png similarity index 100% rename from unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png diff --git a/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png similarity index 100% rename from unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png diff --git a/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png similarity index 100% rename from unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png diff --git a/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png similarity index 100% rename from unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png diff --git a/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png similarity index 100% rename from unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png diff --git a/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png similarity index 100% rename from unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png diff --git a/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png similarity index 100% rename from unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png diff --git a/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png similarity index 100% rename from unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png diff --git a/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png similarity index 100% rename from unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png diff --git a/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png similarity index 100% rename from unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png diff --git a/unit2/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 similarity index 100% rename from unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png rename to ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png diff --git a/unit2/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json similarity index 100% rename from unit2/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json rename to ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json diff --git a/unit2/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png similarity index 100% rename from unit2/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png rename to ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png diff --git a/unit2/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png similarity index 100% rename from unit2/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png rename to ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png diff --git a/unit2/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png similarity index 100% rename from unit2/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png rename to ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png diff --git a/unit2/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md similarity index 100% rename from unit2/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md rename to ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md diff --git a/unit2/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from unit2/ios/Runner/Base.lproj/LaunchScreen.storyboard rename to ios/Runner/Base.lproj/LaunchScreen.storyboard diff --git a/unit2/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard similarity index 100% rename from unit2/ios/Runner/Base.lproj/Main.storyboard rename to ios/Runner/Base.lproj/Main.storyboard diff --git a/unit2/ios/Runner/Info.plist b/ios/Runner/Info.plist similarity index 100% rename from unit2/ios/Runner/Info.plist rename to ios/Runner/Info.plist diff --git a/unit2/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h similarity index 100% rename from unit2/ios/Runner/Runner-Bridging-Header.h rename to ios/Runner/Runner-Bridging-Header.h diff --git a/unit2/lib/bloc/bloc/user_bloc.dart b/lib/bloc/bloc/user_bloc.dart similarity index 100% rename from unit2/lib/bloc/bloc/user_bloc.dart rename to lib/bloc/bloc/user_bloc.dart diff --git a/unit2/lib/bloc/bloc/user_event.dart b/lib/bloc/bloc/user_event.dart similarity index 100% rename from unit2/lib/bloc/bloc/user_event.dart rename to lib/bloc/bloc/user_event.dart diff --git a/unit2/lib/bloc/bloc/user_state.dart b/lib/bloc/bloc/user_state.dart similarity index 100% rename from unit2/lib/bloc/bloc/user_state.dart rename to lib/bloc/bloc/user_state.dart diff --git a/unit2/lib/main.dart b/lib/main.dart similarity index 100% rename from unit2/lib/main.dart rename to lib/main.dart diff --git a/unit2/lib/model/login_data/employee_info/department.dart b/lib/model/login_data/employee_info/department.dart similarity index 100% rename from unit2/lib/model/login_data/employee_info/department.dart rename to lib/model/login_data/employee_info/department.dart diff --git a/unit2/lib/model/login_data/employee_info/employee_info.dart b/lib/model/login_data/employee_info/employee_info.dart similarity index 100% rename from unit2/lib/model/login_data/employee_info/employee_info.dart rename to lib/model/login_data/employee_info/employee_info.dart diff --git a/unit2/lib/model/login_data/employee_info/head.dart b/lib/model/login_data/employee_info/head.dart similarity index 100% rename from unit2/lib/model/login_data/employee_info/head.dart rename to lib/model/login_data/employee_info/head.dart diff --git a/unit2/lib/model/login_data/employee_info/office.dart b/lib/model/login_data/employee_info/office.dart similarity index 100% rename from unit2/lib/model/login_data/employee_info/office.dart rename to lib/model/login_data/employee_info/office.dart diff --git a/unit2/lib/model/login_data/employee_info/position_class.dart b/lib/model/login_data/employee_info/position_class.dart similarity index 100% rename from unit2/lib/model/login_data/employee_info/position_class.dart rename to lib/model/login_data/employee_info/position_class.dart diff --git a/unit2/lib/model/login_data/user_info/assigned_area.dart b/lib/model/login_data/user_info/assigned_area.dart similarity index 100% rename from unit2/lib/model/login_data/user_info/assigned_area.dart rename to lib/model/login_data/user_info/assigned_area.dart diff --git a/unit2/lib/model/login_data/user_info/login_user.dart b/lib/model/login_data/user_info/login_user.dart similarity index 100% rename from unit2/lib/model/login_data/user_info/login_user.dart rename to lib/model/login_data/user_info/login_user.dart diff --git a/unit2/lib/model/login_data/user_info/module.dart b/lib/model/login_data/user_info/module.dart similarity index 100% rename from unit2/lib/model/login_data/user_info/module.dart rename to lib/model/login_data/user_info/module.dart diff --git a/unit2/lib/model/login_data/user_info/role.dart b/lib/model/login_data/user_info/role.dart similarity index 100% rename from unit2/lib/model/login_data/user_info/role.dart rename to lib/model/login_data/user_info/role.dart diff --git a/unit2/lib/model/login_data/user_info/user_data.dart b/lib/model/login_data/user_info/user_data.dart similarity index 100% rename from unit2/lib/model/login_data/user_info/user_data.dart rename to lib/model/login_data/user_info/user_data.dart diff --git a/unit2/lib/model/login_data/version_info.dart b/lib/model/login_data/version_info.dart similarity index 100% rename from unit2/lib/model/login_data/version_info.dart rename to lib/model/login_data/version_info.dart diff --git a/unit2/lib/screens/docsms/components/doc_info_tile.dart b/lib/screens/docsms/components/doc_info_tile.dart similarity index 100% rename from unit2/lib/screens/docsms/components/doc_info_tile.dart rename to lib/screens/docsms/components/doc_info_tile.dart diff --git a/unit2/lib/screens/docsms/request_receipt.dart b/lib/screens/docsms/request_receipt.dart similarity index 100% rename from unit2/lib/screens/docsms/request_receipt.dart rename to lib/screens/docsms/request_receipt.dart diff --git a/unit2/lib/screens/sos/add_mobile.dart b/lib/screens/sos/add_mobile.dart similarity index 100% rename from unit2/lib/screens/sos/add_mobile.dart rename to lib/screens/sos/add_mobile.dart diff --git a/unit2/lib/screens/sos/components/mobile.dart b/lib/screens/sos/components/mobile.dart similarity index 100% rename from unit2/lib/screens/sos/components/mobile.dart rename to lib/screens/sos/components/mobile.dart diff --git a/unit2/lib/screens/sos/request_sos.dart b/lib/screens/sos/request_sos.dart similarity index 100% rename from unit2/lib/screens/sos/request_sos.dart rename to lib/screens/sos/request_sos.dart diff --git a/unit2/lib/screens/sos/sos_received.dart b/lib/screens/sos/sos_received.dart similarity index 100% rename from unit2/lib/screens/sos/sos_received.dart rename to lib/screens/sos/sos_received.dart diff --git a/unit2/lib/screens/unit2/basic-info/basic-info.dart b/lib/screens/unit2/basic-info/basic-info.dart similarity index 100% rename from unit2/lib/screens/unit2/basic-info/basic-info.dart rename to lib/screens/unit2/basic-info/basic-info.dart diff --git a/unit2/lib/screens/unit2/basic-info/components/cover-image.dart b/lib/screens/unit2/basic-info/components/cover-image.dart similarity index 100% rename from unit2/lib/screens/unit2/basic-info/components/cover-image.dart rename to lib/screens/unit2/basic-info/components/cover-image.dart diff --git a/unit2/lib/screens/unit2/homepage.dart/components/dashboard.dart b/lib/screens/unit2/homepage.dart/components/dashboard.dart similarity index 100% rename from unit2/lib/screens/unit2/homepage.dart/components/dashboard.dart rename to lib/screens/unit2/homepage.dart/components/dashboard.dart diff --git a/unit2/lib/screens/unit2/homepage.dart/components/drawer-screen.dart b/lib/screens/unit2/homepage.dart/components/drawer-screen.dart similarity index 100% rename from unit2/lib/screens/unit2/homepage.dart/components/drawer-screen.dart rename to lib/screens/unit2/homepage.dart/components/drawer-screen.dart diff --git a/unit2/lib/screens/unit2/homepage.dart/components/empty_module.dart b/lib/screens/unit2/homepage.dart/components/empty_module.dart similarity index 100% rename from unit2/lib/screens/unit2/homepage.dart/components/empty_module.dart rename to lib/screens/unit2/homepage.dart/components/empty_module.dart diff --git a/unit2/lib/screens/unit2/homepage.dart/components/menu-screen.dart b/lib/screens/unit2/homepage.dart/components/menu-screen.dart similarity index 100% rename from unit2/lib/screens/unit2/homepage.dart/components/menu-screen.dart rename to lib/screens/unit2/homepage.dart/components/menu-screen.dart diff --git a/unit2/lib/screens/unit2/homepage.dart/components/menu.dart b/lib/screens/unit2/homepage.dart/components/menu.dart similarity index 100% rename from unit2/lib/screens/unit2/homepage.dart/components/menu.dart rename to lib/screens/unit2/homepage.dart/components/menu.dart diff --git a/unit2/lib/screens/unit2/homepage.dart/module-screen.dart b/lib/screens/unit2/homepage.dart/module-screen.dart similarity index 100% rename from unit2/lib/screens/unit2/homepage.dart/module-screen.dart rename to lib/screens/unit2/homepage.dart/module-screen.dart diff --git a/unit2/lib/screens/unit2/login/components/login-via-qr-label.dart b/lib/screens/unit2/login/components/login-via-qr-label.dart similarity index 100% rename from unit2/lib/screens/unit2/login/components/login-via-qr-label.dart rename to lib/screens/unit2/login/components/login-via-qr-label.dart diff --git a/unit2/lib/screens/unit2/login/components/showAlert.dart b/lib/screens/unit2/login/components/showAlert.dart similarity index 100% rename from unit2/lib/screens/unit2/login/components/showAlert.dart rename to lib/screens/unit2/login/components/showAlert.dart diff --git a/unit2/lib/screens/unit2/login/components/update_required.dart b/lib/screens/unit2/login/components/update_required.dart similarity index 100% rename from unit2/lib/screens/unit2/login/components/update_required.dart rename to lib/screens/unit2/login/components/update_required.dart diff --git a/unit2/lib/screens/unit2/login/functions/get_app_version.dart b/lib/screens/unit2/login/functions/get_app_version.dart similarity index 100% rename from unit2/lib/screens/unit2/login/functions/get_app_version.dart rename to lib/screens/unit2/login/functions/get_app_version.dart diff --git a/unit2/lib/screens/unit2/login/functions/press-again-to-exit.dart b/lib/screens/unit2/login/functions/press-again-to-exit.dart similarity index 100% rename from unit2/lib/screens/unit2/login/functions/press-again-to-exit.dart rename to lib/screens/unit2/login/functions/press-again-to-exit.dart diff --git a/lib/screens/unit2/login/login.dart b/lib/screens/unit2/login/login.dart new file mode 100644 index 0000000..bd2d590 --- /dev/null +++ b/lib/screens/unit2/login/login.dart @@ -0,0 +1,314 @@ +import 'package:barcode_scan2/barcode_scan2.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:fluttericon/font_awesome5_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:unit2/bloc/bloc/user_bloc.dart'; +import 'package:unit2/screens/unit2/login/components/update_required.dart'; +import 'package:unit2/utils/text_container.dart'; +import 'package:unit2/widgets/error_state.dart'; +import '../../../widgets/splash_screen.dart'; +import '../../../widgets/wave.dart'; +import '../../../utils/global.dart'; +import '../../../theme-data.dart/colors.dart'; +import '../../../theme-data.dart/form-style.dart'; +import '../../../theme-data.dart/btn-style.dart'; +import './components/login-via-qr-label.dart'; +import './functions/press-again-to-exit.dart'; + +class UniT2Login extends StatefulWidget { + const UniT2Login({super.key}); + + @override + State createState() => _UniT2LoginState(); +} + +class _UniT2LoginState extends State { + final _formKey = GlobalKey(); + bool showSuffixIcon = false; + bool _showPassword = true; + @override + Widget build(BuildContext context) { + return WillPopScope( + onWillPop: pressAgainToExit, + child: Scaffold( + body: ProgressHUD( + child: BlocConsumer(listener: (context, state) { + if (state is UserLoggedIn) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + Navigator.pushReplacementNamed(context, '/module-screen'); + } + if (state is UuidLoaded) { + Navigator.pushNamed(context, '/qr-login'); + } + }, builder: (context, state) { + if (state is VersionLoaded) { + return Builder(builder: (context) { + if (state.versionInfo!.version != state.apkVersion) { + return SizedBox( + child: SingleChildScrollView( + child: Stack( + alignment: Alignment.center, + children: [ + Positioned( + bottom: 0, + right: 0, + child: + WaveReverse(height: blockSizeVertical * 7)), + SizedBox( + height: blockSizeVertical * 100, + child: FormBuilder( + key: _formKey, + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 25), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SvgPicture.asset( + 'assets/svgs/logo.svg', + height: blockSizeVertical * 12, + allowDrawingOutsideViewBox: true, + color: primary, + ), + + Text( + welcome, + style: TextStyle( + fontSize: blockSizeVertical * 4, + fontWeight: FontWeight.w600), + ), + Text(unitApp, + style: TextStyle( + fontSize: blockSizeVertical * 6, + fontWeight: FontWeight.w800, + letterSpacing: .2, + height: 1, + color: primary)), + // Text( + // loginToContinue, + // style: TextStyle( + // fontSize: blockSizeVertical * 1.7, + // height: 1.5, + // fontWeight: FontWeight.w600), + // ), + SizedBox( + height: blockSizeVertical * 3, + ), + // USERNAME + FormBuilderTextField( + name: 'username', + validator: + FormBuilderValidators.required( + errorText: usernameRequired), + autofocus: false, + style: const TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black87), + decoration: + loginTextFieldStyle().copyWith()), + SizedBox( + height: blockSizeVertical * 1.5, + ), + // PASSWORD + FormBuilderTextField( + name: 'password', + validator: FormBuilderValidators.required( + errorText: passwordRequired), + onChanged: (value) { + value!.isEmpty + ? setState(() { + showSuffixIcon = false; + }) + : setState(() { + showSuffixIcon = true; + }); + }, + autofocus: false, + style: const TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black87), + decoration: + loginTextFieldStyle().copyWith( + suffixIcon: Visibility( + visible: showSuffixIcon, + child: _showPassword + ? IconButton( + icon: Icon( + FontAwesome5 + .eye_slash, + size: 24, + color: Theme.of( + context) + .textTheme + .displayLarge + ?.color), + onPressed: () { + setState(() { + _showPassword = + false; + }); + }, + ) + : IconButton( + onPressed: () { + setState(() { + _showPassword = + true; + }); + }, + icon: Icon( + FontAwesome5.eye, + size: 24, + color: Theme.of( + context) + .textTheme + .displayLarge + ?.color)), + ), + prefixIcon: const Icon( + Icons.lock, + color: primary, + ), + labelText: "Password", + hintText: enterPassword), + obscureText: _showPassword ? true : false, + ), + SizedBox( + height: blockSizeVertical * 2, + ), + SizedBox( + height: blockSizeVertical * 7, + // Login Button + child: SizedBox( + width: + MediaQuery.of(context).size.width, + child: ElevatedButton( + style: mainBtnStyle( + second, + Colors.transparent, + Colors.white54), + child: const Text( + login, + style: + TextStyle(color: Colors.white), + ), + onPressed: () { + final progress = + ProgressHUD.of(context); + + FocusScope.of(context).unfocus(); + + if (_formKey.currentState! + .saveAndValidate()) { + progress?.showWithText( + 'Logging in...', + ); + + BlocProvider.of(context) + .add(UserLogin( + username: _formKey + .currentState! + .value['username'], + password: _formKey + .currentState! + .value['password'])); + } + }, + ), + ), + ), + SizedBox( + height: blockSizeVertical * 1.5, + ), + + SizedBox( + height: blockSizeVertical * 7, + child: SizedBox( + width: + MediaQuery.of(context).size.width, + child: ElevatedButton.icon( + style: mainBtnStyle( + Colors.white, + second, + primary.withOpacity(.4)), + icon: const Icon( + Icons.qr_code, + color: second, + ), + label: const Text( + loginViaQr, + style: TextStyle(color: second), + ), + onPressed: () { + context + .read() + .add(GetUuid()); + }, + ), + )), + SizedBox( + height: blockSizeVertical * 1, + ), + const LoginViaQr( + text: emergencyReponseLabel), + SizedBox( + height: blockSizeVertical * 1, + ), + // REQUEST SOS + SizedBox( + height: screenHeight * .07, + width: MediaQuery.of(context).size.width, + child: ElevatedButton.icon( + icon: const Icon( + FontAwesome5.life_ring, + color: Colors.white, + ), + style: mainBtnStyle( + third, + Colors.transparent, + Colors.white38), + onPressed: () {}, + label: const Text( + requestSOS, + style: + TextStyle(color: Colors.white), + )), + ) + ], + ), + ), + ), + ), + ], + ), + ), + ); + } else { + return Update( + apkVersion: state.apkVersion!, + currenVersion: state.versionInfo!.version!, + ); + } + }); + } + if (state is UserError) { + return ErrorState( + message: state.message, + ); + } + if (state is SplashScreen) { + return const UniTSplashScreen(); + } + return Container(); + }), + ), + ), + ); + } +} diff --git a/unit2/lib/screens/unit2/login/qr_login.dart b/lib/screens/unit2/login/qr_login.dart similarity index 100% rename from unit2/lib/screens/unit2/login/qr_login.dart rename to lib/screens/unit2/login/qr_login.dart diff --git a/unit2/lib/screens/unit2/roles/qr_code_scanner.dart/components/custom_switch.dart b/lib/screens/unit2/roles/qr_code_scanner.dart/components/custom_switch.dart similarity index 100% rename from unit2/lib/screens/unit2/roles/qr_code_scanner.dart/components/custom_switch.dart rename to lib/screens/unit2/roles/qr_code_scanner.dart/components/custom_switch.dart diff --git a/unit2/lib/screens/unit2/roles/qr_code_scanner.dart/components/save_settings.dart b/lib/screens/unit2/roles/qr_code_scanner.dart/components/save_settings.dart similarity index 100% rename from unit2/lib/screens/unit2/roles/qr_code_scanner.dart/components/save_settings.dart rename to lib/screens/unit2/roles/qr_code_scanner.dart/components/save_settings.dart diff --git a/unit2/lib/screens/unit2/roles/qr_code_scanner.dart/scan.dart b/lib/screens/unit2/roles/qr_code_scanner.dart/scan.dart similarity index 100% rename from unit2/lib/screens/unit2/roles/qr_code_scanner.dart/scan.dart rename to lib/screens/unit2/roles/qr_code_scanner.dart/scan.dart diff --git a/unit2/lib/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart b/lib/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart similarity index 100% rename from unit2/lib/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart rename to lib/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart diff --git a/unit2/lib/screens/unit2/roles/registration_in_charge/components/add.dart b/lib/screens/unit2/roles/registration_in_charge/components/add.dart similarity index 100% rename from unit2/lib/screens/unit2/roles/registration_in_charge/components/add.dart rename to lib/screens/unit2/roles/registration_in_charge/components/add.dart diff --git a/unit2/lib/screens/unit2/roles/registration_in_charge/components/request_qr.dart b/lib/screens/unit2/roles/registration_in_charge/components/request_qr.dart similarity index 100% rename from unit2/lib/screens/unit2/roles/registration_in_charge/components/request_qr.dart rename to lib/screens/unit2/roles/registration_in_charge/components/request_qr.dart diff --git a/unit2/lib/screens/unit2/roles/registration_in_charge/components/sync.dart b/lib/screens/unit2/roles/registration_in_charge/components/sync.dart similarity index 100% rename from unit2/lib/screens/unit2/roles/registration_in_charge/components/sync.dart rename to lib/screens/unit2/roles/registration_in_charge/components/sync.dart diff --git a/unit2/lib/screens/unit2/roles/registration_in_charge/components/view.dart b/lib/screens/unit2/roles/registration_in_charge/components/view.dart similarity index 100% rename from unit2/lib/screens/unit2/roles/registration_in_charge/components/view.dart rename to lib/screens/unit2/roles/registration_in_charge/components/view.dart diff --git a/unit2/lib/screens/unit2/roles/registration_in_charge/home.dart b/lib/screens/unit2/roles/registration_in_charge/home.dart similarity index 100% rename from unit2/lib/screens/unit2/roles/registration_in_charge/home.dart rename to lib/screens/unit2/roles/registration_in_charge/home.dart diff --git a/unit2/lib/screens/unit2/signature/signature_pad.dart b/lib/screens/unit2/signature/signature_pad.dart similarity index 100% rename from unit2/lib/screens/unit2/signature/signature_pad.dart rename to lib/screens/unit2/signature/signature_pad.dart diff --git a/unit2/lib/sevices/login_service/auth_service.dart b/lib/sevices/login_service/auth_service.dart similarity index 100% rename from unit2/lib/sevices/login_service/auth_service.dart rename to lib/sevices/login_service/auth_service.dart diff --git a/unit2/lib/test_data.dart b/lib/test_data.dart similarity index 100% rename from unit2/lib/test_data.dart rename to lib/test_data.dart diff --git a/unit2/lib/theme-data.dart/btn-style.dart b/lib/theme-data.dart/btn-style.dart similarity index 100% rename from unit2/lib/theme-data.dart/btn-style.dart rename to lib/theme-data.dart/btn-style.dart diff --git a/unit2/lib/theme-data.dart/colors.dart b/lib/theme-data.dart/colors.dart similarity index 100% rename from unit2/lib/theme-data.dart/colors.dart rename to lib/theme-data.dart/colors.dart diff --git a/unit2/lib/theme-data.dart/form-style.dart b/lib/theme-data.dart/form-style.dart similarity index 100% rename from unit2/lib/theme-data.dart/form-style.dart rename to lib/theme-data.dart/form-style.dart diff --git a/unit2/lib/theme-data.dart/text-styles.dart b/lib/theme-data.dart/text-styles.dart similarity index 100% rename from unit2/lib/theme-data.dart/text-styles.dart rename to lib/theme-data.dart/text-styles.dart diff --git a/unit2/lib/utils/alerts.dart b/lib/utils/alerts.dart similarity index 100% rename from unit2/lib/utils/alerts.dart rename to lib/utils/alerts.dart diff --git a/unit2/lib/utils/app_router.dart b/lib/utils/app_router.dart similarity index 100% rename from unit2/lib/utils/app_router.dart rename to lib/utils/app_router.dart diff --git a/unit2/lib/utils/cpu_architecture.dart b/lib/utils/cpu_architecture.dart similarity index 100% rename from unit2/lib/utils/cpu_architecture.dart rename to lib/utils/cpu_architecture.dart diff --git a/unit2/lib/utils/global.dart b/lib/utils/global.dart similarity index 100% rename from unit2/lib/utils/global.dart rename to lib/utils/global.dart diff --git a/unit2/lib/utils/global_context.dart b/lib/utils/global_context.dart similarity index 100% rename from unit2/lib/utils/global_context.dart rename to lib/utils/global_context.dart diff --git a/unit2/lib/utils/request.dart b/lib/utils/request.dart similarity index 100% rename from unit2/lib/utils/request.dart rename to lib/utils/request.dart diff --git a/unit2/lib/utils/router.dart b/lib/utils/router.dart similarity index 100% rename from unit2/lib/utils/router.dart rename to lib/utils/router.dart diff --git a/unit2/lib/utils/scanner.dart b/lib/utils/scanner.dart similarity index 100% rename from unit2/lib/utils/scanner.dart rename to lib/utils/scanner.dart diff --git a/unit2/lib/utils/screen_info.dart b/lib/utils/screen_info.dart similarity index 100% rename from unit2/lib/utils/screen_info.dart rename to lib/utils/screen_info.dart diff --git a/unit2/lib/utils/text_container.dart b/lib/utils/text_container.dart similarity index 100% rename from unit2/lib/utils/text_container.dart rename to lib/utils/text_container.dart diff --git a/unit2/lib/utils/urls.dart b/lib/utils/urls.dart similarity index 100% rename from unit2/lib/utils/urls.dart rename to lib/utils/urls.dart diff --git a/unit2/lib/utils/validators.dart b/lib/utils/validators.dart similarity index 100% rename from unit2/lib/utils/validators.dart rename to lib/utils/validators.dart diff --git a/unit2/lib/widgets/costum_divider.dart b/lib/widgets/costum_divider.dart similarity index 100% rename from unit2/lib/widgets/costum_divider.dart rename to lib/widgets/costum_divider.dart diff --git a/unit2/lib/widgets/error_state.dart b/lib/widgets/error_state.dart similarity index 100% rename from unit2/lib/widgets/error_state.dart rename to lib/widgets/error_state.dart diff --git a/unit2/lib/widgets/label.dart b/lib/widgets/label.dart similarity index 100% rename from unit2/lib/widgets/label.dart rename to lib/widgets/label.dart diff --git a/unit2/lib/widgets/splash_screen.dart b/lib/widgets/splash_screen.dart similarity index 100% rename from unit2/lib/widgets/splash_screen.dart rename to lib/widgets/splash_screen.dart diff --git a/unit2/lib/widgets/text_icon.dart b/lib/widgets/text_icon.dart similarity index 100% rename from unit2/lib/widgets/text_icon.dart rename to lib/widgets/text_icon.dart diff --git a/unit2/lib/widgets/wave.dart b/lib/widgets/wave.dart similarity index 100% rename from unit2/lib/widgets/wave.dart rename to lib/widgets/wave.dart diff --git a/unit2/linux/.gitignore b/linux/.gitignore similarity index 100% rename from unit2/linux/.gitignore rename to linux/.gitignore diff --git a/unit2/linux/CMakeLists.txt b/linux/CMakeLists.txt similarity index 100% rename from unit2/linux/CMakeLists.txt rename to linux/CMakeLists.txt diff --git a/unit2/linux/flutter/CMakeLists.txt b/linux/flutter/CMakeLists.txt similarity index 100% rename from unit2/linux/flutter/CMakeLists.txt rename to linux/flutter/CMakeLists.txt diff --git a/unit2/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc similarity index 100% rename from unit2/linux/flutter/generated_plugin_registrant.cc rename to linux/flutter/generated_plugin_registrant.cc diff --git a/unit2/linux/flutter/generated_plugin_registrant.h b/linux/flutter/generated_plugin_registrant.h similarity index 100% rename from unit2/linux/flutter/generated_plugin_registrant.h rename to linux/flutter/generated_plugin_registrant.h diff --git a/unit2/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake similarity index 100% rename from unit2/linux/flutter/generated_plugins.cmake rename to linux/flutter/generated_plugins.cmake diff --git a/unit2/linux/main.cc b/linux/main.cc similarity index 100% rename from unit2/linux/main.cc rename to linux/main.cc diff --git a/unit2/linux/my_application.cc b/linux/my_application.cc similarity index 100% rename from unit2/linux/my_application.cc rename to linux/my_application.cc diff --git a/unit2/linux/my_application.h b/linux/my_application.h similarity index 100% rename from unit2/linux/my_application.h rename to linux/my_application.h diff --git a/unit2/macos/.gitignore b/macos/.gitignore similarity index 100% rename from unit2/macos/.gitignore rename to macos/.gitignore diff --git a/unit2/macos/Flutter/Flutter-Debug.xcconfig b/macos/Flutter/Flutter-Debug.xcconfig similarity index 100% rename from unit2/macos/Flutter/Flutter-Debug.xcconfig rename to macos/Flutter/Flutter-Debug.xcconfig diff --git a/unit2/macos/Flutter/Flutter-Release.xcconfig b/macos/Flutter/Flutter-Release.xcconfig similarity index 100% rename from unit2/macos/Flutter/Flutter-Release.xcconfig rename to macos/Flutter/Flutter-Release.xcconfig diff --git a/unit2/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift similarity index 100% rename from unit2/macos/Flutter/GeneratedPluginRegistrant.swift rename to macos/Flutter/GeneratedPluginRegistrant.swift diff --git a/unit2/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj similarity index 100% rename from unit2/macos/Runner.xcodeproj/project.pbxproj rename to macos/Runner.xcodeproj/project.pbxproj diff --git a/unit2/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from unit2/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/unit2/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme similarity index 100% rename from unit2/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme diff --git a/unit2/macos/Runner.xcworkspace/contents.xcworkspacedata b/macos/Runner.xcworkspace/contents.xcworkspacedata similarity index 100% rename from unit2/macos/Runner.xcworkspace/contents.xcworkspacedata rename to macos/Runner.xcworkspace/contents.xcworkspacedata diff --git a/unit2/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from unit2/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/unit2/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift similarity index 100% rename from unit2/macos/Runner/AppDelegate.swift rename to macos/Runner/AppDelegate.swift diff --git a/unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json rename to macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png similarity index 100% rename from unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png rename to macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png diff --git a/unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png similarity index 100% rename from unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png rename to macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png diff --git a/unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png similarity index 100% rename from unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png rename to macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png diff --git a/unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png similarity index 100% rename from unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png rename to macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png diff --git a/unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png similarity index 100% rename from unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png rename to macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png diff --git a/unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png similarity index 100% rename from unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png rename to macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png diff --git a/unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png similarity index 100% rename from unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png rename to macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png diff --git a/unit2/macos/Runner/Base.lproj/MainMenu.xib b/macos/Runner/Base.lproj/MainMenu.xib similarity index 100% rename from unit2/macos/Runner/Base.lproj/MainMenu.xib rename to macos/Runner/Base.lproj/MainMenu.xib diff --git a/unit2/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig similarity index 100% rename from unit2/macos/Runner/Configs/AppInfo.xcconfig rename to macos/Runner/Configs/AppInfo.xcconfig diff --git a/unit2/macos/Runner/Configs/Debug.xcconfig b/macos/Runner/Configs/Debug.xcconfig similarity index 100% rename from unit2/macos/Runner/Configs/Debug.xcconfig rename to macos/Runner/Configs/Debug.xcconfig diff --git a/unit2/macos/Runner/Configs/Release.xcconfig b/macos/Runner/Configs/Release.xcconfig similarity index 100% rename from unit2/macos/Runner/Configs/Release.xcconfig rename to macos/Runner/Configs/Release.xcconfig diff --git a/unit2/macos/Runner/Configs/Warnings.xcconfig b/macos/Runner/Configs/Warnings.xcconfig similarity index 100% rename from unit2/macos/Runner/Configs/Warnings.xcconfig rename to macos/Runner/Configs/Warnings.xcconfig diff --git a/unit2/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements similarity index 100% rename from unit2/macos/Runner/DebugProfile.entitlements rename to macos/Runner/DebugProfile.entitlements diff --git a/unit2/macos/Runner/Info.plist b/macos/Runner/Info.plist similarity index 100% rename from unit2/macos/Runner/Info.plist rename to macos/Runner/Info.plist diff --git a/unit2/macos/Runner/MainFlutterWindow.swift b/macos/Runner/MainFlutterWindow.swift similarity index 100% rename from unit2/macos/Runner/MainFlutterWindow.swift rename to macos/Runner/MainFlutterWindow.swift diff --git a/unit2/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements similarity index 100% rename from unit2/macos/Runner/Release.entitlements rename to macos/Runner/Release.entitlements diff --git a/unit2/notes.txt b/notes.txt similarity index 100% rename from unit2/notes.txt rename to notes.txt diff --git a/unit2/pubspec.lock b/pubspec.lock similarity index 100% rename from unit2/pubspec.lock rename to pubspec.lock diff --git a/unit2/pubspec.yaml b/pubspec.yaml similarity index 100% rename from unit2/pubspec.yaml rename to pubspec.yaml diff --git a/unit2/test/widget_test.dart b/test/widget_test.dart similarity index 100% rename from unit2/test/widget_test.dart rename to test/widget_test.dart diff --git a/unit2/README.md b/unit2/README.md deleted file mode 100644 index 9a223c4..0000000 --- a/unit2/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# unit2 - -A new Flutter project. - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) - -For help getting started with Flutter development, view the -[online documentation](https://docs.flutter.dev/), which offers tutorials, -samples, guidance on mobile development, and a full API reference. diff --git a/unit2/lib/screens/unit2/login/login.dart b/unit2/lib/screens/unit2/login/login.dart deleted file mode 100644 index e3e7e3a..0000000 --- a/unit2/lib/screens/unit2/login/login.dart +++ /dev/null @@ -1,292 +0,0 @@ -import 'package:barcode_scan2/barcode_scan2.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:flutter_form_builder/flutter_form_builder.dart'; -import 'package:fluttericon/font_awesome5_icons.dart'; -import 'package:form_builder_validators/form_builder_validators.dart'; -import 'package:flutter_progress_hud/flutter_progress_hud.dart'; -import 'package:unit2/bloc/bloc/user_bloc.dart'; -import 'package:unit2/screens/unit2/login/components/update_required.dart'; -import 'package:unit2/utils/text_container.dart'; -import 'package:unit2/widgets/error_state.dart'; -import '../../../widgets/splash_screen.dart'; -import '../../../widgets/wave.dart'; -import '../../../utils/global.dart'; -import '../../../theme-data.dart/colors.dart'; -import '../../../theme-data.dart/form-style.dart'; -import '../../../theme-data.dart/btn-style.dart'; -import './components/login-via-qr-label.dart'; -import './functions/press-again-to-exit.dart'; - -class UniT2Login extends StatefulWidget { - const UniT2Login({super.key}); - - @override - State createState() => _UniT2LoginState(); -} - -class _UniT2LoginState extends State { - final _formKey = GlobalKey(); - bool showSuffixIcon = false; - bool _showPassword = true; - @override - Widget build(BuildContext context) { - return WillPopScope( - onWillPop: pressAgainToExit, - child: Scaffold( - body: ProgressHUD( - child: BlocConsumer(listener: (context, state) { - if (state is UserLoggedIn) { - final progress = ProgressHUD.of(context); - progress!.dismiss(); - Navigator.pushReplacementNamed(context, '/module-screen'); - } - if (state is UuidLoaded) { - Navigator.pushNamed(context, '/qr-login'); - } - }, builder: (context, state) { - if (state is VersionLoaded) { - return Builder(builder: (context) { - if(state.versionInfo!.version == state.apkVersion){ - return SizedBox( - child: SingleChildScrollView( - child: Stack( - alignment: Alignment.center, - children: [ - Positioned( - bottom: 0, - right: 0, - child: WaveReverse(height: blockSizeVertical * 7)), - SizedBox( - height: blockSizeVertical * 100, - child: FormBuilder( - key: _formKey, - child: Padding( - padding: - const EdgeInsets.symmetric(horizontal: 25), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SvgPicture.asset( - 'assets/svgs/logo.svg', - height: blockSizeVertical * 12, - allowDrawingOutsideViewBox: true, - color: primary, - ), - - Text( - welcome, - style: TextStyle( - fontSize: blockSizeVertical * 4, - fontWeight: FontWeight.w600), - ), - Text(unitApp, - style: TextStyle( - fontSize: blockSizeVertical * 6, - fontWeight: FontWeight.w800, - letterSpacing: .2, - height: 1, - color: primary)), - // Text( - // loginToContinue, - // style: TextStyle( - // fontSize: blockSizeVertical * 1.7, - // height: 1.5, - // fontWeight: FontWeight.w600), - // ), - SizedBox( - height: blockSizeVertical * 3, - ), - // USERNAME - FormBuilderTextField( - name: 'username', - validator: FormBuilderValidators.required( - errorText: usernameRequired), - autofocus: false, - style: const TextStyle( - fontWeight: FontWeight.bold, - color: Colors.black87), - decoration: - loginTextFieldStyle().copyWith()), - SizedBox( - height: blockSizeVertical * 1.5, - ), - // PASSWORD - FormBuilderTextField( - name: 'password', - validator: FormBuilderValidators.required( - errorText: passwordRequired), - onChanged: (value) { - value!.isEmpty - ? setState(() { - showSuffixIcon = false; - }) - : setState(() { - showSuffixIcon = true; - }); - }, - autofocus: false, - style: const TextStyle( - fontWeight: FontWeight.bold, - color: Colors.black87), - decoration: loginTextFieldStyle().copyWith( - suffixIcon: Visibility( - visible: showSuffixIcon, - child: _showPassword - ? IconButton( - icon: Icon( - FontAwesome5.eye_slash, - size: 24, - color: Theme.of(context) - .textTheme - .displayLarge - ?.color), - onPressed: () { - setState(() { - _showPassword = false; - }); - }, - ) - : IconButton( - onPressed: () { - setState(() { - _showPassword = true; - }); - }, - icon: Icon(FontAwesome5.eye, - size: 24, - color: Theme.of(context) - .textTheme - .displayLarge - ?.color)), - ), - prefixIcon: const Icon( - Icons.lock, - color: primary, - ), - labelText: "Password", - hintText: enterPassword), - obscureText: _showPassword ? true : false, - ), - SizedBox( - height: blockSizeVertical * 2, - ), - SizedBox( - height: blockSizeVertical * 7, - // Login Button - child: SizedBox( - width: MediaQuery.of(context).size.width, - child: ElevatedButton( - style: mainBtnStyle(second, - Colors.transparent, Colors.white54), - child: const Text( - login, - style: TextStyle(color: Colors.white), - ), - onPressed: () { - final progress = - ProgressHUD.of(context); - - FocusScope.of(context).unfocus(); - - if (_formKey.currentState! - .saveAndValidate()) { - progress?.showWithText( - 'Logging in...', - ); - - BlocProvider.of(context) - .add(UserLogin( - username: - _formKey.currentState!.value['username'], - password:_formKey.currentState!.value['password'] - )); - } - }, - ), - ), - ), - SizedBox( - height: blockSizeVertical * 1.5, - ), - - SizedBox( - height: blockSizeVertical * 7, - child: SizedBox( - width: - MediaQuery.of(context).size.width, - child: ElevatedButton.icon( - style: mainBtnStyle(Colors.white, - second, primary.withOpacity(.4)), - icon: const Icon( - Icons.qr_code, - color: second, - ), - label: const Text( - loginViaQr, - style: TextStyle(color: second), - ), - onPressed: () { - context - .read() - .add(GetUuid()); - }, - ), - )), - SizedBox( - height: blockSizeVertical * 1, - ), - const LoginViaQr(text: emergencyReponseLabel), - SizedBox( - height: blockSizeVertical * 1, - ), - // REQUEST SOS - SizedBox( - height: screenHeight * .07, - width: MediaQuery.of(context).size.width, - child: ElevatedButton.icon( - icon: const Icon( - FontAwesome5.life_ring, - color: Colors.white, - ), - style: mainBtnStyle(third, - Colors.transparent, Colors.white38), - onPressed: () { - }, - label: const Text( - requestSOS, - style: TextStyle(color: Colors.white), - )), - ) - ], - ), - ), - ), - ), - ], - ), - ), - ); - }else{ - return Update(apkVersion: state.apkVersion!,currenVersion: state.versionInfo!.version!,); - } - - }); - } - if (state is UserError) { - return ErrorState( - message: state.message, - ); - } - if (state is SplashScreen) { - return const UniTSplashScreen(); - } - return Container(); - }), - ), - ), - ); - } -} diff --git a/unit2/web/favicon.png b/web/favicon.png similarity index 100% rename from unit2/web/favicon.png rename to web/favicon.png diff --git a/unit2/web/icons/Icon-192.png b/web/icons/Icon-192.png similarity index 100% rename from unit2/web/icons/Icon-192.png rename to web/icons/Icon-192.png diff --git a/unit2/web/icons/Icon-512.png b/web/icons/Icon-512.png similarity index 100% rename from unit2/web/icons/Icon-512.png rename to web/icons/Icon-512.png diff --git a/unit2/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png similarity index 100% rename from unit2/web/icons/Icon-maskable-192.png rename to web/icons/Icon-maskable-192.png diff --git a/unit2/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png similarity index 100% rename from unit2/web/icons/Icon-maskable-512.png rename to web/icons/Icon-maskable-512.png diff --git a/unit2/web/index.html b/web/index.html similarity index 100% rename from unit2/web/index.html rename to web/index.html diff --git a/unit2/web/manifest.json b/web/manifest.json similarity index 100% rename from unit2/web/manifest.json rename to web/manifest.json diff --git a/unit2/windows/.gitignore b/windows/.gitignore similarity index 100% rename from unit2/windows/.gitignore rename to windows/.gitignore diff --git a/unit2/windows/CMakeLists.txt b/windows/CMakeLists.txt similarity index 100% rename from unit2/windows/CMakeLists.txt rename to windows/CMakeLists.txt diff --git a/unit2/windows/flutter/CMakeLists.txt b/windows/flutter/CMakeLists.txt similarity index 100% rename from unit2/windows/flutter/CMakeLists.txt rename to windows/flutter/CMakeLists.txt diff --git a/unit2/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc similarity index 100% rename from unit2/windows/flutter/generated_plugin_registrant.cc rename to windows/flutter/generated_plugin_registrant.cc diff --git a/unit2/windows/flutter/generated_plugin_registrant.h b/windows/flutter/generated_plugin_registrant.h similarity index 100% rename from unit2/windows/flutter/generated_plugin_registrant.h rename to windows/flutter/generated_plugin_registrant.h diff --git a/unit2/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake similarity index 100% rename from unit2/windows/flutter/generated_plugins.cmake rename to windows/flutter/generated_plugins.cmake diff --git a/unit2/windows/runner/CMakeLists.txt b/windows/runner/CMakeLists.txt similarity index 100% rename from unit2/windows/runner/CMakeLists.txt rename to windows/runner/CMakeLists.txt diff --git a/unit2/windows/runner/Runner.rc b/windows/runner/Runner.rc similarity index 100% rename from unit2/windows/runner/Runner.rc rename to windows/runner/Runner.rc diff --git a/unit2/windows/runner/flutter_window.cpp b/windows/runner/flutter_window.cpp similarity index 100% rename from unit2/windows/runner/flutter_window.cpp rename to windows/runner/flutter_window.cpp diff --git a/unit2/windows/runner/flutter_window.h b/windows/runner/flutter_window.h similarity index 100% rename from unit2/windows/runner/flutter_window.h rename to windows/runner/flutter_window.h diff --git a/unit2/windows/runner/main.cpp b/windows/runner/main.cpp similarity index 100% rename from unit2/windows/runner/main.cpp rename to windows/runner/main.cpp diff --git a/unit2/windows/runner/resource.h b/windows/runner/resource.h similarity index 100% rename from unit2/windows/runner/resource.h rename to windows/runner/resource.h diff --git a/unit2/windows/runner/resources/app_icon.ico b/windows/runner/resources/app_icon.ico similarity index 100% rename from unit2/windows/runner/resources/app_icon.ico rename to windows/runner/resources/app_icon.ico diff --git a/unit2/windows/runner/runner.exe.manifest b/windows/runner/runner.exe.manifest similarity index 100% rename from unit2/windows/runner/runner.exe.manifest rename to windows/runner/runner.exe.manifest diff --git a/unit2/windows/runner/utils.cpp b/windows/runner/utils.cpp similarity index 100% rename from unit2/windows/runner/utils.cpp rename to windows/runner/utils.cpp diff --git a/unit2/windows/runner/utils.h b/windows/runner/utils.h similarity index 100% rename from unit2/windows/runner/utils.h rename to windows/runner/utils.h diff --git a/unit2/windows/runner/win32_window.cpp b/windows/runner/win32_window.cpp similarity index 100% rename from unit2/windows/runner/win32_window.cpp rename to windows/runner/win32_window.cpp diff --git a/unit2/windows/runner/win32_window.h b/windows/runner/win32_window.h similarity index 100% rename from unit2/windows/runner/win32_window.h rename to windows/runner/win32_window.h From 018816c660cfe27892ee35023196d2ce3e97e051 Mon Sep 17 00:00:00 2001 From: rodolfobacuinjr Date: Thu, 26 Jan 2023 10:15:00 +0800 Subject: [PATCH 20/86] Initial design for profile --- lib/screens/profile/components/main_menu.dart | 27 ++++ lib/screens/profile/components/submenu.dart | 19 +++ lib/screens/profile/profile.dart | 146 ++++++++++++++++++ .../unit2/homepage.dart/module-screen.dart | 1 + lib/utils/app_router.dart | 19 ++- pubspec.lock | 7 + pubspec.yaml | 1 + 7 files changed, 213 insertions(+), 7 deletions(-) create mode 100644 lib/screens/profile/components/main_menu.dart create mode 100644 lib/screens/profile/components/submenu.dart create mode 100644 lib/screens/profile/profile.dart diff --git a/lib/screens/profile/components/main_menu.dart b/lib/screens/profile/components/main_menu.dart new file mode 100644 index 0000000..d315a7d --- /dev/null +++ b/lib/screens/profile/components/main_menu.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; + +class MainMenu extends StatelessWidget { + final IconData icon; + final String title; + const MainMenu({ + required this.icon, + required this.title, + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return ListTile( + leading: Icon( + icon, + color: primary, + ), + title: Text( + title, + style: TextStyle(fontWeight: FontWeight.bold), + ), + trailing: const Icon(Icons.keyboard_arrow_right), + ); + } +} diff --git a/lib/screens/profile/components/submenu.dart b/lib/screens/profile/components/submenu.dart new file mode 100644 index 0000000..72fa2d2 --- /dev/null +++ b/lib/screens/profile/components/submenu.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; + +ListTile subMenu(IconData icon, String title) { + return ListTile( + leading: Container( + margin: const EdgeInsets.only(left: 20), + child: Icon( + icon, + size: 20, + color: primary, + )), + title: Text( + title, + style: const TextStyle(), + ), + trailing: const Icon(Icons.keyboard_arrow_right), + ); +} diff --git a/lib/screens/profile/profile.dart b/lib/screens/profile/profile.dart new file mode 100644 index 0000000..d3ab4a3 --- /dev/null +++ b/lib/screens/profile/profile.dart @@ -0,0 +1,146 @@ +import 'package:expandable_group/expandable_group_widget.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/container.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:fluttericon/brandico_icons.dart'; +import 'package:fluttericon/elusive_icons.dart'; +import 'package:fluttericon/entypo_icons.dart'; +import 'package:fluttericon/font_awesome5_icons.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:fluttericon/modern_pictograms_icons.dart'; +import 'package:fluttericon/typicons_icons.dart'; +import 'package:unit2/bloc/bloc/user_bloc.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; + +import 'components/main_menu.dart'; +import 'components/submenu.dart'; + +class ProfileInfo extends StatefulWidget { + const ProfileInfo({super.key}); + + @override + State createState() => _ProfileInfoState(); +} + +class _ProfileInfoState extends State { + @override + Widget build(BuildContext context) { + return SafeArea( + child: Scaffold( + appBar: AppBar( + backgroundColor: primary, + centerTitle: true, + title: const Text('Profile'), + ), + body: BlocConsumer( + listener: (context, state) { + // TODO: implement listener + }, + builder: (context, state) { + if (state is UserLoggedIn) { + return Container( + padding: const EdgeInsets.symmetric( + vertical: 12, horizontal: 12), + child: ListView( + children: [ + ExpandableGroup( + collapsedIcon: + const Icon(Icons.keyboard_arrow_down), + expandedIcon: const Icon(Icons.keyboard_arrow_up), + header: const ListTile( + leading: Icon( + Elusive.address_book, + color: primary, + ), + title: Text( + "Basic Information", + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + items: [ + subMenu(Icons.person, "Primary"), + subMenu(Icons.home, "Home Addresses"), + subMenu(Icons.contact_mail, "Identifications"), + subMenu(Icons.contact_phone, "Contact Info"), + subMenu(Icons.flag, "Citizenships"), + ]), + const MainMenu( + icon: Elusive.group, + title: "Family", + ), + const MainMenu( + icon: FontAwesome5.graduation_cap, + title: "Education", + ), + const MainMenu( + icon: Icons.stars, + title: "Eligibility", + ), + const MainMenu( + icon: FontAwesome5.shopping_bag, + title: "Work History", + ), + const MainMenu( + icon: FontAwesome5.walking, + title: "Voluntary Work & Civic Services", + ), + const MainMenu( + icon: Elusive.lightbulb, + title: "Learning & Development", + ), + const MainMenu( + icon: Brandico.codepen, + title: "Personal References", + ), + ExpandableGroup( + collapsedIcon: + const Icon(Icons.keyboard_arrow_down), + expandedIcon: const Icon(Icons.keyboard_arrow_up), + header: const ListTile( + leading: Icon( + Icons.info, + color: primary, + ), + title: Text( + "Other Information", + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + items: [ + subMenu(Icons.fitness_center, "Skills & Hobbies"), + subMenu(FontAwesome5.certificate, + "Organization Memberships"), + subMenu( + Entypo.doc_text, "Non-Academic Recognitions"), + ]), + ExpandableGroup( + collapsedIcon: + const Icon(Icons.keyboard_arrow_down), + expandedIcon: const Icon(Icons.keyboard_arrow_up), + header: const ListTile( + leading: Icon( + FontAwesome5.laptop_house, + color: primary, + ), + title: Text( + "Assets", + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + items: [ + subMenu( + ModernPictograms.home, "Real Property Tax"), + ]), + ], + ), + ); + } + return const Center( + child: Text("default"), + ); + }, + ))); + } +} diff --git a/lib/screens/unit2/homepage.dart/module-screen.dart b/lib/screens/unit2/homepage.dart/module-screen.dart index 96ed319..5626247 100644 --- a/lib/screens/unit2/homepage.dart/module-screen.dart +++ b/lib/screens/unit2/homepage.dart/module-screen.dart @@ -32,6 +32,7 @@ class _MainScreenState extends State { }, child: BlocBuilder(builder: (context, state) { if (state is UserLoggedIn) { + print(state.userData!.user!.login!.token); state.userData!.user!.login!.user!.roles!.forEach((role) { Role? getRole = role; role!.modules!.forEach((module) { diff --git a/lib/utils/app_router.dart b/lib/utils/app_router.dart index 97750b2..e6bc31b 100644 --- a/lib/utils/app_router.dart +++ b/lib/utils/app_router.dart @@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:unit2/bloc/bloc/user_bloc.dart'; import 'package:unit2/screens/unit2/login/login.dart'; import 'package:unit2/utils/global_context.dart'; +import '../screens/profile/profile.dart'; import '../screens/unit2/basic-info/basic-info.dart'; import '../screens/unit2/homepage.dart/components/drawer-screen.dart'; import '../screens/unit2/login/qr_login.dart'; @@ -18,18 +19,22 @@ class AppRouter { return const UniT2Login(); }); case '/module-screen': - // BlocProvider.of( NavigationService.navigatorKey.currentContext!).add(LoadLoggedInUser()); + // BlocProvider.of( NavigationService.navigatorKey.currentContext!).add(LoadLoggedInUser()); return MaterialPageRoute(builder: (_) { return const DrawerScreen(); }); case '/basic-info': - return MaterialPageRoute(builder: (_){ - return const BasicInfo(); - }); + return MaterialPageRoute(builder: (_) { + return const BasicInfo(); + }); case '/qr-login': - return MaterialPageRoute(builder: (_){ - return const QRLogin(); - }); + return MaterialPageRoute(builder: (_) { + return const QRLogin(); + }); + case '/profile': + return MaterialPageRoute(builder: (_) { + return const ProfileInfo(); + }); default: return MaterialPageRoute(builder: (context) { return Container(); diff --git a/pubspec.lock b/pubspec.lock index e894f88..42cf4b1 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -176,6 +176,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.5" + expandable_group: + dependency: "direct main" + description: + name: expandable_group + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.8" fake_async: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 8f5fcac..f989e51 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -66,6 +66,7 @@ dependencies: dio: ^4.0.6 cool_alert: ^1.1.0 permission_handler: ^10.2.0 + expandable_group: ^0.0.8 dev_dependencies: flutter_test: From d919051a03e94537ebd4b6f221baedd2101a9446 Mon Sep 17 00:00:00 2001 From: rodolfobacuinjr Date: Thu, 26 Jan 2023 13:35:07 +0800 Subject: [PATCH 21/86] finalize design for profile --- lib/screens/profile/profile.dart | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/screens/profile/profile.dart b/lib/screens/profile/profile.dart index d3ab4a3..672d8c1 100644 --- a/lib/screens/profile/profile.dart +++ b/lib/screens/profile/profile.dart @@ -45,6 +45,7 @@ class _ProfileInfoState extends State { vertical: 12, horizontal: 12), child: ListView( children: [ + const Text("View and Update your Profile Information"), ExpandableGroup( collapsedIcon: const Icon(Icons.keyboard_arrow_down), @@ -66,30 +67,37 @@ class _ProfileInfoState extends State { subMenu(Icons.contact_phone, "Contact Info"), subMenu(Icons.flag, "Citizenships"), ]), + const Divider(), const MainMenu( icon: Elusive.group, title: "Family", ), + const Divider(), const MainMenu( icon: FontAwesome5.graduation_cap, title: "Education", ), + const Divider(), const MainMenu( icon: Icons.stars, title: "Eligibility", ), + const Divider(), const MainMenu( icon: FontAwesome5.shopping_bag, title: "Work History", ), + const Divider(), const MainMenu( icon: FontAwesome5.walking, title: "Voluntary Work & Civic Services", ), + const Divider(), const MainMenu( icon: Elusive.lightbulb, title: "Learning & Development", ), + const Divider(), const MainMenu( icon: Brandico.codepen, title: "Personal References", From 25d3d1b3cde5c519e52c5e0d7e1e3fd50e94f2a0 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Wed, 1 Feb 2023 16:03:05 +0800 Subject: [PATCH 22/86] primary information and contact information api integration --- ios/Flutter/Debug.xcconfig | 1 + ios/Flutter/Release.xcconfig | 1 + ios/Podfile | 41 ++ ios/Podfile.lock | 87 ++++ ios/Runner.xcodeproj/project.pbxproj | 72 ++- .../contents.xcworkspacedata | 3 + lib/bloc/profile/profile_bloc.dart | 30 ++ lib/bloc/profile/profile_event.dart | 23 + lib/bloc/profile/profile_state.dart | 29 ++ lib/bloc/{bloc => user}/user_bloc.dart | 0 lib/bloc/{bloc => user}/user_event.dart | 0 lib/bloc/{bloc => user}/user_state.dart | 0 lib/main.dart | 2 +- lib/model/profile/basic_info.dart | 9 + .../contact_information.dart | 180 ++++++++ .../primary-information.dart | 95 ++++ lib/model/profile/profileInfomation.dart | 7 + .../contact_information.dart | 80 ++++ .../primary_information.dart | 163 +++++++ .../profile/components/loading_screen.dart | 132 ++++++ lib/screens/profile/components/submenu.dart | 3 +- lib/screens/profile/profile.dart | 263 ++++++----- lib/screens/unit2/basic-info/basic-info.dart | 2 +- .../homepage.dart/components/dashboard.dart | 4 +- .../components/drawer-screen.dart | 2 +- .../unit2/homepage.dart/components/menu.dart | 13 +- .../unit2/homepage.dart/module-screen.dart | 7 +- .../login/components/update_required.dart | 2 +- lib/screens/unit2/login/login.dart | 17 +- lib/screens/unit2/login/qr_login.dart | 3 +- lib/sevices/login_service/auth_service.dart | 8 +- lib/sevices/profile/profile_service.dart | 50 ++ lib/utils/app_router.dart | 10 +- lib/utils/request.dart | 5 +- lib/utils/urls.dart | 19 +- macos/Flutter/Flutter-Debug.xcconfig | 1 + macos/Flutter/Flutter-Release.xcconfig | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 4 +- macos/Podfile | 40 ++ pubspec.lock | 436 +++++++++++------- pubspec.yaml | 1 + 41 files changed, 1524 insertions(+), 322 deletions(-) create mode 100644 ios/Podfile create mode 100644 ios/Podfile.lock create mode 100644 lib/bloc/profile/profile_bloc.dart create mode 100644 lib/bloc/profile/profile_event.dart create mode 100644 lib/bloc/profile/profile_state.dart rename lib/bloc/{bloc => user}/user_bloc.dart (100%) rename lib/bloc/{bloc => user}/user_event.dart (100%) rename lib/bloc/{bloc => user}/user_state.dart (100%) create mode 100644 lib/model/profile/basic_info.dart create mode 100644 lib/model/profile/basic_information/contact_information.dart create mode 100644 lib/model/profile/basic_information/primary-information.dart create mode 100644 lib/model/profile/profileInfomation.dart create mode 100644 lib/screens/profile/components/basic_information/contact_information.dart create mode 100644 lib/screens/profile/components/basic_information/primary_information.dart create mode 100644 lib/screens/profile/components/loading_screen.dart create mode 100644 lib/sevices/profile/profile_service.dart create mode 100644 macos/Podfile diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig index 592ceee..ec97fc6 100644 --- a/ios/Flutter/Debug.xcconfig +++ b/ios/Flutter/Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig index 592ceee..c4855bf 100644 --- a/ios/Flutter/Release.xcconfig +++ b/ios/Flutter/Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/ios/Podfile b/ios/Podfile new file mode 100644 index 0000000..88359b2 --- /dev/null +++ b/ios/Podfile @@ -0,0 +1,41 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '11.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/ios/Podfile.lock b/ios/Podfile.lock new file mode 100644 index 0000000..8fa5b72 --- /dev/null +++ b/ios/Podfile.lock @@ -0,0 +1,87 @@ +PODS: + - barcode_scan2 (0.0.1): + - Flutter + - MTBBarcodeScanner + - SwiftProtobuf + - easy_app_installer (0.0.1): + - Flutter + - Flutter (1.0.0) + - fluttertoast (0.0.2): + - Flutter + - Toast + - FMDB (2.7.5): + - FMDB/standard (= 2.7.5) + - FMDB/standard (2.7.5) + - MTBBarcodeScanner (5.0.11) + - package_info_plus (0.4.5): + - Flutter + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS + - permission_handler_apple (9.0.4): + - Flutter + - shared_preferences_foundation (0.0.1): + - Flutter + - FlutterMacOS + - sqflite (0.0.2): + - Flutter + - FMDB (>= 2.7.5) + - SwiftProtobuf (1.20.3) + - Toast (4.0.0) + +DEPENDENCIES: + - barcode_scan2 (from `.symlinks/plugins/barcode_scan2/ios`) + - easy_app_installer (from `.symlinks/plugins/easy_app_installer/ios`) + - Flutter (from `Flutter`) + - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) + - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`) + - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) + - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`) + - sqflite (from `.symlinks/plugins/sqflite/ios`) + +SPEC REPOS: + trunk: + - FMDB + - MTBBarcodeScanner + - SwiftProtobuf + - Toast + +EXTERNAL SOURCES: + barcode_scan2: + :path: ".symlinks/plugins/barcode_scan2/ios" + easy_app_installer: + :path: ".symlinks/plugins/easy_app_installer/ios" + Flutter: + :path: Flutter + fluttertoast: + :path: ".symlinks/plugins/fluttertoast/ios" + package_info_plus: + :path: ".symlinks/plugins/package_info_plus/ios" + path_provider_foundation: + :path: ".symlinks/plugins/path_provider_foundation/ios" + permission_handler_apple: + :path: ".symlinks/plugins/permission_handler_apple/ios" + shared_preferences_foundation: + :path: ".symlinks/plugins/shared_preferences_foundation/ios" + sqflite: + :path: ".symlinks/plugins/sqflite/ios" + +SPEC CHECKSUMS: + barcode_scan2: 0af2bb63c81b4565aab6cd78278e4c0fa136dbb0 + easy_app_installer: 29abe397da7d86721fee853281202f414373f45c + Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + fluttertoast: eb263d302cc92e04176c053d2385237e9f43fad0 + FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a + MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb + package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e + path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852 + permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce + shared_preferences_foundation: 297b3ebca31b34ec92be11acd7fb0ba932c822ca + sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 + SwiftProtobuf: b02b5075dcf60c9f5f403000b3b0c202a11b6ae1 + Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 + +PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 + +COCOAPODS: 1.11.3 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 65a38e0..c8b8a8f 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -3,11 +3,12 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 28FEB91C01FFFF1F3AF03958 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2946B4F99A9B9A85D9A4DC4F /* Pods_Runner.framework */; }; 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 */; }; @@ -31,7 +32,11 @@ /* 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 = ""; }; + 24CA4A0209E683A311335098 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 2946B4F99A9B9A85D9A4DC4F /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74181747FCF55E0339265087 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 7435055C04EA907D4DF9DEFB /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; 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 = ""; }; @@ -49,12 +54,24 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 28FEB91C01FFFF1F3AF03958 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 53E3B75A4BA5D03DDDC74903 /* Pods */ = { + isa = PBXGroup; + children = ( + 74181747FCF55E0339265087 /* Pods-Runner.debug.xcconfig */, + 7435055C04EA907D4DF9DEFB /* Pods-Runner.release.xcconfig */, + 24CA4A0209E683A311335098 /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -72,6 +89,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, + 53E3B75A4BA5D03DDDC74903 /* Pods */, + B81885F5B040F93E6F30E902 /* Frameworks */, ); sourceTree = ""; }; @@ -98,6 +117,14 @@ path = Runner; sourceTree = ""; }; + B81885F5B040F93E6F30E902 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 2946B4F99A9B9A85D9A4DC4F /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -105,12 +132,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + 961512EDE7FE1573CFAF92FB /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + CC94AAA4981E9E5E5B1BC9E4 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -171,6 +200,7 @@ /* Begin PBXShellScriptBuildPhase section */ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -183,8 +213,31 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; + 961512EDE7FE1573CFAF92FB /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -197,6 +250,23 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; + CC94AAA4981E9E5E5B1BC9E4 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata index 1d526a1..21a3cc1 100644 --- a/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/lib/bloc/profile/profile_bloc.dart b/lib/bloc/profile/profile_bloc.dart new file mode 100644 index 0000000..f357ecc --- /dev/null +++ b/lib/bloc/profile/profile_bloc.dart @@ -0,0 +1,30 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/model/profile/basic_info.dart'; +import 'package:unit2/model/profile/basic_information/primary-information.dart'; +import 'package:unit2/model/profile/profileInfomation.dart'; +import 'package:unit2/sevices/profile/profile_service.dart'; + +import '../../model/profile/basic_info.dart'; +import '../../model/profile/basic_info.dart'; + +part 'profile_event.dart'; +part 'profile_state.dart'; + +class ProfileBloc extends Bloc { + ProfileBloc() : super(ProfileInitial()) { + ProfileInformation? _profileInformation; + on((event, emit) async { + try { + emit(ProfileLoading()); + ProfileInformation? profileInformation = + await ProfileService.instance.getProfile(event.token, event.userID); + _profileInformation = profileInformation; + emit(ProfileLoaded( + profileInformation: _profileInformation!)); + } catch (e) { + emit(ProfileErrorState(mesage: e.toString())); + } + }); + } +} diff --git a/lib/bloc/profile/profile_event.dart b/lib/bloc/profile/profile_event.dart new file mode 100644 index 0000000..e978beb --- /dev/null +++ b/lib/bloc/profile/profile_event.dart @@ -0,0 +1,23 @@ +part of 'profile_bloc.dart'; + +abstract class ProfileEvent extends Equatable { + const ProfileEvent(); + + @override + List get props => []; +} + +class LoadProfile extends ProfileEvent{ +final String token; +final int userID; +const LoadProfile({required this.token, required this.userID}); + @override + List get props => [token,userID]; + +} + +class LoadProfileInformation extends ProfileEvent{ + @override + List get props => []; +} + diff --git a/lib/bloc/profile/profile_state.dart b/lib/bloc/profile/profile_state.dart new file mode 100644 index 0000000..ae753d5 --- /dev/null +++ b/lib/bloc/profile/profile_state.dart @@ -0,0 +1,29 @@ +part of 'profile_bloc.dart'; + +abstract class ProfileState extends Equatable { + const ProfileState(); + + @override + List get props => []; +} + +class ProfileInitial extends ProfileState {} + +class ProfileLoaded extends ProfileState{ + final ProfileInformation profileInformation; + const ProfileLoaded({required this.profileInformation}); + @override + List get props => [profileInformation]; +} + +class ProfileErrorState extends ProfileState{ + final String mesage; + const ProfileErrorState({required this.mesage}); + @override + List get props => [mesage]; +} + +class ProfileLoading extends ProfileState{ + +} + diff --git a/lib/bloc/bloc/user_bloc.dart b/lib/bloc/user/user_bloc.dart similarity index 100% rename from lib/bloc/bloc/user_bloc.dart rename to lib/bloc/user/user_bloc.dart diff --git a/lib/bloc/bloc/user_event.dart b/lib/bloc/user/user_event.dart similarity index 100% rename from lib/bloc/bloc/user_event.dart rename to lib/bloc/user/user_event.dart diff --git a/lib/bloc/bloc/user_state.dart b/lib/bloc/user/user_state.dart similarity index 100% rename from lib/bloc/bloc/user_state.dart rename to lib/bloc/user/user_state.dart diff --git a/lib/main.dart b/lib/main.dart index be88b93..d41d56d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:device_preview/device_preview.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:unit2/bloc/bloc/user_bloc.dart'; +import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/utils/app_router.dart'; import 'package:unit2/utils/global_context.dart'; import 'package:unit2/utils/global_context.dart'; diff --git a/lib/model/profile/basic_info.dart b/lib/model/profile/basic_info.dart new file mode 100644 index 0000000..fda9bc1 --- /dev/null +++ b/lib/model/profile/basic_info.dart @@ -0,0 +1,9 @@ +import 'package:unit2/model/profile/basic_information/contact_information.dart'; +import 'package:unit2/model/profile/basic_information/primary-information.dart'; + +class BasicInfo{ + PrimaryInformation primaryInformation; + List contactInformation; + BasicInfo({required this.contactInformation, required this.primaryInformation}); + +} \ No newline at end of file diff --git a/lib/model/profile/basic_information/contact_information.dart b/lib/model/profile/basic_information/contact_information.dart new file mode 100644 index 0000000..e7f9d7a --- /dev/null +++ b/lib/model/profile/basic_information/contact_information.dart @@ -0,0 +1,180 @@ +// To parse this JSON data, do +// +// final contactInformation = contactInformationFromJson(jsonString); + + +class ContactInfo { + ContactInfo({ + required this.id, + required this.active, + required this.primary, + required this.numbermail, + required this.commService, + }); + + int? id; + bool? active; + bool? primary; + String? numbermail; + CommService? commService; + + factory ContactInfo.fromJson(Map json) => ContactInfo( + id: json["id"], + active: json["active"], + primary: json["primary"], + numbermail: json["numbermail"], + commService: json["comm_service"]==null?null: CommService.fromJson(json["comm_service"]), + ); + + Map toJson() => { + "id": id, + "active": active, + "primary": primary, + "numbermail": numbermail, + "comm_service": commService!.toJson(), + }; +} + +class CommService { + CommService({ + required this.id, + required this.serviceType, + required this.serviceProvider, + }); + + int? id; + ServiceType? serviceType; + ServiceProvider? serviceProvider; + + factory CommService.fromJson(Map json) => CommService( + id: json["id"], + serviceType: ServiceType.fromJson(json["service_type"]), + serviceProvider: ServiceProvider.fromJson(json["service_provider"]), + ); + + Map toJson() => { + "id": id, + "service_type": serviceType!.toJson(), + "service_provider": serviceProvider!.toJson(), + }; +} + +class ServiceProvider { + ServiceProvider({ + required this.id, + required this.alias, + required this.agency, + }); + + int? id; + String? alias; + Agency? agency; + + factory ServiceProvider.fromJson(Map json) => ServiceProvider( + id: json["id"], + alias: json["alias"], + agency: Agency.fromJson(json["agency"]), + ); + + Map toJson() => { + "id": id, + "alias": alias, + "agency": agency!.toJson(), + }; +} + +class Agency { + Agency({ + required this.id, + required this.name, + required this.category, + required this.privateEntity, + }); + + int? id; + String? name; + Category? category; + bool? privateEntity; + + factory Agency.fromJson(Map json) => Agency( + id: json["id"], + name: json["name"], + category: Category.fromJson(json["category"]), + privateEntity: json["private_entity"], + ); + + Map toJson() => { + "id": id, + "name": name, + "category": category!.toJson(), + "private_entity": privateEntity, + }; +} + +class Category { + Category({ + required this.id, + required this.name, + required this.industryClass, + }); + + int? id; + String? name; + IndustryClass? industryClass; + + factory Category.fromJson(Map json) => Category( + id: json["id"], + name: json["name"], + industryClass: IndustryClass.fromJson(json["industry_class"]), + ); + + Map toJson() => { + "id": id, + "name": name, + "industry_class": industryClass!.toJson(), + }; +} + +class IndustryClass { + IndustryClass({ + required this.id, + required this.name, + this.description, + }); + + int? id; + String? name; + String? description; + + factory IndustryClass.fromJson(Map json) => IndustryClass( + id: json["id"], + name: json["name"], + description: json["description"], + ); + + Map toJson() => { + "id": id, + "name": name, + "description": description, + }; +} + +class ServiceType { + ServiceType({ + required this.id, + required this.name, + }); + + int? id; + String? name; + + factory ServiceType.fromJson(Map json) => ServiceType( + id: json["id"], + name: json["name"], + ); + + Map toJson() => { + "id": id, + "name": name, + }; +} diff --git a/lib/model/profile/basic_information/primary-information.dart b/lib/model/profile/basic_information/primary-information.dart new file mode 100644 index 0000000..adb26f8 --- /dev/null +++ b/lib/model/profile/basic_information/primary-information.dart @@ -0,0 +1,95 @@ +import 'dart:convert'; + +class PrimaryInformation { + PrimaryInformation({ + required this.id, + required this.lastName, + required this.firstName, + required this.middleName, + required this.nameExtension, + required this.sex, + required this.birthdate, + required this.civilStatus, + required this.bloodType, + required this.heightM, + required this.weightKg, + required this.photoPath, + required this.esigPath, + required this.maidenName, + required this.deceased, + required this.gender, + required this.uuidQrcode, + required this.titlePrefix, + required this.titleSuffix, + required this.showTitleId, + }); + + int? id; + String? lastName; + String? firstName; + String? middleName; + String? nameExtension; + String? sex; + DateTime? birthdate; + String? civilStatus; + String? bloodType; + double? heightM; + double? weightKg; + String? photoPath; + String? esigPath; + String? maidenName; + bool? deceased; + String? gender; + String? uuidQrcode; + String? titlePrefix; + String? titleSuffix; + bool? showTitleId; + + factory PrimaryInformation.fromJson(Map json) => + PrimaryInformation( + id: json["id"], + lastName: json["last_name"], + firstName: json["first_name"], + middleName: json["middle_name"], + nameExtension: json["name_extension"], + sex: json["sex"], + birthdate: json['birthdate']==null?null:DateTime.parse(json["birthdate"]), + civilStatus: json["civil_status"], + bloodType: json["blood_type"], + heightM: json["height_m"]?.toDouble(), + weightKg: json["weight_kg"], + photoPath: json["photo_path"], + esigPath: json["esig_path"], + maidenName: json["maiden_name"], + deceased: json["deceased"], + gender: json["gender"], + uuidQrcode: json["uuid_qrcode"], + titlePrefix: json["title_prefix"], + titleSuffix: json["title_suffix"], + showTitleId: json["show_title_id"], + ); + + Map toJson() => { + "id": id, + "last_name": lastName, + "first_name": firstName, + "middle_name": middleName, + "name_extension": nameExtension, + "sex": sex, + "birthdate": + "${birthdate!.year.toString().padLeft(4, '0')}-${birthdate!.month.toString().padLeft(2, '0')}-${birthdate!.day.toString().padLeft(2, '0')}", + "civil_status": civilStatus, + "blood_type": bloodType, + "height_m": heightM, + "weight_kg": weightKg, + "photo_path": photoPath, + "esig_path": esigPath, + "maiden_name": maidenName, + "deceased": deceased, + "gender": gender, + "uuid_qrcode": uuidQrcode, + "title_prefix": titlePrefix, + "title_suffix": titleSuffix, + "show_title_id": showTitleId, + }; +} diff --git a/lib/model/profile/profileInfomation.dart b/lib/model/profile/profileInfomation.dart new file mode 100644 index 0000000..891b522 --- /dev/null +++ b/lib/model/profile/profileInfomation.dart @@ -0,0 +1,7 @@ +import 'package:unit2/model/profile/basic_info.dart'; +import 'package:unit2/model/profile/basic_information/primary-information.dart'; + +class ProfileInformation{ + BasicInfo basicInfo; + ProfileInformation({required this.basicInfo}); +} \ No newline at end of file diff --git a/lib/screens/profile/components/basic_information/contact_information.dart b/lib/screens/profile/components/basic_information/contact_information.dart new file mode 100644 index 0000000..9fa40aa --- /dev/null +++ b/lib/screens/profile/components/basic_information/contact_information.dart @@ -0,0 +1,80 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:unit2/model/profile/basic_information/contact_information.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; + +class ContactInformation extends StatefulWidget { + final List contacts; + const ContactInformation({super.key, required this.contacts}); + + @override + State createState() => _ContactInformationState(); +} + +class _ContactInformationState extends State { + @override + Widget build(BuildContext context) { + return SafeArea( + child: Scaffold( + appBar: AppBar( + title: const Text("Contact Information"), + centerTitle: true, + backgroundColor: primary, + ), + body: ListView.builder( + itemCount: widget.contacts.length, + itemBuilder: (BuildContext context, int index) { + return Container( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: const BorderRadius.all(Radius.circular(12))), + padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(widget.contacts[index].numbermail.toString(),style: Theme.of(context).textTheme.titleLarge,), + const SizedBox(height: 5,), + Row( + children: [ + Text(widget.contacts[index].commService! + .serviceProvider!.alias + .toString()), + const SizedBox(width: 15,), + widget.contacts[index].active==true? const Badge(backgroundColor: Colors.green, label: Text("Active",),):const SizedBox(), + const SizedBox(width: 8), + widget.contacts[index].primary==true? const Badge(backgroundColor: Colors.blue, label: Text("Primary"),):const SizedBox() + ], + ), + const SizedBox(height: 5,), + Text(widget.contacts[index].commService! + .serviceProvider!.agency!.name + .toString()), + + ]), + ), + IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)) + ], + ), + ), + const SizedBox(height: 5,), + + + + ], + ), + ); + }), + ), + ); + } +} diff --git a/lib/screens/profile/components/basic_information/primary_information.dart b/lib/screens/profile/components/basic_information/primary_information.dart new file mode 100644 index 0000000..f8243a0 --- /dev/null +++ b/lib/screens/profile/components/basic_information/primary_information.dart @@ -0,0 +1,163 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:intl/intl.dart'; +import 'package:unit2/model/profile/basic_information/primary-information.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/theme-data.dart/form-style.dart'; +import 'package:unit2/utils/global.dart'; + +class PrimaryInfo extends StatefulWidget { + final PrimaryInformation primaryInformation; + const PrimaryInfo({super.key, required this.primaryInformation}); + + @override + State createState() => _PrimaryInfoState(); +} + +class _PrimaryInfoState extends State { + @override + Widget build(BuildContext context) { + final _formKey = GlobalKey(); + bool enabled = false; + DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); + return SafeArea( + child: Scaffold( + appBar: AppBar( + title: const Text("Primary Information"), + centerTitle: true, + backgroundColor: primary, + ), + body: Container( + padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 24), + child: FormBuilder( + child: Column( + children: [ + FormBuilderTextField( + enabled: enabled, + name: 'lastname', + initialValue: widget.primaryInformation.lastName!, + decoration: normalTextFieldStyle("Last name", ""), + + ), + const SizedBox(height: 15,), + FormBuilderTextField( + enabled: enabled, + name: 'firstname', + initialValue: widget.primaryInformation.firstName!, + decoration: normalTextFieldStyle("First name", ""), + ), + const SizedBox(height: 15,), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 2, + child: FormBuilderTextField( + enabled: enabled, + name: 'middlename', + initialValue: widget.primaryInformation.middleName!, + decoration: normalTextFieldStyle("Middle name", ""), + ),), + const SizedBox(width: 10,), + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: 'extension-name', + initialValue: widget.primaryInformation.nameExtension??='N/A', + decoration: normalTextFieldStyle("Name extension", ""), + ),) + ]), + + ), + const SizedBox(height: 15,), + SizedBox(width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: 'bday', + initialValue: dteFormat2.format(widget.primaryInformation.birthdate!), + decoration: normalTextFieldStyle("Birth date", ""), + ),), + const SizedBox(width: 10,), + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: 'sex', + initialValue: widget.primaryInformation.sex!, + decoration: normalTextFieldStyle("Sex", ""), + ),) + ]),), + const SizedBox(height: 15,), + SizedBox(width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: 'bloodtype', + initialValue:widget.primaryInformation.bloodType!, + decoration: normalTextFieldStyle("Blood type", ""), + ),), + const SizedBox(width: 10,), + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: 'civil-status', + initialValue: widget.primaryInformation.civilStatus!, + decoration: normalTextFieldStyle("Civil Status", ""), + ),), + const SizedBox(width: 10,), + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: 'gender', + initialValue:widget.primaryInformation.gender??="N/A", + decoration: normalTextFieldStyle("Gender", ""), + ),), + ]),), + + const SizedBox(height: 15,), + SizedBox(width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: 'height', + initialValue:widget.primaryInformation.heightM!.toString(), + decoration: normalTextFieldStyle("Height", ""), + ),), + const SizedBox(width: 10,), + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: 'Weight', + initialValue: widget.primaryInformation.weightKg!.toString(), + decoration: normalTextFieldStyle("Weight", ""), + ),), + const SizedBox(width: 10,), + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: 'prefix&suffix', + initialValue:"${widget.primaryInformation.titlePrefix??="NA"} | ${widget.primaryInformation.titleSuffix??="N/A"}", + decoration: normalTextFieldStyle("Title Prefix and Suffix", ""), + ),), + ]),), + ], + )), + )), + ); + } +} diff --git a/lib/screens/profile/components/loading_screen.dart b/lib/screens/profile/components/loading_screen.dart new file mode 100644 index 0000000..a84ebaf --- /dev/null +++ b/lib/screens/profile/components/loading_screen.dart @@ -0,0 +1,132 @@ +import 'package:expandable_group/expandable_group_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttericon/brandico_icons.dart'; +import 'package:fluttericon/elusive_icons.dart'; +import 'package:fluttericon/entypo_icons.dart'; +import 'package:fluttericon/font_awesome5_icons.dart'; +import 'package:fluttericon/modern_pictograms_icons.dart'; +import 'package:unit2/screens/profile/components/main_menu.dart'; +import 'package:unit2/screens/profile/components/submenu.dart'; + +import '../../../theme-data.dart/colors.dart'; + +class LoadingScreen extends StatelessWidget { + const LoadingScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric( + vertical: 12, horizontal: 12), + child: ListView( + children: [ + const Text( + "View and Update your Profile Information"), + ExpandableGroup( + collapsedIcon: + const Icon(Icons.keyboard_arrow_down), + expandedIcon: + const Icon(Icons.keyboard_arrow_up), + header: const ListTile( + leading: Icon( + Elusive.address_book, + color: primary, + ), + title: Text( + "Basic Information", + style: + TextStyle(fontWeight: FontWeight.bold), + ), + ), + items: [ + subMenu(Icons.person, "Primary",(){}), + subMenu(Icons.home, "Home Addresses",(){}), + subMenu( + Icons.contact_mail, "Identifications",(){}), + subMenu(Icons.contact_phone, "Contact Info",(){}), + subMenu(Icons.flag, "Citizenships",(){}), + ]), + const Divider(), + const MainMenu( + icon: Elusive.group, + title: "Family", + ), + const Divider(), + const MainMenu( + icon: FontAwesome5.graduation_cap, + title: "Education", + ), + const Divider(), + const MainMenu( + icon: Icons.stars, + title: "Eligibility", + ), + const Divider(), + const MainMenu( + icon: FontAwesome5.shopping_bag, + title: "Work History", + ), + const Divider(), + const MainMenu( + icon: FontAwesome5.walking, + title: "Voluntary Work & Civic Services", + ), + const Divider(), + const MainMenu( + icon: Elusive.lightbulb, + title: "Learning & Development", + ), + const Divider(), + const MainMenu( + icon: Brandico.codepen, + title: "Personal References", + ), + ExpandableGroup( + collapsedIcon: + const Icon(Icons.keyboard_arrow_down), + expandedIcon: + const Icon(Icons.keyboard_arrow_up), + header: const ListTile( + leading: Icon( + Icons.info, + color: primary, + ), + title: Text( + "Other Information", + style: + TextStyle(fontWeight: FontWeight.bold), + ), + ), + items: [ + subMenu( + Icons.fitness_center, "Skills & Hobbies",(){}), + subMenu(FontAwesome5.certificate, + "Organization Memberships",(){}), + subMenu(Entypo.doc_text, + "Non-Academic Recognitions",(){}), + ]), + ExpandableGroup( + collapsedIcon: + const Icon(Icons.keyboard_arrow_down), + expandedIcon: + const Icon(Icons.keyboard_arrow_up), + header: const ListTile( + leading: Icon( + FontAwesome5.laptop_house, + color: primary, + ), + title: Text( + "Assets", + style: + TextStyle(fontWeight: FontWeight.bold), + ), + ), + items: [ + subMenu(ModernPictograms.home, + "Real Property Tax",(){}), + ]), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/screens/profile/components/submenu.dart b/lib/screens/profile/components/submenu.dart index 72fa2d2..c412a36 100644 --- a/lib/screens/profile/components/submenu.dart +++ b/lib/screens/profile/components/submenu.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:unit2/theme-data.dart/colors.dart'; -ListTile subMenu(IconData icon, String title) { +ListTile subMenu(IconData icon, String title,Function() onTap) { return ListTile( leading: Container( margin: const EdgeInsets.only(left: 20), @@ -15,5 +15,6 @@ ListTile subMenu(IconData icon, String title) { style: const TextStyle(), ), trailing: const Icon(Icons.keyboard_arrow_right), + onTap: onTap, ); } diff --git a/lib/screens/profile/profile.dart b/lib/screens/profile/profile.dart index 672d8c1..8e7b6f0 100644 --- a/lib/screens/profile/profile.dart +++ b/lib/screens/profile/profile.dart @@ -1,19 +1,18 @@ import 'package:expandable_group/expandable_group_widget.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/container.dart'; -import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:fluttericon/brandico_icons.dart'; import 'package:fluttericon/elusive_icons.dart'; import 'package:fluttericon/entypo_icons.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; -import 'package:fluttericon/font_awesome_icons.dart'; import 'package:fluttericon/modern_pictograms_icons.dart'; -import 'package:fluttericon/typicons_icons.dart'; -import 'package:unit2/bloc/bloc/user_bloc.dart'; +import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/screens/profile/components/basic_information/contact_information.dart'; +import 'package:unit2/screens/profile/components/basic_information/primary_information.dart'; +import 'package:unit2/screens/profile/components/loading_screen.dart'; import 'package:unit2/theme-data.dart/colors.dart'; - +import '../../bloc/user/user_bloc.dart'; import 'components/main_menu.dart'; import 'components/submenu.dart'; @@ -34,121 +33,159 @@ class _ProfileInfoState extends State { centerTitle: true, title: const Text('Profile'), ), - body: BlocConsumer( - listener: (context, state) { - // TODO: implement listener - }, - builder: (context, state) { + body: ProgressHUD( + child: + BlocBuilder(builder: (context, state) { if (state is UserLoggedIn) { - return Container( - padding: const EdgeInsets.symmetric( - vertical: 12, horizontal: 12), - child: ListView( - children: [ - const Text("View and Update your Profile Information"), - ExpandableGroup( - collapsedIcon: - const Icon(Icons.keyboard_arrow_down), - expandedIcon: const Icon(Icons.keyboard_arrow_up), - header: const ListTile( - leading: Icon( - Elusive.address_book, - color: primary, + return BlocConsumer( + listener: (context, state) { + if (state is ProfileLoading) { + final progress = ProgressHUD.of(context); + progress?.showWithText( + 'Loading Profile', + ); + } + if (state is ProfileLoaded) { + final progress = ProgressHUD.of(context); + progress?.dismiss(); + } + }, + builder: (context, state) { + if (state is ProfileLoaded) { + + return Container( + padding: const EdgeInsets.symmetric( + vertical: 12, horizontal: 12), + child: ListView( + children: [ + const Text( + "View and Update your Profile Information"), + ExpandableGroup( + collapsedIcon: + const Icon(Icons.keyboard_arrow_down), + expandedIcon: + const Icon(Icons.keyboard_arrow_up), + header: const ListTile( + leading: Icon( + Elusive.address_book, + color: primary, + ), + title: Text( + "Basic Information", + style: TextStyle( + fontWeight: FontWeight.bold), + ), + ), + items: [ + subMenu(Icons.person, "Primary",(){ + Navigator.push(context,MaterialPageRoute(builder: (BuildContext context){ + return PrimaryInfo(primaryInformation: state.profileInformation.basicInfo.primaryInformation); + }) ); + }), + subMenu(Icons.home, "Home Addresses",(){}), + subMenu( + Icons.contact_mail, "Identifications",(){}), + subMenu( + Icons.contact_phone, "Contact Info",(){ + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + return ContactInformation(contacts: state.profileInformation.basicInfo.contactInformation,); + })); + }), + subMenu(Icons.flag, "Citizenships",(){}), + ]), + const Divider(), + const MainMenu( + icon: Elusive.group, + title: "Family", ), - title: Text( - "Basic Information", - style: TextStyle(fontWeight: FontWeight.bold), + const Divider(), + const MainMenu( + icon: FontAwesome5.graduation_cap, + title: "Education", ), - ), - items: [ - subMenu(Icons.person, "Primary"), - subMenu(Icons.home, "Home Addresses"), - subMenu(Icons.contact_mail, "Identifications"), - subMenu(Icons.contact_phone, "Contact Info"), - subMenu(Icons.flag, "Citizenships"), - ]), - const Divider(), - const MainMenu( - icon: Elusive.group, - title: "Family", - ), - const Divider(), - const MainMenu( - icon: FontAwesome5.graduation_cap, - title: "Education", - ), - const Divider(), - const MainMenu( - icon: Icons.stars, - title: "Eligibility", - ), - const Divider(), - const MainMenu( - icon: FontAwesome5.shopping_bag, - title: "Work History", - ), - const Divider(), - const MainMenu( - icon: FontAwesome5.walking, - title: "Voluntary Work & Civic Services", - ), - const Divider(), - const MainMenu( - icon: Elusive.lightbulb, - title: "Learning & Development", - ), - const Divider(), - const MainMenu( - icon: Brandico.codepen, - title: "Personal References", - ), - ExpandableGroup( - collapsedIcon: - const Icon(Icons.keyboard_arrow_down), - expandedIcon: const Icon(Icons.keyboard_arrow_up), - header: const ListTile( - leading: Icon( - Icons.info, - color: primary, + const Divider(), + const MainMenu( + icon: Icons.stars, + title: "Eligibility", ), - title: Text( - "Other Information", - style: TextStyle(fontWeight: FontWeight.bold), + const Divider(), + const MainMenu( + icon: FontAwesome5.shopping_bag, + title: "Work History", ), - ), - items: [ - subMenu(Icons.fitness_center, "Skills & Hobbies"), - subMenu(FontAwesome5.certificate, - "Organization Memberships"), - subMenu( - Entypo.doc_text, "Non-Academic Recognitions"), - ]), - ExpandableGroup( - collapsedIcon: - const Icon(Icons.keyboard_arrow_down), - expandedIcon: const Icon(Icons.keyboard_arrow_up), - header: const ListTile( - leading: Icon( - FontAwesome5.laptop_house, - color: primary, + const Divider(), + const MainMenu( + icon: FontAwesome5.walking, + title: "Voluntary Work & Civic Services", ), - title: Text( - "Assets", - style: TextStyle(fontWeight: FontWeight.bold), + const Divider(), + const MainMenu( + icon: Elusive.lightbulb, + title: "Learning & Development", ), - ), - items: [ - subMenu( - ModernPictograms.home, "Real Property Tax"), - ]), - ], - ), + const Divider(), + const MainMenu( + icon: Brandico.codepen, + title: "Personal References", + ), + ExpandableGroup( + collapsedIcon: + const Icon(Icons.keyboard_arrow_down), + expandedIcon: + const Icon(Icons.keyboard_arrow_up), + header: const ListTile( + leading: Icon( + Icons.info, + color: primary, + ), + title: Text( + "Other Information", + style: TextStyle( + fontWeight: FontWeight.bold), + ), + ), + items: [ + subMenu(Icons.fitness_center, + "Skills & Hobbies",(){}), + subMenu(FontAwesome5.certificate, + "Organization Memberships",(){}), + subMenu(Entypo.doc_text, + "Non-Academic Recognitions",(){}), + ]), + ExpandableGroup( + collapsedIcon: + const Icon(Icons.keyboard_arrow_down), + expandedIcon: + const Icon(Icons.keyboard_arrow_up), + header: const ListTile( + leading: Icon( + FontAwesome5.laptop_house, + color: primary, + ), + title: Text( + "Assets", + style: TextStyle( + fontWeight: FontWeight.bold), + ), + ), + items: [ + subMenu(ModernPictograms.home, + "Real Property Tax",(){}), + ]), + ], + ), + ); + } + if (state is ProfileLoading) { + return const LoadingScreen(); + } + + return Container(); + }, ); } - return const Center( - child: Text("default"), - ); - }, + return Container(); + }), ))); } } diff --git a/lib/screens/unit2/basic-info/basic-info.dart b/lib/screens/unit2/basic-info/basic-info.dart index 7374720..6459508 100644 --- a/lib/screens/unit2/basic-info/basic-info.dart +++ b/lib/screens/unit2/basic-info/basic-info.dart @@ -5,11 +5,11 @@ import 'package:flutter_svg/svg.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:intl/intl.dart'; import 'package:qr_flutter/qr_flutter.dart'; -import 'package:unit2/bloc/bloc/user_bloc.dart'; import 'package:unit2/model/login_data/user_info/user_data.dart'; import 'package:unit2/theme-data.dart/btn-style.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; +import '../../../bloc/user/user_bloc.dart'; import '../../../theme-data.dart/colors.dart'; import '../../../widgets/splash_screen.dart'; import './components/cover-image.dart'; diff --git a/lib/screens/unit2/homepage.dart/components/dashboard.dart b/lib/screens/unit2/homepage.dart/components/dashboard.dart index b1c591a..ecca003 100644 --- a/lib/screens/unit2/homepage.dart/components/dashboard.dart +++ b/lib/screens/unit2/homepage.dart/components/dashboard.dart @@ -1,7 +1,5 @@ import 'package:flutter/material.dart'; import 'package:unit2/screens/unit2/homepage.dart/module-screen.dart'; - -import 'package:unit2/test_data.dart'; import 'package:unit2/theme-data.dart/colors.dart'; class DashBoard extends StatelessWidget { @@ -62,7 +60,7 @@ class DashBoard extends StatelessWidget { textAlign: TextAlign.center, style: Theme.of(context) .textTheme - .button! + .labelLarge! .copyWith( fontSize: 11, fontWeight: FontWeight.bold), diff --git a/lib/screens/unit2/homepage.dart/components/drawer-screen.dart b/lib/screens/unit2/homepage.dart/components/drawer-screen.dart index c355f90..c1ba365 100644 --- a/lib/screens/unit2/homepage.dart/components/drawer-screen.dart +++ b/lib/screens/unit2/homepage.dart/components/drawer-screen.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_zoom_drawer/config.dart'; import 'package:flutter_zoom_drawer/flutter_zoom_drawer.dart'; -import 'package:unit2/bloc/bloc/user_bloc.dart'; import 'package:unit2/theme-data.dart/colors.dart'; +import '../../../../bloc/user/user_bloc.dart'; import '../../../../widgets/splash_screen.dart'; import 'menu-screen.dart'; import '../module-screen.dart'; diff --git a/lib/screens/unit2/homepage.dart/components/menu.dart b/lib/screens/unit2/homepage.dart/components/menu.dart index 5bf5982..b26cafe 100644 --- a/lib/screens/unit2/homepage.dart/components/menu.dart +++ b/lib/screens/unit2/homepage.dart/components/menu.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:unit2/model/login_data/employee_info/employee_info.dart'; import 'package:unit2/model/login_data/user_info/user_data.dart'; import 'package:unit2/utils/alerts.dart'; import '../../../../theme-data.dart/colors.dart'; @@ -20,9 +21,19 @@ Widget getTile( confirmAlert(context, () { Navigator.pushReplacementNamed (context,"/"); }); - } else { + }if(title.toLowerCase() == 'profile'){ + ProfileArguments profileArguments = ProfileArguments(token: userData.user!.login!.token!, userID:userData.user!.login!.user!.profileId!); + Navigator.pushNamed(context, route,arguments: profileArguments); + } + else { Navigator.pushNamed(context, route); } }, ); } + +class ProfileArguments{ + final int userID; + final String token; + const ProfileArguments({required this.token, required this.userID}); +} diff --git a/lib/screens/unit2/homepage.dart/module-screen.dart b/lib/screens/unit2/homepage.dart/module-screen.dart index 5626247..e8aa371 100644 --- a/lib/screens/unit2/homepage.dart/module-screen.dart +++ b/lib/screens/unit2/homepage.dart/module-screen.dart @@ -7,8 +7,7 @@ import 'package:fluttericon/typicons_icons.dart'; import 'package:unit2/screens/unit2/homepage.dart/components/dashboard.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; - -import '../../../bloc/bloc/user_bloc.dart'; +import '../../../bloc/user/user_bloc.dart'; import '../../../model/login_data/user_info/role.dart'; import 'components/empty_module.dart'; @@ -21,8 +20,8 @@ class MainScreen extends StatefulWidget { class _MainScreenState extends State { List roles = [ - Module(name: 'UniT2 roles', roles: []), - Module(name: 'DocSms roles', roles: []) + Module(name: 'UniT2 module operations', roles: []), + Module(name: 'DocSms module operations', roles: []) ]; @override Widget build(BuildContext context) { diff --git a/lib/screens/unit2/login/components/update_required.dart b/lib/screens/unit2/login/components/update_required.dart index 593169e..a5ae6b0 100644 --- a/lib/screens/unit2/login/components/update_required.dart +++ b/lib/screens/unit2/login/components/update_required.dart @@ -9,10 +9,10 @@ import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_svg/svg.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; -import 'package:unit2/bloc/bloc/user_bloc.dart'; import 'package:unit2/screens/unit2/login/components/showAlert.dart'; import 'package:unit2/theme-data.dart/btn-style.dart'; +import '../../../../bloc/user/user_bloc.dart'; import '../../../../theme-data.dart/colors.dart'; import '../../../../utils/cpu_architecture.dart'; import '../../../../utils/text_container.dart'; diff --git a/lib/screens/unit2/login/login.dart b/lib/screens/unit2/login/login.dart index bd2d590..ec4cb21 100644 --- a/lib/screens/unit2/login/login.dart +++ b/lib/screens/unit2/login/login.dart @@ -6,10 +6,10 @@ import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; -import 'package:unit2/bloc/bloc/user_bloc.dart'; import 'package:unit2/screens/unit2/login/components/update_required.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/error_state.dart'; +import '../../../bloc/user/user_bloc.dart'; import '../../../widgets/splash_screen.dart'; import '../../../widgets/wave.dart'; import '../../../utils/global.dart'; @@ -212,12 +212,15 @@ class _UniT2LoginState extends State { BlocProvider.of(context) .add(UserLogin( - username: _formKey - .currentState! - .value['username'], - password: _formKey - .currentState! - .value['password'])); + username: "rjvincentlopeplopez", + password: "shesthequ33n", + // username: _formKey + // .currentState! + // .value['username'], + // password: _formKey + // .currentState! + // .value['password']) + )); } }, ), diff --git a/lib/screens/unit2/login/qr_login.dart b/lib/screens/unit2/login/qr_login.dart index 9d2f70b..0a52f5c 100644 --- a/lib/screens/unit2/login/qr_login.dart +++ b/lib/screens/unit2/login/qr_login.dart @@ -10,8 +10,7 @@ import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:unit2/theme-data.dart/btn-style.dart'; import 'package:unit2/widgets/error_state.dart'; import 'package:unit2/widgets/wave.dart'; - -import '../../../bloc/bloc/user_bloc.dart'; +import '../../../bloc/user/user_bloc.dart'; import '../../../theme-data.dart/colors.dart'; import '../../../theme-data.dart/form-style.dart'; import '../../../utils/global.dart'; diff --git a/lib/sevices/login_service/auth_service.dart b/lib/sevices/login_service/auth_service.dart index 82b47a5..885bd76 100644 --- a/lib/sevices/login_service/auth_service.dart +++ b/lib/sevices/login_service/auth_service.dart @@ -1,8 +1,6 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; - -import 'package:flutter/material.dart'; import 'package:unit2/model/login_data/user_info/user_data.dart'; import 'package:unit2/model/login_data/version_info.dart'; import 'package:http/http.dart' as http; @@ -30,11 +28,7 @@ class AuthService { Map data = jsonDecode(response.body); versionInfo = VersionInfo.fromJson(data['data']); } - } on TimeoutException catch (_) { - throw (timeoutError); - } on SocketException catch (_) { - throw (timeoutError); - } catch (e) { + }catch (e) { throw (e.toString()); } return versionInfo; diff --git a/lib/sevices/profile/profile_service.dart b/lib/sevices/profile/profile_service.dart new file mode 100644 index 0000000..c502434 --- /dev/null +++ b/lib/sevices/profile/profile_service.dart @@ -0,0 +1,50 @@ +import 'dart:convert'; +import 'package:http/http.dart' as http; +import 'package:unit2/model/login_data/employee_info/employee_info.dart'; +import 'package:unit2/model/profile/basic_info.dart'; +import 'package:unit2/model/profile/basic_information/contact_information.dart'; +import 'package:unit2/model/profile/profileInfomation.dart'; +import 'package:unit2/utils/request.dart'; +import 'package:unit2/utils/urls.dart'; + +import '../../model/profile/basic_information/primary-information.dart'; + +class ProfileService { + static final ProfileService _instance = ProfileService(); + static ProfileService get instance => _instance; + + Future getProfile(String token, int id) async { + String url = Url.instance.profileInformation(); + String path = url + id.toString(); + ProfileInformation? _profileInformation; + ContactInfo contactInfo; + List contactInformation = []; + PrimaryInformation primaryInformation; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': "Token $token" + }; + try{ + http.Response response = await Request.instance + .getRequest(path: path, param: {}, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + // get primary information + primaryInformation = PrimaryInformation.fromJson( + data['data']['basic_information']['primary_information']); + // get all contacts + data['data']['basic_information']['contact_information'] + .forEach((var contact) { + contactInfo = ContactInfo.fromJson(contact['contact_info']); + contactInformation.add(contactInfo); + }); + BasicInfo basicInfo = BasicInfo(contactInformation: contactInformation, primaryInformation: primaryInformation); + ProfileInformation profileInformation = ProfileInformation(basicInfo: basicInfo); + _profileInformation = profileInformation; + } + }catch(e){ + throw(e.toString()); + } + return _profileInformation; + } +} diff --git a/lib/utils/app_router.dart b/lib/utils/app_router.dart index e6bc31b..16eea91 100644 --- a/lib/utils/app_router.dart +++ b/lib/utils/app_router.dart @@ -1,8 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:unit2/bloc/bloc/user_bloc.dart'; +import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/screens/unit2/homepage.dart/components/menu.dart'; import 'package:unit2/screens/unit2/login/login.dart'; import 'package:unit2/utils/global_context.dart'; +import '../bloc/user/user_bloc.dart'; import '../screens/profile/profile.dart'; import '../screens/unit2/basic-info/basic-info.dart'; import '../screens/unit2/homepage.dart/components/drawer-screen.dart'; @@ -33,7 +35,11 @@ class AppRouter { }); case '/profile': return MaterialPageRoute(builder: (_) { - return const ProfileInfo(); + ProfileArguments arguments = routeSettings.arguments as ProfileArguments; + return BlocProvider( + create: (context) => ProfileBloc()..add(LoadProfile(token: arguments.token,userID: arguments.userID)), + child: const ProfileInfo(), + ); }); default: return MaterialPageRoute(builder: (context) { diff --git a/lib/utils/request.dart b/lib/utils/request.dart index d698418..7d5e215 100644 --- a/lib/utils/request.dart +++ b/lib/utils/request.dart @@ -19,7 +19,7 @@ class Request { Map? param}) async { Response response; try { - response = await get(Uri.https(host, path!, param), headers: headers) + response = await get(Uri.http(host, path!, param), headers: headers) .timeout(Duration(seconds: requestTimeout)); } on TimeoutException catch (_) { Fluttertoast.showToast( @@ -61,7 +61,8 @@ class Request { Map? param}) async { Response response; try { - response = await post(Uri.https(host, path!, param), headers: headers,body: jsonEncode(body)) + response = await post(Uri.http(host, path!, param), + headers: headers, body: jsonEncode(body)) .timeout(Duration(seconds: requestTimeout)); } on TimeoutException catch (_) { Fluttertoast.showToast( diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index 438d113..cee712a 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -1,19 +1,18 @@ -class Url{ +class Url { static final Url _instance = Url(); static Url get instance => _instance; - String host(){ - // return '192.168.10.219:3000'; - return 'agusandelnorte.gov.ph'; + String host() { + // return '192.168.10.221:3003'; + // return 'agusandelnorte.gov.ph'; + return 'devweb.agusandelnorte.gov.ph'; } - - String authentication(){ + String authentication() { return '/api/account/auth/login/'; } - - String apkUrl(){ - return ""; - } + String profileInformation(){ + return '/api/jobnet_app/profile/pds/'; + } } \ No newline at end of file diff --git a/macos/Flutter/Flutter-Debug.xcconfig b/macos/Flutter/Flutter-Debug.xcconfig index c2efd0b..4b81f9b 100644 --- a/macos/Flutter/Flutter-Debug.xcconfig +++ b/macos/Flutter/Flutter-Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/Flutter-Release.xcconfig b/macos/Flutter/Flutter-Release.xcconfig index c2efd0b..5caa9d1 100644 --- a/macos/Flutter/Flutter-Release.xcconfig +++ b/macos/Flutter/Flutter-Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 46d0540..63c4df1 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,8 +6,8 @@ import FlutterMacOS import Foundation import package_info_plus -import path_provider_macos -import shared_preferences_macos +import path_provider_foundation +import shared_preferences_foundation import sqflite func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { diff --git a/macos/Podfile b/macos/Podfile new file mode 100644 index 0000000..049abe2 --- /dev/null +++ b/macos/Podfile @@ -0,0 +1,40 @@ +platform :osx, '10.14' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/pubspec.lock b/pubspec.lock index 42cf4b1..388b681 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,224 +5,264 @@ packages: dependency: "direct main" description: name: animate_do - url: "https://pub.dartlang.org" + sha256: "9aeacc1a7238f971c039bdf45d13c628be554a242e0251c4ddda09d19a1a923f" + url: "https://pub.dev" source: hosted version: "3.0.2" archive: dependency: transitive description: name: archive - url: "https://pub.dartlang.org" + sha256: d6347d54a2d8028e0437e3c099f66fdb8ae02c4720c1e7534c9f24c10351f85d + url: "https://pub.dev" source: hosted - version: "3.3.5" + version: "3.3.6" async: dependency: transitive description: name: async - url: "https://pub.dartlang.org" + sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + url: "https://pub.dev" source: hosted - version: "2.9.0" + version: "2.10.0" auto_size_text: dependency: "direct main" description: name: auto_size_text - url: "https://pub.dartlang.org" + sha256: "3f5261cd3fb5f2a9ab4e2fc3fba84fd9fcaac8821f20a1d4e71f557521b22599" + url: "https://pub.dev" source: hosted version: "3.0.0" awesome_dialog: dependency: "direct main" description: name: awesome_dialog - url: "https://pub.dartlang.org" + sha256: ac08268b991f228fc6b8880b68ec030d2941bcc8df8b6a6551fb79f2bd36b7da + url: "https://pub.dev" source: hosted version: "3.0.2" azlistview: dependency: "direct main" description: name: azlistview - url: "https://pub.dartlang.org" + sha256: "93e865f11777a271b439f0d6b00799c0797e9daeec2e082a2e01373809c4b90d" + url: "https://pub.dev" source: hosted version: "2.0.0" + badges: + dependency: "direct main" + description: + name: badges + sha256: "461031a60efbb95276f52107f63d5d45008b5ca1eb7f8ca440cadda9ec2143b0" + url: "https://pub.dev" + source: hosted + version: "3.0.2" barcode_scan2: dependency: "direct main" description: name: barcode_scan2 - url: "https://pub.dartlang.org" + sha256: f9af9252b8f3f5fa446f5456fd45f8871d09f883d8389a1d608b39231bfbc3fa + url: "https://pub.dev" source: hosted version: "4.2.3" bloc: dependency: transitive description: name: bloc - url: "https://pub.dartlang.org" + sha256: bd4f8027bfa60d96c8046dec5ce74c463b2c918dce1b0d36593575995344534a + url: "https://pub.dev" source: hosted version: "8.1.0" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.dartlang.org" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" cached_network_image: dependency: "direct main" description: name: cached_network_image - url: "https://pub.dartlang.org" + sha256: fd3d0dc1d451f9a252b32d95d3f0c3c487bc41a75eba2e6097cb0b9c71491b15 + url: "https://pub.dev" source: hosted version: "3.2.3" cached_network_image_platform_interface: dependency: transitive description: name: cached_network_image_platform_interface - url: "https://pub.dartlang.org" + sha256: bb2b8403b4ccdc60ef5f25c70dead1f3d32d24b9d6117cfc087f496b178594a7 + url: "https://pub.dev" source: hosted version: "2.0.0" cached_network_image_web: dependency: transitive description: name: cached_network_image_web - url: "https://pub.dartlang.org" + sha256: b8eb814ebfcb4dea049680f8c1ffb2df399e4d03bf7a352c775e26fa06e02fa0 + url: "https://pub.dev" source: hosted version: "1.0.2" characters: dependency: transitive description: name: characters - url: "https://pub.dartlang.org" + sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + url: "https://pub.dev" source: hosted version: "1.2.1" clock: dependency: transitive description: name: clock - url: "https://pub.dartlang.org" + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" source: hosted version: "1.1.1" collection: dependency: transitive description: name: collection - url: "https://pub.dartlang.org" + sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.17.0" convert: dependency: transitive description: name: convert - url: "https://pub.dartlang.org" + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" source: hosted version: "3.1.1" convex_bottom_bar: dependency: "direct main" description: name: convex_bottom_bar - url: "https://pub.dartlang.org" + sha256: ebf0f3deb1e8e99374d844fee7485d2980ec502dedfaad395c12118477933aef + url: "https://pub.dev" source: hosted - version: "3.1.0+1" + version: "3.2.0" cool_alert: dependency: "direct main" description: name: cool_alert - url: "https://pub.dartlang.org" + sha256: "48a0b6c04914b6dc7e8d7eaccf2adf21bace6533a3eda0aeb412e0d7a03dc00a" + url: "https://pub.dev" source: hosted version: "1.1.0" crypto: dependency: transitive description: name: crypto - url: "https://pub.dartlang.org" + sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 + url: "https://pub.dev" source: hosted version: "3.0.2" date_time_picker: dependency: "direct main" description: name: date_time_picker - url: "https://pub.dartlang.org" + sha256: "6923c568bcb67a66ab7e083708d0adbcae8214b41bb84d49febc17e89e06fc4a" + url: "https://pub.dev" source: hosted version: "2.1.0" device_frame: dependency: transitive description: name: device_frame - url: "https://pub.dartlang.org" + sha256: afe76182aec178d171953d9b4a50a43c57c7cf3c77d8b09a48bf30c8fa04dd9d + url: "https://pub.dev" source: hosted version: "1.1.0" device_preview: dependency: "direct main" description: name: device_preview - url: "https://pub.dartlang.org" + sha256: "2f097bf31b929e15e6756dbe0ec1bcb63952ab9ed51c25dc5a2c722d2b21fdaf" + url: "https://pub.dev" source: hosted version: "1.1.0" dio: dependency: "direct main" description: name: dio - url: "https://pub.dartlang.org" + sha256: "7d328c4d898a61efc3cd93655a0955858e29a0aa647f0f9e02d59b3bb275e2e8" + url: "https://pub.dev" source: hosted version: "4.0.6" easy_app_installer: dependency: "direct main" description: name: easy_app_installer - url: "https://pub.dartlang.org" + sha256: d7287bf247fe6bc85ad07dfb85804757a6dd2f47e61b0e7ce9195ec7f13e09eb + url: "https://pub.dev" source: hosted version: "1.0.0" equatable: dependency: "direct main" description: name: equatable - url: "https://pub.dartlang.org" + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + url: "https://pub.dev" source: hosted version: "2.0.5" expandable_group: dependency: "direct main" description: name: expandable_group - url: "https://pub.dartlang.org" + sha256: "874f9c2daef8a21366fb1df85405f80ee8e8be6e3c2ced727c303641b33b9a95" + url: "https://pub.dev" source: hosted version: "0.0.8" fake_async: dependency: transitive description: name: fake_async - url: "https://pub.dartlang.org" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" source: hosted version: "1.3.1" ffi: dependency: transitive description: name: ffi - url: "https://pub.dartlang.org" + sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 + url: "https://pub.dev" source: hosted version: "2.0.1" file: dependency: transitive description: name: file - url: "https://pub.dartlang.org" + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" source: hosted version: "6.1.4" file_utils: dependency: transitive description: name: file_utils - url: "https://pub.dartlang.org" + sha256: d1e64389a22649095c8405c9e177272caf05139255931c9ff30d53b5c9bcaa34 + url: "https://pub.dev" source: hosted version: "1.0.1" fixnum: dependency: transitive description: name: fixnum - url: "https://pub.dartlang.org" + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.1.0" flare_flutter: dependency: transitive description: name: flare_flutter - url: "https://pub.dartlang.org" + sha256: "99d63c60f00fac81249ce6410ee015d7b125c63d8278a30da81edf3317a1f6a0" + url: "https://pub.dev" source: hosted version: "3.0.2" flutter: @@ -234,42 +274,48 @@ packages: dependency: "direct main" description: name: flutter_bloc - url: "https://pub.dartlang.org" + sha256: "890c51c8007f0182360e523518a0c732efb89876cb4669307af7efada5b55557" + url: "https://pub.dev" source: hosted version: "8.1.1" flutter_blurhash: dependency: transitive description: name: flutter_blurhash - url: "https://pub.dartlang.org" + sha256: "05001537bd3fac7644fa6558b09ec8c0a3f2eba78c0765f88912882b1331a5c6" + url: "https://pub.dev" source: hosted version: "0.7.0" flutter_cache_manager: dependency: transitive description: name: flutter_cache_manager - url: "https://pub.dartlang.org" + sha256: "32cd900555219333326a2d0653aaaf8671264c29befa65bbd9856d204a4c9fb3" + url: "https://pub.dev" source: hosted version: "3.3.0" flutter_custom_clippers: dependency: "direct main" description: name: flutter_custom_clippers - url: "https://pub.dartlang.org" + sha256: "473e3daf61c2a6cee0ad137393259a25223239d519a131c7ec1cac04d06e5407" + url: "https://pub.dev" source: hosted version: "2.1.0" flutter_form_builder: dependency: "direct main" description: name: flutter_form_builder - url: "https://pub.dartlang.org" + sha256: "768b11307e71c60cb66351a87984815bd438b50aa58b5de02c9256a8f2964bee" + url: "https://pub.dev" source: hosted version: "7.7.0" flutter_lints: dependency: "direct dev" description: name: flutter_lints - url: "https://pub.dartlang.org" + sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + url: "https://pub.dev" source: hosted version: "2.0.1" flutter_localizations: @@ -281,28 +327,32 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - url: "https://pub.dartlang.org" + sha256: "60fc7b78455b94e6de2333d2f95196d32cf5c22f4b0b0520a628804cb463503b" + url: "https://pub.dev" source: hosted version: "2.0.7" flutter_progress_hud: dependency: "direct main" description: name: flutter_progress_hud - url: "https://pub.dartlang.org" + sha256: "19a4889460b7482c5026a936b768996dade4daad8570e8c0493e292d57121dbb" + url: "https://pub.dev" source: hosted version: "2.0.2" flutter_spinkit: dependency: "direct main" description: name: flutter_spinkit - url: "https://pub.dartlang.org" + sha256: "77a2117c0517ff909221f3160b8eb20052ab5216107581168af574ac1f05dff8" + url: "https://pub.dev" source: hosted version: "5.1.0" flutter_svg: dependency: "direct main" description: name: flutter_svg - url: "https://pub.dartlang.org" + sha256: "6ff9fa12892ae074092de2fa6a9938fb21dbabfdaa2ff57dc697ff912fc8d4b2" + url: "https://pub.dev" source: hosted version: "1.1.6" flutter_test: @@ -319,413 +369,456 @@ packages: dependency: "direct main" description: name: flutter_zoom_drawer - url: "https://pub.dartlang.org" + sha256: "93c4f2cfe1edfe2f9e48b94a7f9538520d2f2593e202b8e89c394b7d27137a8e" + url: "https://pub.dev" source: hosted version: "3.0.3" fluttericon: dependency: "direct main" description: name: fluttericon - url: "https://pub.dartlang.org" + sha256: "252fa8043826e93d972a602497a260cb3d62b5aea6d045793e4381590f2c1e99" + url: "https://pub.dev" source: hosted version: "2.0.0" fluttertoast: dependency: "direct main" description: name: fluttertoast - url: "https://pub.dartlang.org" + sha256: "7cc92eabe01e3f1babe1571c5560b135dfc762a34e41e9056881e2196b178ec1" + url: "https://pub.dev" source: hosted version: "8.1.2" form_builder_validators: dependency: "direct main" description: name: form_builder_validators - url: "https://pub.dartlang.org" + sha256: e4d54c0c513e3e36ae4e4905994873a0a907585407212effeef39a68e759670c + url: "https://pub.dev" source: hosted version: "8.4.0" freezed_annotation: dependency: transitive description: name: freezed_annotation - url: "https://pub.dartlang.org" + sha256: aeac15850ef1b38ee368d4c53ba9a847e900bb2c53a4db3f6881cbb3cb684338 + url: "https://pub.dev" source: hosted version: "2.2.0" globbing: dependency: transitive description: name: globbing - url: "https://pub.dartlang.org" + sha256: "4f89cfaf6fa74c9c1740a96259da06bd45411ede56744e28017cc534a12b6e2d" + url: "https://pub.dev" source: hosted version: "1.0.0" graphs: dependency: transitive description: name: graphs - url: "https://pub.dartlang.org" + sha256: f9e130f3259f52d26f0cfc0e964513796dafed572fa52e45d2f8d6ca14db39b2 + url: "https://pub.dev" source: hosted version: "2.2.0" http: dependency: transitive description: name: http - url: "https://pub.dartlang.org" + sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482" + url: "https://pub.dev" source: hosted version: "0.13.5" http_parser: dependency: transitive description: name: http_parser - url: "https://pub.dartlang.org" + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" source: hosted version: "4.0.2" image: dependency: transitive description: name: image - url: "https://pub.dartlang.org" + sha256: "8e9d133755c3e84c73288363e6343157c383a0c6c56fc51afcc5d4d7180306d6" + url: "https://pub.dev" source: hosted version: "3.3.0" intl: dependency: "direct main" description: name: intl - url: "https://pub.dartlang.org" + sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" + url: "https://pub.dev" source: hosted version: "0.17.0" js: dependency: transitive description: name: js - url: "https://pub.dartlang.org" + sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + url: "https://pub.dev" source: hosted - version: "0.6.4" + version: "0.6.5" json_annotation: dependency: transitive description: name: json_annotation - url: "https://pub.dartlang.org" + sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317 + url: "https://pub.dev" source: hosted - version: "4.7.0" + version: "4.8.0" lints: dependency: transitive description: name: lints - url: "https://pub.dartlang.org" + sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + url: "https://pub.dev" source: hosted version: "2.0.1" lottie: dependency: transitive description: name: lottie - url: "https://pub.dartlang.org" + sha256: "893da7a0022ec2fcaa616f34529a081f617e86cc501105b856e5a3184c58c7c2" + url: "https://pub.dev" source: hosted version: "1.4.3" matcher: dependency: transitive description: name: matcher - url: "https://pub.dartlang.org" + sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + url: "https://pub.dev" source: hosted - version: "0.12.12" + version: "0.12.13" material_color_utilities: dependency: transitive description: name: material_color_utilities - url: "https://pub.dartlang.org" + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + url: "https://pub.dev" source: hosted - version: "0.1.5" + version: "0.2.0" meta: dependency: transitive description: name: meta - url: "https://pub.dartlang.org" + sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + url: "https://pub.dev" source: hosted version: "1.8.0" nested: dependency: transitive description: name: nested - url: "https://pub.dartlang.org" + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" source: hosted version: "1.0.0" octo_image: dependency: transitive description: name: octo_image - url: "https://pub.dartlang.org" + sha256: "107f3ed1330006a3bea63615e81cf637433f5135a52466c7caa0e7152bca9143" + url: "https://pub.dev" source: hosted version: "1.0.2" package_info_plus: dependency: "direct main" description: name: package_info_plus - url: "https://pub.dartlang.org" + sha256: f619162573096d428ccde2e33f92e05b5a179cd6f0e3120c1005f181bee8ed16 + url: "https://pub.dev" source: hosted version: "3.0.2" package_info_plus_platform_interface: dependency: transitive description: name: package_info_plus_platform_interface - url: "https://pub.dartlang.org" + sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6" + url: "https://pub.dev" source: hosted version: "2.0.1" path: dependency: transitive description: name: path - url: "https://pub.dartlang.org" + sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + url: "https://pub.dev" source: hosted version: "1.8.2" path_drawing: dependency: transitive description: name: path_drawing - url: "https://pub.dartlang.org" + sha256: bbb1934c0cbb03091af082a6389ca2080345291ef07a5fa6d6e078ba8682f977 + url: "https://pub.dev" source: hosted version: "1.0.1" path_parsing: dependency: transitive description: name: path_parsing - url: "https://pub.dartlang.org" + sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf + url: "https://pub.dev" source: hosted version: "1.0.1" path_provider: dependency: "direct main" description: name: path_provider - url: "https://pub.dartlang.org" + sha256: dcea5feb97d8abf90cab9e9030b497fb7c3cbf26b7a1fe9e3ef7dcb0a1ddec95 + url: "https://pub.dev" source: hosted - version: "2.0.11" + version: "2.0.12" path_provider_android: dependency: transitive description: name: path_provider_android - url: "https://pub.dartlang.org" + sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e + url: "https://pub.dev" source: hosted version: "2.0.22" - path_provider_ios: + path_provider_foundation: dependency: transitive description: - name: path_provider_ios - url: "https://pub.dartlang.org" + name: path_provider_foundation + sha256: "62a68e7e1c6c459f9289859e2fae58290c981ce21d1697faf54910fe1faa4c74" + url: "https://pub.dev" source: hosted - version: "2.0.11" + version: "2.1.1" path_provider_linux: dependency: transitive description: name: path_provider_linux - url: "https://pub.dartlang.org" + sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379 + url: "https://pub.dev" source: hosted version: "2.1.7" - path_provider_macos: - dependency: transitive - description: - name: path_provider_macos - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.6" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - url: "https://pub.dartlang.org" + sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76 + url: "https://pub.dev" source: hosted version: "2.0.5" path_provider_windows: dependency: transitive description: name: path_provider_windows - url: "https://pub.dartlang.org" + sha256: bcabbe399d4042b8ee687e17548d5d3f527255253b4a639f5f8d2094a9c2b45c + url: "https://pub.dev" source: hosted version: "2.1.3" pedantic: dependency: transitive description: name: pedantic - url: "https://pub.dartlang.org" + sha256: "67fc27ed9639506c856c840ccce7594d0bdcd91bc8d53d6e52359449a1d50602" + url: "https://pub.dev" source: hosted version: "1.11.1" permission_handler: dependency: "direct main" description: name: permission_handler - url: "https://pub.dartlang.org" + sha256: "33c6a1253d1f95fd06fa74b65b7ba907ae9811f9d5c1d3150e51417d04b8d6a8" + url: "https://pub.dev" source: hosted version: "10.2.0" permission_handler_android: dependency: transitive description: name: permission_handler_android - url: "https://pub.dartlang.org" + sha256: "8028362b40c4a45298f1cbfccd227c8dd6caf0e27088a69f2ba2ab15464159e2" + url: "https://pub.dev" source: hosted version: "10.2.0" permission_handler_apple: dependency: transitive description: name: permission_handler_apple - url: "https://pub.dartlang.org" + sha256: "9c370ef6a18b1c4b2f7f35944d644a56aa23576f23abee654cf73968de93f163" + url: "https://pub.dev" source: hosted version: "9.0.7" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface - url: "https://pub.dartlang.org" + sha256: "68abbc472002b5e6dfce47fe9898c6b7d8328d58b5d2524f75e277c07a97eb84" + url: "https://pub.dev" source: hosted version: "3.9.0" permission_handler_windows: dependency: transitive description: name: permission_handler_windows - url: "https://pub.dartlang.org" + sha256: f67cab14b4328574938ecea2db3475dad7af7ead6afab6338772c5f88963e38b + url: "https://pub.dev" source: hosted version: "0.1.2" petitparser: dependency: transitive description: name: petitparser - url: "https://pub.dartlang.org" + sha256: "49392a45ced973e8d94a85fdb21293fbb40ba805fc49f2965101ae748a3683b4" + url: "https://pub.dev" source: hosted version: "5.1.0" platform: dependency: transitive description: name: platform - url: "https://pub.dartlang.org" + sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + url: "https://pub.dev" source: hosted version: "3.1.0" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - url: "https://pub.dartlang.org" + sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a + url: "https://pub.dev" source: hosted version: "2.1.3" pointycastle: dependency: transitive description: name: pointycastle - url: "https://pub.dartlang.org" + sha256: db7306cf0249f838d1a24af52b5a5887c5bf7f31d8bb4e827d071dc0939ad346 + url: "https://pub.dev" source: hosted version: "3.6.2" process: dependency: transitive description: name: process - url: "https://pub.dartlang.org" + sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + url: "https://pub.dev" source: hosted version: "4.2.4" protobuf: dependency: transitive description: name: protobuf - url: "https://pub.dartlang.org" + sha256: "01dd9bd0fa02548bf2ceee13545d4a0ec6046459d847b6b061d8a27237108a08" + url: "https://pub.dev" source: hosted version: "2.1.0" provider: dependency: transitive description: name: provider - url: "https://pub.dartlang.org" + sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + url: "https://pub.dev" source: hosted version: "6.0.5" qr: dependency: transitive description: name: qr - url: "https://pub.dartlang.org" + sha256: "5c4208b4dc0d55c3184d10d83ee0ded6212dc2b5e2ba17c5a0c0aab279128d21" + url: "https://pub.dev" source: hosted version: "2.1.0" qr_flutter: dependency: "direct main" description: name: qr_flutter - url: "https://pub.dartlang.org" + sha256: c5c121c54cb6dd837b9b9d57eb7bc7ec6df4aee741032060c8833a678c80b87e + url: "https://pub.dev" source: hosted version: "4.0.0" rive: dependency: transitive description: name: rive - url: "https://pub.dartlang.org" + sha256: "22e3755b75f4ea4492d2fecf4fc2acf1c8d0073df39781d290a20cbfe74c3760" + url: "https://pub.dev" source: hosted version: "0.9.1" rxdart: dependency: transitive description: name: rxdart - url: "https://pub.dartlang.org" + sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" + url: "https://pub.dev" source: hosted version: "0.27.7" scrollable_positioned_list: dependency: transitive description: name: scrollable_positioned_list - url: "https://pub.dartlang.org" + sha256: "9566352ab9ba05794ee6c8864f154afba5d36c5637d0e3e32c615ba4ceb92772" + url: "https://pub.dev" source: hosted version: "0.2.3" shared_preferences: dependency: transitive description: name: shared_preferences - url: "https://pub.dartlang.org" + sha256: "5949029e70abe87f75cfe59d17bf5c397619c4b74a099b10116baeb34786fad9" + url: "https://pub.dev" source: hosted - version: "2.0.15" + version: "2.0.17" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - url: "https://pub.dartlang.org" + sha256: "955e9736a12ba776bdd261cf030232b30eadfcd9c79b32a3250dd4a494e8c8f7" + url: "https://pub.dev" source: hosted - version: "2.0.14" - shared_preferences_ios: + version: "2.0.15" + shared_preferences_foundation: dependency: transitive description: - name: shared_preferences_ios - url: "https://pub.dartlang.org" + name: shared_preferences_foundation + sha256: "1ffa239043ab8baf881ec3094a3c767af9d10399b2839020b9e4d44c0bb23951" + url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux - url: "https://pub.dartlang.org" + sha256: f8ea038aa6da37090093974ebdcf4397010605fd2ff65c37a66f9d28394cb874 + url: "https://pub.dev" source: hosted - version: "2.1.2" - shared_preferences_macos: - dependency: transitive - description: - name: shared_preferences_macos - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.5" + version: "2.1.3" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface - url: "https://pub.dartlang.org" + sha256: da9431745ede5ece47bc26d5d73a9d3c6936ef6945c101a5aca46f62e52c1cf3 + url: "https://pub.dev" source: hosted version: "2.1.0" shared_preferences_web: dependency: transitive description: name: shared_preferences_web - url: "https://pub.dartlang.org" + sha256: a4b5bc37fe1b368bbc81f953197d55e12f49d0296e7e412dfe2d2d77d6929958 + url: "https://pub.dev" source: hosted version: "2.0.4" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows - url: "https://pub.dartlang.org" + sha256: "5eaf05ae77658d3521d0e993ede1af962d4b326cd2153d312df716dc250f00c9" + url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.3" signature: dependency: "direct main" description: name: signature - url: "https://pub.dartlang.org" + sha256: ad23383717dfa926204695ef6928ff513a77387be1b4a8c685099ce3ec35e5f8 + url: "https://pub.dev" source: hosted version: "5.3.0" sky_engine: @@ -737,121 +830,138 @@ packages: dependency: transitive description: name: source_span - url: "https://pub.dartlang.org" + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" sqflite: dependency: transitive description: name: sqflite - url: "https://pub.dartlang.org" + sha256: "78324387dc81df14f78df06019175a86a2ee0437624166c382e145d0a7fd9a4f" + url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.2.4+1" sqflite_common: dependency: transitive description: name: sqflite_common - url: "https://pub.dartlang.org" + sha256: bfd6973aaeeb93475bc0d875ac9aefddf7965ef22ce09790eb963992ffc5183f + url: "https://pub.dev" source: hosted - version: "2.4.0+2" + version: "2.4.2+2" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.dartlang.org" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.dartlang.org" + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.dartlang.org" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.2.0" synchronized: dependency: transitive description: name: synchronized - url: "https://pub.dartlang.org" + sha256: "33b31b6beb98100bf9add464a36a8dd03eb10c7a8cf15aeec535e9b054aaf04b" + url: "https://pub.dev" source: hosted - version: "3.0.0+3" + version: "3.0.1" system_info2: dependency: "direct main" description: name: system_info2 - url: "https://pub.dartlang.org" + sha256: "90621f3ba586e1f268e38cc7951b172cd4d997e43dc1fbed12eb334c8a22a886" + url: "https://pub.dev" source: hosted version: "2.0.4" term_glyph: dependency: transitive description: name: term_glyph - url: "https://pub.dartlang.org" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" source: hosted version: "1.2.1" test_api: dependency: transitive description: name: test_api - url: "https://pub.dartlang.org" + sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + url: "https://pub.dev" source: hosted - version: "0.4.12" + version: "0.4.16" toggle_switch: dependency: "direct main" description: name: toggle_switch - url: "https://pub.dartlang.org" + sha256: "82c778c4bfe93af154a41e346ccd1d5b0cb6a50f8f187941412edd0a9bbf51ee" + url: "https://pub.dev" source: hosted version: "2.0.1" typed_data: dependency: transitive description: name: typed_data - url: "https://pub.dartlang.org" + sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + url: "https://pub.dev" source: hosted version: "1.3.1" uuid: dependency: transitive description: name: uuid - url: "https://pub.dartlang.org" + sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" + url: "https://pub.dev" source: hosted version: "3.0.7" vector_math: dependency: transitive description: name: vector_math - url: "https://pub.dartlang.org" + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" win32: dependency: transitive description: name: win32 - url: "https://pub.dartlang.org" + sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46 + url: "https://pub.dev" source: hosted version: "3.1.3" xdg_directories: dependency: transitive description: name: xdg_directories - url: "https://pub.dartlang.org" + sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86 + url: "https://pub.dev" source: hosted - version: "0.2.0+2" + version: "0.2.0+3" xml: dependency: transitive description: name: xml - url: "https://pub.dartlang.org" + sha256: "979ee37d622dec6365e2efa4d906c37470995871fe9ae080d967e192d88286b5" + url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "6.2.2" sdks: - dart: ">=2.18.5 <3.0.0" + dart: ">=2.19.0 <4.0.0" flutter: ">=3.3.0" diff --git a/pubspec.yaml b/pubspec.yaml index f989e51..698736c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -67,6 +67,7 @@ dependencies: cool_alert: ^1.1.0 permission_handler: ^10.2.0 expandable_group: ^0.0.8 + badges: ^3.0.2 dev_dependencies: flutter_test: From 40ff288ddd68770394bfd06196e698d51000cb78 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Fri, 3 Feb 2023 11:34:09 +0800 Subject: [PATCH 23/86] integrated Eligibility, Learning and development, Personal references API --- ios/Runner.xcodeproj/project.pbxproj | 10 +- ios/Runner/Info.plist | 8 +- lib/bloc/profile/profile_bloc.dart | 8 +- lib/model/profile/basic_info.dart | 6 +- .../basic_information/citizenship.dart | 50 +++ .../identification_information.dart | 293 ++++++++++++++ lib/model/profile/eligibility.dart | 250 ++++++++++++ lib/model/profile/learning_development.dart | 361 ++++++++++++++++++ lib/model/profile/profileInfomation.dart | 8 +- lib/model/profile/references.dart | 234 ++++++++++++ .../basic_information/citizenship_screen.dart | 45 +++ .../contact_information.dart | 86 ++--- .../identification_information.dart | 63 +++ .../profile/components/eligibility.dart | 68 ++++ .../learning_and_development_screen.dart | 67 ++++ .../profile/components/loading_screen.dart | 35 +- lib/screens/profile/components/main_menu.dart | 5 +- .../profile/components/references_screen.dart | 57 +++ lib/screens/profile/profile.dart | 50 ++- lib/sevices/profile/profile_service.dart | 67 +++- lib/utils/urls.dart | 4 +- macos/Podfile.lock | 51 +++ macos/Runner.xcodeproj/project.pbxproj | 71 +++- .../contents.xcworkspacedata | 3 + 24 files changed, 1807 insertions(+), 93 deletions(-) create mode 100644 lib/model/profile/basic_information/citizenship.dart create mode 100644 lib/model/profile/basic_information/identification_information.dart create mode 100644 lib/model/profile/eligibility.dart create mode 100644 lib/model/profile/learning_development.dart create mode 100644 lib/model/profile/references.dart create mode 100644 lib/screens/profile/components/basic_information/citizenship_screen.dart create mode 100644 lib/screens/profile/components/basic_information/identification_information.dart create mode 100644 lib/screens/profile/components/eligibility.dart create mode 100644 lib/screens/profile/components/learning_and_development_screen.dart create mode 100644 lib/screens/profile/components/references_screen.dart create mode 100644 macos/Podfile.lock diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index c8b8a8f..4cf4c30 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -68,7 +68,6 @@ 7435055C04EA907D4DF9DEFB /* Pods-Runner.release.xcconfig */, 24CA4A0209E683A311335098 /* Pods-Runner.profile.xcconfig */, ); - name = Pods; path = Pods; sourceTree = ""; }; @@ -358,13 +357,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 2WLSMMLG6W; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.unit2; + PRODUCT_BUNDLE_IDENTIFIER = "uniT-App"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -486,13 +486,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 2WLSMMLG6W; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.unit2; + PRODUCT_BUNDLE_IDENTIFIER = "uniT-App"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -508,13 +509,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 2WLSMMLG6W; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.unit2; + PRODUCT_BUNDLE_IDENTIFIER = "uniT-App"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 48a1080..097b297 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -2,6 +2,8 @@ + CADisableMinimumFrameDurationOnPhone + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName @@ -24,6 +26,8 @@ $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS + UIApplicationSupportsIndirectInputEvents + UILaunchStoryboardName LaunchScreen UIMainStoryboardFile @@ -43,9 +47,5 @@ UIViewControllerBasedStatusBarAppearance - CADisableMinimumFrameDurationOnPhone - - UIApplicationSupportsIndirectInputEvents - diff --git a/lib/bloc/profile/profile_bloc.dart b/lib/bloc/profile/profile_bloc.dart index f357ecc..c778ab6 100644 --- a/lib/bloc/profile/profile_bloc.dart +++ b/lib/bloc/profile/profile_bloc.dart @@ -15,16 +15,16 @@ class ProfileBloc extends Bloc { ProfileBloc() : super(ProfileInitial()) { ProfileInformation? _profileInformation; on((event, emit) async { - try { + // try { emit(ProfileLoading()); ProfileInformation? profileInformation = await ProfileService.instance.getProfile(event.token, event.userID); _profileInformation = profileInformation; emit(ProfileLoaded( profileInformation: _profileInformation!)); - } catch (e) { - emit(ProfileErrorState(mesage: e.toString())); - } + // } catch (e) { + // emit(ProfileErrorState(mesage: e.toString())); + // } }); } } diff --git a/lib/model/profile/basic_info.dart b/lib/model/profile/basic_info.dart index fda9bc1..29b1db2 100644 --- a/lib/model/profile/basic_info.dart +++ b/lib/model/profile/basic_info.dart @@ -1,9 +1,13 @@ +import 'package:unit2/model/profile/basic_information/citizenship.dart'; import 'package:unit2/model/profile/basic_information/contact_information.dart'; +import 'package:unit2/model/profile/basic_information/identification_information.dart'; import 'package:unit2/model/profile/basic_information/primary-information.dart'; class BasicInfo{ PrimaryInformation primaryInformation; List contactInformation; - BasicInfo({required this.contactInformation, required this.primaryInformation}); + List identifications; + List citizenships; + BasicInfo({required this.contactInformation, required this.primaryInformation,required this.identifications,required this.citizenships}); } \ No newline at end of file diff --git a/lib/model/profile/basic_information/citizenship.dart b/lib/model/profile/basic_information/citizenship.dart new file mode 100644 index 0000000..cddc21e --- /dev/null +++ b/lib/model/profile/basic_information/citizenship.dart @@ -0,0 +1,50 @@ +// To parse this JSON data, do +// +// final citizenship = citizenshipFromJson(jsonString); + +import 'package:meta/meta.dart'; +import 'dart:convert'; + +class Citizenship { + Citizenship({ + required this.country, + required this.naturalBorn, + }); + + final Country? country; + final bool? naturalBorn; + + factory Citizenship.fromJson(Map json) => Citizenship( + country: Country.fromJson(json["country"]), + naturalBorn: json["natural_born"], + ); + + Map toJson() => { + "country": country!.toJson(), + "natural_born": naturalBorn, + }; +} + +class Country { + Country({ + required this.id, + required this.code, + required this.name, + }); + + final int? id; + final String? code; + final String? name; + + factory Country.fromJson(Map json) => Country( + id: json["id"], + code: json["code"], + name: json["name"], + ); + + Map toJson() => { + "id": id, + "code": code, + "name": name, + }; +} diff --git a/lib/model/profile/basic_information/identification_information.dart b/lib/model/profile/basic_information/identification_information.dart new file mode 100644 index 0000000..09e20fe --- /dev/null +++ b/lib/model/profile/basic_information/identification_information.dart @@ -0,0 +1,293 @@ +import 'dart:convert'; + +Identification identificationFromJson(String str) => Identification.fromJson(json.decode(str)); + +String identificationToJson(Identification data) => json.encode(data.toJson()); + +class Identification { + Identification({ + required this.id, + required this.agency, + required this.issuedAt, + this.dateIssued, + this.expirationDate, + required this.asPdfReference, + required this.identificationNumber, + }); + + int? id; + Agency? agency; + IssuedAt? issuedAt; + DateTime? dateIssued; + DateTime? expirationDate; + bool? asPdfReference; + String? identificationNumber; + + factory Identification.fromJson(Map json) => Identification( + id: json["id"], + agency: Agency.fromJson(json["agency"]), + issuedAt: IssuedAt.fromJson(json["issued_at"]), + dateIssued:json["date_issued"]==null?null:DateTime.parse(json["date_issued"]), + expirationDate:json["expiration_date"]==null?null:DateTime.parse(json["expiration_date"]), + asPdfReference: json["as_pdf_reference"], + identificationNumber: json["identification_number"], + ); + + Map toJson() => { + "id": id, + "agency": agency!.toJson(), + "issued_at": issuedAt!.toJson(), + "date_issued": dateIssued, + "expiration_date": expirationDate, + "as_pdf_reference": asPdfReference, + "identification_number": identificationNumber, + }; +} + +class Agency { + Agency({ + required this.id, + required this.name, + required this.category, + required this.privateEntity, + }); + + int? id; + String? name; + Category? category; + bool? privateEntity; + + factory Agency.fromJson(Map json) => Agency( + id: json["id"], + name: json["name"], + category: Category.fromJson(json["category"]), + privateEntity: json["private_entity"], + ); + + Map toJson() => { + "id": id, + "name": name, + "category": category!.toJson(), + "private_entity": privateEntity, + }; +} + +class Category { + Category({ + required this.id, + required this.name, + required this.industryClass, + }); + + int? id; + String? name; + IndustryClass? industryClass; + + factory Category.fromJson(Map json) => Category( + id: json["id"], + name: json["name"], + industryClass: IndustryClass.fromJson(json["industry_class"]), + ); + + Map toJson() => { + "id": id, + "name": name, + "industry_class": industryClass!.toJson(), + }; +} + +class IndustryClass { + IndustryClass({ + required this.id, + required this.name, + this.description, + }); + + int? id; + String? name; + String? description; + + factory IndustryClass.fromJson(Map json) => IndustryClass( + id: json["id"], + name: json["name"], + description: json["description"], + ); + + Map toJson() => { + "id": id, + "name": name, + "description": description, + }; +} + +class IssuedAt { + IssuedAt({ + required this.id, + this.issuedAtClass, + required this.country, + this.barangay, + required this.addressCategory, + required this.cityMunicipality, + }); + + int? id; + dynamic issuedAtClass; + Country? country; + dynamic barangay; + AddressCategory? addressCategory; + CityMunicipality? cityMunicipality; + + factory IssuedAt.fromJson(Map json) => IssuedAt( + id: json["id"], + issuedAtClass: json["class"], + country: Country.fromJson(json["country"]), + barangay: json["barangay"], + addressCategory: AddressCategory.fromJson(json["address_category"]), + cityMunicipality: CityMunicipality.fromJson(json["city_municipality"]), + ); + + Map toJson() => { + "id": id, + "class": issuedAtClass, + "country": country!.toJson(), + "barangay": barangay, + "address_category": addressCategory!.toJson(), + "city_municipality": cityMunicipality!.toJson(), + }; +} + +class AddressCategory { + AddressCategory({ + required this.id, + required this.name, + required this.type, + }); + + int? id; + String? name; + String? type; + + factory AddressCategory.fromJson(Map json) => AddressCategory( + id: json["id"], + name: json["name"], + type: json["type"], + ); + + Map toJson() => { + "id": id, + "name": name, + "type": type, + }; +} + +class CityMunicipality { + CityMunicipality({ + required this.code, + required this.zipcode, + required this.province, + required this.psgcCode, + required this.description, + }); + + String? code; + String? zipcode; + Province? province; + String? psgcCode; + String? description; + + factory CityMunicipality.fromJson(Map json) => CityMunicipality( + code: json["code"], + zipcode: json["zipcode"], + province: Province.fromJson(json["province"]), + psgcCode: json["psgc_code"], + description: json["description"], + ); + + Map toJson() => { + "code": code, + "zipcode": zipcode, + "province": province!.toJson(), + "psgc_code": psgcCode, + "description": description, + }; +} + +class Province { + Province({ + required this.code, + required this.region, + required this.psgcCode, + required this.shortname, + required this.description, + }); + + String? code; + Region? region; + String? psgcCode; + String? shortname; + String? description; + + factory Province.fromJson(Map json) => Province( + code: json["code"], + region: Region.fromJson(json["region"]), + psgcCode: json["psgc_code"], + shortname: json["shortname"], + description: json["description"], + ); + + Map toJson() => { + "code": code, + "region": region!.toJson(), + "psgc_code": psgcCode, + "shortname": shortname, + "description": description, + }; +} + +class Region { + Region({ + required this.code, + required this.psgcCode, + required this.description, + }); + + int? code; + String? psgcCode; + String? description; + + factory Region.fromJson(Map json) => Region( + code: json["code"], + psgcCode: json["psgc_code"], + description: json["description"], + ); + + Map toJson() => { + "code": code, + "psgc_code": psgcCode, + "description": description, + }; +} + +class Country { + Country({ + required this.id, + required this.code, + required this.name, + }); + + int? id; + String? code; + String? name; + + factory Country.fromJson(Map json) => Country( + id: json["id"], + code: json["code"], + name: json["name"], + ); + + Map toJson() => { + "id": id, + "code": code, + "name": name, + }; +} diff --git a/lib/model/profile/eligibility.dart b/lib/model/profile/eligibility.dart new file mode 100644 index 0000000..8da69b6 --- /dev/null +++ b/lib/model/profile/eligibility.dart @@ -0,0 +1,250 @@ +// To parse this JSON data, do +// +// final eligibity = eligibityFromJson(jsonString); + +import 'package:meta/meta.dart'; +import 'dart:convert'; + +EligibityCert eligibityFromJson(String str) => EligibityCert.fromJson(json.decode(str)); + +String eligibityToJson(EligibityCert data) => json.encode(data.toJson()); + +class EligibityCert { + EligibityCert({ + required this.id, + required this.rating, + required this.examDate, + required this.attachments, + required this.eligibility, + required this.examAddress, + required this.validityDate, + required this.licenseNumber, + }); + + final int? id; + final double? rating; + final DateTime? examDate; + final dynamic attachments; + final Eligibility? eligibility; + final ExamAddress? examAddress; + final DateTime? validityDate; + final DateTime? licenseNumber; + + factory EligibityCert.fromJson(Map json) => EligibityCert( + id: json["id"], + rating: json["rating"]?.toDouble(), + examDate: DateTime.parse(json["exam_date"]), + attachments: null, + eligibility: Eligibility.fromJson(json["eligibility"]), + examAddress: ExamAddress.fromJson(json["exam_address"]), + validityDate: json["validity_date"], + licenseNumber: json["license_number"], + ); + + Map toJson() => { + "id": id, + "rating": rating, + "exam_date": "${examDate!.year.toString().padLeft(4, '0')}-${examDate!.month.toString().padLeft(2, '0')}-${examDate!.day.toString().padLeft(2, '0')}", + "attachments": attachments, + "eligibility": eligibility!.toJson(), + "exam_address": examAddress!.toJson(), + "validity_date": validityDate, + "license_number": licenseNumber, + }; +} + +class Eligibility { + Eligibility({ + required this.id, + required this.type, + required this.title, + }); + + final int? id; + final String? type; + final String? title; + + factory Eligibility.fromJson(Map json) => Eligibility( + id: json["id"], + type: json["type"], + title: json["title"], + ); + + Map toJson() => { + "id": id, + "type": type, + "title": title, + }; +} + +class ExamAddress { + ExamAddress({ + required this.id, + required this.examAddressClass, + required this.country, + required this.barangay, + required this.addressCategory, + required this.cityMunicipality, + }); + + final int? id; + final dynamic examAddressClass; + final Country? country; + final dynamic barangay; + final AddressCategory? addressCategory; + final CityMunicipality? cityMunicipality; + + factory ExamAddress.fromJson(Map json) => ExamAddress( + id: json["id"], + examAddressClass: json["class"], + country: Country.fromJson(json["country"]), + barangay: json["barangay"], + addressCategory: AddressCategory.fromJson(json["address_category"]), + cityMunicipality: CityMunicipality.fromJson(json["city_municipality"]), + ); + + Map toJson() => { + "id": id, + "class": examAddressClass, + "country": country!.toJson(), + "barangay": barangay, + "address_category": addressCategory!.toJson(), + "city_municipality": cityMunicipality!.toJson(), + }; +} + +class AddressCategory { + AddressCategory({ + required this.id, + required this.name, + required this.type, + }); + + final int? id; + final String? name; + final String? type; + + factory AddressCategory.fromJson(Map json) => AddressCategory( + id: json["id"], + name: json["name"], + type: json["type"], + ); + + Map toJson() => { + "id": id, + "name": name, + "type": type, + }; +} + +class CityMunicipality { + CityMunicipality({ + required this.code, + required this.zipcode, + required this.province, + required this.psgcCode, + required this.description, + }); + + final String? code; + final String? zipcode; + final Province? province; + final String? psgcCode; + final String? description; + + factory CityMunicipality.fromJson(Map json) => CityMunicipality( + code: json["code"], + zipcode: json["zipcode"], + province: Province.fromJson(json["province"]), + psgcCode: json["psgc_code"], + description: json["description"], + ); + + Map toJson() => { + "code": code, + "zipcode": zipcode, + "province": province!.toJson(), + "psgc_code": psgcCode, + "description": description, + }; +} + +class Province { + Province({ + required this.code, + required this.region, + required this.psgcCode, + required this.shortname, + required this.description, + }); + + final String? code; + final Region? region; + final String? psgcCode; + final String? shortname; + final String? description; + + factory Province.fromJson(Map json) => Province( + code: json["code"], + region: Region.fromJson(json["region"]), + psgcCode: json["psgc_code"], + shortname: json["shortname"], + description: json["description"], + ); + + Map toJson() => { + "code": code, + "region": region!.toJson(), + "psgc_code": psgcCode, + "shortname": shortname, + "description": description, + }; +} + +class Region { + Region({ + required this.code, + required this.psgcCode, + required this.description, + }); + + final int? code; + final String? psgcCode; + final String? description; + + factory Region.fromJson(Map json) => Region( + code: json["code"], + psgcCode: json["psgc_code"], + description: json["description"], + ); + + Map toJson() => { + "code": code, + "psgc_code": psgcCode, + "description": description, + }; +} + +class Country { + Country({ + required this.id, + required this.code, + required this.name, + }); + + final int? id; + final String? code; + final String? name; + + factory Country.fromJson(Map json) => Country( + id: json["id"], + code: json["code"], + name: json["name"], + ); + + Map toJson() => { + "id": id, + "code": code, + "name": name, + }; +} diff --git a/lib/model/profile/learning_development.dart b/lib/model/profile/learning_development.dart new file mode 100644 index 0000000..4131a02 --- /dev/null +++ b/lib/model/profile/learning_development.dart @@ -0,0 +1,361 @@ +// To parse this JSON data, do +// +// final learningDevelopement = learningDevelopementFromJson(jsonString); + +import 'dart:convert'; + +LearningDevelopement learningDevelopementFromJson(String str) => LearningDevelopement.fromJson(json.decode(str)); + +String learningDevelopementToJson(LearningDevelopement data) => json.encode(data.toJson()); + +class LearningDevelopement { + LearningDevelopement({ + this.attachments, + this.sponsoredBy, + this.conductedTraining, + this.totalHoursAttended, + }); + + final dynamic attachments; + final EdBy? sponsoredBy; + final ConductedTraining? conductedTraining; + final int? totalHoursAttended; + + factory LearningDevelopement.fromJson(Map json) => LearningDevelopement( + attachments: json["attachments"], + sponsoredBy: json["sponsored_by"] == null ? null : EdBy.fromJson(json["sponsored_by"]), + conductedTraining: json["conducted_training"] == null ? null : ConductedTraining.fromJson(json["conducted_training"]), + totalHoursAttended: json["total_hours_attended"], + ); + + Map toJson() => { + "attachments": attachments, + "sponsored_by": sponsoredBy?.toJson(), + "conducted_training": conductedTraining?.toJson(), + "total_hours_attended": totalHoursAttended, + }; +} + +class ConductedTraining { + ConductedTraining({ + this.id, + this.title, + this.topic, + this.venue, + this.locked, + this.toDate, + this.fromDate, + this.totalHours, + this.conductedBy, + this.sessionsAttended, + this.learningDevelopmentType, + }); + + final int? id; + final LearningDevelopmentType? title; + final LearningDevelopmentType? topic; + final Venue? venue; + final bool? locked; + final DateTime? toDate; + final DateTime? fromDate; + final int? totalHours; + final EdBy? conductedBy; + final List? sessionsAttended; + final LearningDevelopmentType? learningDevelopmentType; + + factory ConductedTraining.fromJson(Map json) => ConductedTraining( + id: json["id"], + title: json["title"] == null ? null : LearningDevelopmentType.fromJson(json["title"]), + topic: json["topic"] == null ? null : LearningDevelopmentType.fromJson(json["topic"]), + venue: json["venue"] == null ? null : Venue.fromJson(json["venue"]), + locked: json["locked"], + toDate: json["to_date"] == null ? null : DateTime.parse(json["to_date"]), + fromDate: json["from_date"] == null ? null : DateTime.parse(json["from_date"]), + totalHours: json["total_hours"], + conductedBy: json["conducted_by"] == null ? null : EdBy.fromJson(json["conducted_by"]), + sessionsAttended: json["sessions_attended"] == null ? [] : List.from(json["sessions_attended"]!.map((x) => x)), + learningDevelopmentType: json["learning_development_type"] == null ? null : LearningDevelopmentType.fromJson(json["learning_development_type"]), + ); + + Map toJson() => { + "id": id, + "title": title?.toJson(), + "topic": topic?.toJson(), + "venue": venue?.toJson(), + "locked": locked, + "to_date": "${toDate!.year.toString().padLeft(4, '0')}-${toDate!.month.toString().padLeft(2, '0')}-${toDate!.day.toString().padLeft(2, '0')}", + "from_date": "${fromDate!.year.toString().padLeft(4, '0')}-${fromDate!.month.toString().padLeft(2, '0')}-${fromDate!.day.toString().padLeft(2, '0')}", + "total_hours": totalHours, + "conducted_by": conductedBy?.toJson(), + "sessions_attended": sessionsAttended == null ? [] : List.from(sessionsAttended!.map((x) => x)), + "learning_development_type": learningDevelopmentType?.toJson(), + }; +} + +class EdBy { + EdBy({ + this.id, + this.name, + this.category, + this.privateEntity, + }); + + final int? id; + final String? name; + final SponsoredByCategory? category; + final bool? privateEntity; + + factory EdBy.fromJson(Map json) => EdBy( + id: json["id"], + name: json["name"], + category: json["category"] == null ? null : SponsoredByCategory.fromJson(json["category"]), + privateEntity: json["private_entity"], + ); + + Map toJson() => { + "id": id, + "name": name, + "category": category?.toJson(), + "private_entity": privateEntity, + }; +} + +class SponsoredByCategory { + SponsoredByCategory({ + this.id, + this.name, + this.industryClass, + }); + + final int? id; + final String? name; + final IndustryClass? industryClass; + + factory SponsoredByCategory.fromJson(Map json) => SponsoredByCategory( + id: json["id"], + name: json["name"], + industryClass: json["industry_class"] == null ? null : IndustryClass.fromJson(json["industry_class"]), + ); + + Map toJson() => { + "id": id, + "name": name, + "industry_class": industryClass?.toJson(), + }; +} + +class IndustryClass { + IndustryClass({ + this.id, + this.name, + this.description, + }); + + final int? id; + final String? name; + final dynamic description; + + factory IndustryClass.fromJson(Map json) => IndustryClass( + id: json["id"], + name: json["name"], + description: json["description"], + ); + + Map toJson() => { + "id": id, + "name": name, + "description": description, + }; +} + +class LearningDevelopmentType { + LearningDevelopmentType({ + this.id, + this.title, + }); + + final int? id; + final String? title; + + factory LearningDevelopmentType.fromJson(Map json) => LearningDevelopmentType( + id: json["id"], + title: json["title"], + ); + + Map toJson() => { + "id": id, + "title": title, + }; +} + +class Venue { + Venue({ + this.id, + this.country, + this.barangay, + this.category, + this.areaClass, + this.cityMunicipality, + }); + + final int? id; + final Country? country; + final dynamic barangay; + final VenueCategory? category; + final dynamic areaClass; + final CityMunicipality? cityMunicipality; + + factory Venue.fromJson(Map json) => Venue( + id: json["id"], + country: json["country"] == null ? null : Country.fromJson(json["country"]), + barangay: json["barangay"], + category: json["category"] == null ? null : VenueCategory.fromJson(json["category"]), + areaClass: json["area_class"], + cityMunicipality: json["city_municipality"] == null ? null : CityMunicipality.fromJson(json["city_municipality"]), + ); + + Map toJson() => { + "id": id, + "country": country?.toJson(), + "barangay": barangay, + "category": category?.toJson(), + "area_class": areaClass, + "city_municipality": cityMunicipality?.toJson(), + }; +} + +class VenueCategory { + VenueCategory({ + this.id, + this.name, + this.type, + }); + + final int? id; + final String? name; + final String? type; + + factory VenueCategory.fromJson(Map json) => VenueCategory( + id: json["id"], + name: json["name"], + type: json["type"], + ); + + Map toJson() => { + "id": id, + "name": name, + "type": type, + }; +} + +class CityMunicipality { + CityMunicipality({ + this.code, + this.zipcode, + this.province, + this.psgcCode, + this.description, + }); + + final String? code; + final String? zipcode; + final Province? province; + final String? psgcCode; + final String? description; + + factory CityMunicipality.fromJson(Map json) => CityMunicipality( + code: json["code"], + zipcode: json["zipcode"], + province: json["province"] == null ? null : Province.fromJson(json["province"]), + psgcCode: json["psgc_code"], + description: json["description"], + ); + + Map toJson() => { + "code": code, + "zipcode": zipcode, + "province": province?.toJson(), + "psgc_code": psgcCode, + "description": description, + }; +} + +class Province { + Province({ + this.code, + this.region, + this.psgcCode, + this.shortname, + this.description, + }); + + final String? code; + final Region? region; + final String? psgcCode; + final String? shortname; + final String? description; + + factory Province.fromJson(Map json) => Province( + code: json["code"], + region: json["region"] == null ? null : Region.fromJson(json["region"]), + psgcCode: json["psgc_code"], + shortname: json["shortname"], + description: json["description"], + ); + + Map toJson() => { + "code": code, + "region": region?.toJson(), + "psgc_code": psgcCode, + "shortname": shortname, + "description": description, + }; +} + +class Region { + Region({ + this.code, + this.psgcCode, + this.description, + }); + + final int? code; + final String? psgcCode; + final String? description; + + factory Region.fromJson(Map json) => Region( + code: json["code"], + psgcCode: json["psgc_code"], + description: json["description"], + ); + + Map toJson() => { + "code": code, + "psgc_code": psgcCode, + "description": description, + }; +} + +class Country { + Country({ + this.id, + this.code, + this.name, + }); + + final int? id; + final String? code; + final String? name; + + factory Country.fromJson(Map json) => Country( + id: json["id"], + code: json["code"], + name: json["name"], + ); + + Map toJson() => { + "id": id, + "code": code, + "name": name, + }; +} diff --git a/lib/model/profile/profileInfomation.dart b/lib/model/profile/profileInfomation.dart index 891b522..1e7fa36 100644 --- a/lib/model/profile/profileInfomation.dart +++ b/lib/model/profile/profileInfomation.dart @@ -1,7 +1,13 @@ import 'package:unit2/model/profile/basic_info.dart'; import 'package:unit2/model/profile/basic_information/primary-information.dart'; +import 'package:unit2/model/profile/eligibility.dart'; +import 'package:unit2/model/profile/learning_development.dart'; +import 'package:unit2/model/profile/references.dart'; class ProfileInformation{ BasicInfo basicInfo; - ProfileInformation({required this.basicInfo}); + List eligibilities; + List references; + List learningsAndDevelopment; + ProfileInformation({required this.basicInfo,required this.eligibilities,required this.references, required this.learningsAndDevelopment}); } \ No newline at end of file diff --git a/lib/model/profile/references.dart b/lib/model/profile/references.dart new file mode 100644 index 0000000..b353cec --- /dev/null +++ b/lib/model/profile/references.dart @@ -0,0 +1,234 @@ +// To parse this JSON data, do +// +// final references = referencesFromJson(jsonString); +class PersonalReference { + PersonalReference({ + required this.id, + required this.address, + required this.lastName, + required this.contactNo, + required this.firstName, + required this.middleName, + }); + + final int? id; + final Address? address; + final String? lastName; + final String? contactNo; + final String? firstName; + final String? middleName; + + factory PersonalReference.fromJson(Map json) => PersonalReference( + id: json["id"], + address: json['address'] == null?null: Address.fromJson(json["address"]), + lastName: json["last_name"], + contactNo: json["contact_no"], + firstName: json["first_name"], + middleName: json["middle_name"], + ); + + Map toJson() => { + "id": id, + "address": address!.toJson(), + "last_name": lastName, + "contact_no": contactNo, + "first_name": firstName, + "middle_name": middleName, + }; +} + +class Address { + Address({ + required this.id, + required this.addressClass, + required this.country, + required this.barangay, + required this.addressCategory, + required this.cityMunicipality, + }); + + final int? id; + final String? addressClass; + final Country? country; + final Barangay? barangay; + final AddressCategory? addressCategory; + final CityMunicipality? cityMunicipality; + + factory Address.fromJson(Map json) => Address( + id: json["id"], + addressClass: json["class"], + country: json['country']== null?null: Country.fromJson(json["country"]), + barangay:json["barangay"]== null?null: Barangay.fromJson(json["barangay"]), + addressCategory: json["address_category"]== null? null: AddressCategory.fromJson(json["address_category"]), + cityMunicipality: json["city_municipality"]==null?null: CityMunicipality.fromJson(json["city_municipality"]), + ); + + Map toJson() => { + "id": id, + "class": addressClass, + "country": country!.toJson(), + "barangay": barangay!.toJson(), + "address_category": addressCategory!.toJson(), + "city_municipality": cityMunicipality!.toJson(), + }; +} + +class AddressCategory { + AddressCategory({ + required this.id, + required this.name, + required this.type, + }); + + final int? id; + final String? name; + final String? type; + + factory AddressCategory.fromJson(Map json) => AddressCategory( + id: json["id"], + name: json["name"], + type: json["type"], + ); + + Map toJson() => { + "id": id, + "name": name, + "type": type, + }; +} + +class Barangay { + Barangay({ + required this.code, + required this.description, + required this.cityMunicipality, + }); + + final String? code; + final String? description; + final CityMunicipality? cityMunicipality; + + factory Barangay.fromJson(Map json) => Barangay( + code: json["code"], + description: json["description"], + cityMunicipality: CityMunicipality.fromJson(json["city_municipality"]), + ); + + Map toJson() => { + "code": code, + "description": description, + "city_municipality": cityMunicipality!.toJson(), + }; +} + +class CityMunicipality { + CityMunicipality({ + required this.code, + required this.zipcode, + required this.province, + required this.psgcCode, + required this.description, + }); + + final String? code; + final String? zipcode; + final Province? province; + final String? psgcCode; + final String? description; + + factory CityMunicipality.fromJson(Map json) => CityMunicipality( + code: json["code"], + zipcode: json["zipcode"], + province: Province.fromJson(json["province"]), + psgcCode: json["psgc_code"], + description: json["description"], + ); + + Map toJson() => { + "code": code, + "zipcode": zipcode, + "province": province!.toJson(), + "psgc_code": psgcCode, + "description": description, + }; +} + +class Province { + Province({ + required this.code, + required this.region, + required this.psgcCode, + required this.shortname, + required this.description, + }); + + final String? code; + final Region? region; + final String? psgcCode; + final String? shortname; + final String? description; + + factory Province.fromJson(Map json) => Province( + code: json["code"], + region: Region.fromJson(json["region"]), + psgcCode: json["psgc_code"], + shortname: json["shortname"], + description: json["description"], + ); + + Map toJson() => { + "code": code, + "region": region!.toJson(), + "psgc_code": psgcCode, + "shortname": shortname, + "description": description, + }; +} + +class Region { + Region({ + required this.code, + required this.psgcCode, + required this.description, + }); + + final int? code; + final String? psgcCode; + final String? description; + + factory Region.fromJson(Map json) => Region( + code: json["code"], + psgcCode: json["psgc_code"], + description: json["description"], + ); + + Map toJson() => { + "code": code, + "psgc_code": psgcCode, + "description": description, + }; +} + +class Country { + Country({ + required this.id, + required this.code, + required this.name, + }); + + final int? id; + final String? code; + final String? name; + + factory Country.fromJson(Map json) => Country( + id: json["id"], + code: json["code"], + name: json["name"], + ); + + Map toJson() => { + "id": id, + "code": code, + "name": name, + }; +} diff --git a/lib/screens/profile/components/basic_information/citizenship_screen.dart b/lib/screens/profile/components/basic_information/citizenship_screen.dart new file mode 100644 index 0000000..3275bd0 --- /dev/null +++ b/lib/screens/profile/components/basic_information/citizenship_screen.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:unit2/model/profile/basic_information/citizenship.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/global.dart'; + +class CitizenShipScreen extends StatefulWidget { + final List citizenships; + const CitizenShipScreen({super.key, required this.citizenships}); + + @override + State createState() => _CitizenShipScreenState(); +} + +class _CitizenShipScreenState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("Citizenship"),centerTitle: true, + backgroundColor: primary, + ), + body: widget.citizenships.isEmpty? + Container( + width: screenWidth, + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: const BorderRadius.all(Radius.circular(12))), + + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Philippines",style: Theme.of(context).textTheme.titleLarge,), + Text("Filipino",style: Theme.of(context).textTheme.titleSmall,) + ], + + + ), + ):Container() + , + ); + } +} \ No newline at end of file diff --git a/lib/screens/profile/components/basic_information/contact_information.dart b/lib/screens/profile/components/basic_information/contact_information.dart index 9fa40aa..68962a6 100644 --- a/lib/screens/profile/components/basic_information/contact_information.dart +++ b/lib/screens/profile/components/basic_information/contact_information.dart @@ -1,6 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; import 'package:unit2/model/profile/basic_information/contact_information.dart'; import 'package:unit2/theme-data.dart/colors.dart'; @@ -25,53 +23,51 @@ class _ContactInformationState extends State { body: ListView.builder( itemCount: widget.contacts.length, itemBuilder: (BuildContext context, int index) { - return Container( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - decoration: BoxDecoration( - color: Colors.grey[200], - borderRadius: const BorderRadius.all(Radius.circular(12))), - padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: const BorderRadius.all(Radius.circular(12))), + padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(widget.contacts[index].numbermail.toString(),style: Theme.of(context).textTheme.titleLarge,), + const SizedBox(height: 5,), + Row( children: [ - Text(widget.contacts[index].numbermail.toString(),style: Theme.of(context).textTheme.titleLarge,), - const SizedBox(height: 5,), - Row( - children: [ - Text(widget.contacts[index].commService! - .serviceProvider!.alias - .toString()), - const SizedBox(width: 15,), - widget.contacts[index].active==true? const Badge(backgroundColor: Colors.green, label: Text("Active",),):const SizedBox(), - const SizedBox(width: 8), - widget.contacts[index].primary==true? const Badge(backgroundColor: Colors.blue, label: Text("Primary"),):const SizedBox() - ], - ), - const SizedBox(height: 5,), - Text(widget.contacts[index].commService! - .serviceProvider!.agency!.name - .toString()), - - ]), - ), - IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)) - ], - ), + Text(widget.contacts[index].commService! + .serviceProvider!.alias + .toString()), + const SizedBox(width: 15,), + widget.contacts[index].active==true? const Badge(backgroundColor: Colors.green, label: Text("Active",),):const SizedBox(), + const SizedBox(width: 8), + widget.contacts[index].primary==true? const Badge(backgroundColor: Colors.blue, label: Text("Primary"),):const SizedBox() + ], + ), + const SizedBox(height: 5,), + Text(widget.contacts[index].commService! + .serviceProvider!.agency!.name + .toString()), + + ]), + ), + IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)) + ], ), - const SizedBox(height: 5,), - + ), + const SizedBox(height: 5,), + - ], - ), + ], ); }), ), diff --git a/lib/screens/profile/components/basic_information/identification_information.dart b/lib/screens/profile/components/basic_information/identification_information.dart new file mode 100644 index 0000000..7cf8efc --- /dev/null +++ b/lib/screens/profile/components/basic_information/identification_information.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:unit2/model/profile/basic_information/identification_information.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/global.dart'; + +class IdentificationInformations extends StatefulWidget { + final List identities; + const IdentificationInformations({super.key, required this.identities}); + + @override + State createState() => _IdentificationInformationsState(); +} + +class _IdentificationInformationsState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text("Identifications"),centerTitle: true, backgroundColor: primary,), +body: ListView.builder( + itemCount: widget.identities.length, + itemBuilder: (BuildContext context, int index){ + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: screenWidth, + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: const BorderRadius.all(Radius.circular(12))), + padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(widget.identities[index].agency!.name!,style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.bold)), + Row( + children: [ + Text(widget.identities[index].identificationNumber!,style: Theme.of(context).textTheme.titleSmall!.copyWith(fontWeight: FontWeight.bold)), + const SizedBox(width: 20,), + Badge(backgroundColor: success2, label:Text(widget.identities[index].agency!.privateEntity==true?"PRIVATE":"GOVERNMENT",)), + ], + ), + Text("${widget.identities[index].issuedAt!.cityMunicipality!.description!} ${widget.identities[index].issuedAt!.cityMunicipality!.province!.description}"), + ]), + + ), + IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)) + ], + ), + ), + const SizedBox(height: 5,), + ], + ); +}), + ); + } +} \ No newline at end of file diff --git a/lib/screens/profile/components/eligibility.dart b/lib/screens/profile/components/eligibility.dart new file mode 100644 index 0000000..be2d7ff --- /dev/null +++ b/lib/screens/profile/components/eligibility.dart @@ -0,0 +1,68 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:unit2/model/profile/eligibility.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/global.dart'; + +class EligibiltyScreen extends StatefulWidget { + final List eligibilities; + const EligibiltyScreen({super.key, required this.eligibilities}); + + @override + State createState() => _EligibiltyScreenState(); +} + +class _EligibiltyScreenState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("Eligibility"), + centerTitle: true, + backgroundColor: primary, + ), + body: ListView.builder( + itemCount: widget.eligibilities.length, + itemBuilder: (BuildContext context, int index) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: screenWidth, + padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: + const BorderRadius.all(Radius.circular(12))), + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + + children: [ + + Text(widget.eligibilities[index].eligibility!.title!, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(fontWeight: FontWeight.bold)), + Text( + "License number: ${widget.eligibilities[index].licenseNumber==null?'N/A':widget.eligibilities[index].licenseNumber.toString()}"), + Text("Rating: ${widget.eligibilities[index].rating}.") + ]), + + ), + IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)) + ], + ), + ) + ], + ); + }), + ); + } +} diff --git a/lib/screens/profile/components/learning_and_development_screen.dart b/lib/screens/profile/components/learning_and_development_screen.dart new file mode 100644 index 0000000..f2f89ed --- /dev/null +++ b/lib/screens/profile/components/learning_and_development_screen.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:intl/intl.dart'; +import 'package:unit2/model/profile/learning_development.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/global.dart'; + +class LearningAndDevelopmentScreen extends StatefulWidget { + final List learningDevelopments; + const LearningAndDevelopmentScreen({super.key, required this.learningDevelopments}); + + @override + State createState() => _LearningAndDevelopmentScreenState(); +} + +class _LearningAndDevelopmentScreenState extends State { + DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text("Learning and Development"), + centerTitle: true, + backgroundColor: primary, + + ), + body: ListView.builder( + itemCount: widget.learningDevelopments.length, + itemBuilder: (BuildContext context, int index){ + String start = dteFormat2.format(widget.learningDevelopments[index].conductedTraining!.fromDate!); + String end = dteFormat2.format(widget.learningDevelopments[index].conductedTraining!.toDate!); + String type = widget.learningDevelopments[index].conductedTraining!.learningDevelopmentType!.title!; + return Column( + children: [ + Container( + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: const BorderRadius.all(Radius.circular(12))), + padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), + width: screenWidth, + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(widget.learningDevelopments[index].conductedTraining!.title!.title!), + const SizedBox(height: 5,), + Text(widget.learningDevelopments[index].conductedTraining!.conductedBy!.name!), + const SizedBox(height: 5,), + Text("$start TO $end"), + const SizedBox(height: 5,), + Text("TYPE : $type"), + ]), + ), + IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)), + ], + ), + ), + const SizedBox(height: 8,), + ], + ); + }), + ); + } +} \ No newline at end of file diff --git a/lib/screens/profile/components/loading_screen.dart b/lib/screens/profile/components/loading_screen.dart index a84ebaf..eda754a 100644 --- a/lib/screens/profile/components/loading_screen.dart +++ b/lib/screens/profile/components/loading_screen.dart @@ -47,39 +47,60 @@ class LoadingScreen extends StatelessWidget { subMenu(Icons.flag, "Citizenships",(){}), ]), const Divider(), - const MainMenu( + MainMenu( icon: Elusive.group, title: "Family", + onTap: () { + + }, ), const Divider(), - const MainMenu( + MainMenu( icon: FontAwesome5.graduation_cap, title: "Education", + onTap: () { + + }, ), const Divider(), - const MainMenu( + MainMenu( icon: Icons.stars, title: "Eligibility", + onTap: () { + + }, ), const Divider(), - const MainMenu( + MainMenu( icon: FontAwesome5.shopping_bag, title: "Work History", + onTap: () { + + }, ), const Divider(), - const MainMenu( + MainMenu( icon: FontAwesome5.walking, title: "Voluntary Work & Civic Services", + onTap: () { + + }, ), const Divider(), - const MainMenu( + MainMenu( icon: Elusive.lightbulb, title: "Learning & Development", + onTap: () { + + }, ), const Divider(), - const MainMenu( + MainMenu( icon: Brandico.codepen, title: "Personal References", + onTap: () { + + }, ), ExpandableGroup( collapsedIcon: diff --git a/lib/screens/profile/components/main_menu.dart b/lib/screens/profile/components/main_menu.dart index d315a7d..cdc5c26 100644 --- a/lib/screens/profile/components/main_menu.dart +++ b/lib/screens/profile/components/main_menu.dart @@ -4,9 +4,11 @@ import 'package:unit2/theme-data.dart/colors.dart'; class MainMenu extends StatelessWidget { final IconData icon; final String title; + final Function() onTap; const MainMenu({ required this.icon, required this.title, + required this.onTap, Key? key, }) : super(key: key); @@ -19,9 +21,10 @@ class MainMenu extends StatelessWidget { ), title: Text( title, - style: TextStyle(fontWeight: FontWeight.bold), + style: const TextStyle(fontWeight: FontWeight.bold), ), trailing: const Icon(Icons.keyboard_arrow_right), + onTap: onTap, ); } } diff --git a/lib/screens/profile/components/references_screen.dart b/lib/screens/profile/components/references_screen.dart new file mode 100644 index 0000000..5fb200b --- /dev/null +++ b/lib/screens/profile/components/references_screen.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:unit2/model/profile/references.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; + +class ReferencesScreen extends StatefulWidget { + final List references; + const ReferencesScreen({super.key, required this.references}); + + @override + State createState() => _ReferencesScreenState(); +} + +class _ReferencesScreenState extends State { + @override + Widget build(BuildContext context) { + + return Scaffold( + appBar: AppBar(title: const Text("Personal References"),centerTitle: true,backgroundColor: primary,), + body: ListView.builder( + itemCount: widget.references.length, + itemBuilder: (BuildContext context, int index){ + String fullname = "${widget.references[0].firstName} ${widget.references[0].middleName} ${widget.references[0].lastName}"; + String addres = "${widget.references[0].address!.cityMunicipality!.description}, ${widget.references[0].address!.cityMunicipality!.province!.description}, ${widget.references[0].address!.cityMunicipality!.province!.region!.description}"; + String mobile = widget.references[0].contactNo.toString(); + return Column(children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: const BorderRadius.all(Radius.circular(12))), + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + + Text(fullname,style: Theme.of(context).textTheme.titleLarge!.copyWith(fontWeight: FontWeight.bold)), + const SizedBox(height: 5,), + Text(addres,style: Theme.of(context).textTheme.labelLarge!.copyWith(fontWeight: FontWeight.bold)), + const SizedBox(height: 8,), + Text("PHONE / MOBILE NUMBER $mobile",style: Theme.of(context).textTheme.labelSmall!.copyWith(fontWeight: FontWeight.bold)), + ],), + ), + IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)) + ], + ), + ), + const SizedBox(height: 8,), + ],); + }) , + ); + } +} \ No newline at end of file diff --git a/lib/screens/profile/profile.dart b/lib/screens/profile/profile.dart index 8e7b6f0..3cd5c79 100644 --- a/lib/screens/profile/profile.dart +++ b/lib/screens/profile/profile.dart @@ -8,9 +8,14 @@ import 'package:fluttericon/entypo_icons.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:fluttericon/modern_pictograms_icons.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/screens/profile/components/basic_information/citizenship_screen.dart'; import 'package:unit2/screens/profile/components/basic_information/contact_information.dart'; +import 'package:unit2/screens/profile/components/basic_information/identification_information.dart'; import 'package:unit2/screens/profile/components/basic_information/primary_information.dart'; +import 'package:unit2/screens/profile/components/eligibility.dart'; +import 'package:unit2/screens/profile/components/learning_and_development_screen.dart'; import 'package:unit2/screens/profile/components/loading_screen.dart'; +import 'package:unit2/screens/profile/components/references_screen.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import '../../bloc/user/user_bloc.dart'; import 'components/main_menu.dart'; @@ -84,49 +89,76 @@ class _ProfileInfoState extends State { }), subMenu(Icons.home, "Home Addresses",(){}), subMenu( - Icons.contact_mail, "Identifications",(){}), + Icons.contact_mail, "Identifications",(){ + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + return IdentificationInformations(identities: state.profileInformation.basicInfo.identifications); + })); + }), subMenu( Icons.contact_phone, "Contact Info",(){ Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ return ContactInformation(contacts: state.profileInformation.basicInfo.contactInformation,); })); }), - subMenu(Icons.flag, "Citizenships",(){}), + subMenu(Icons.flag, "Citizenships",(){ + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + return CitizenShipScreen(citizenships: state.profileInformation.basicInfo.citizenships,); + })); + }), ]), const Divider(), - const MainMenu( + MainMenu( icon: Elusive.group, title: "Family", + onTap: (){}, ), const Divider(), - const MainMenu( + MainMenu( icon: FontAwesome5.graduation_cap, title: "Education", + onTap: (){}, ), const Divider(), - const MainMenu( + MainMenu( icon: Icons.stars, title: "Eligibility", + onTap: (){ + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + return EligibiltyScreen(eligibilities: state.profileInformation.eligibilities); + })); + }, ), const Divider(), - const MainMenu( + MainMenu( icon: FontAwesome5.shopping_bag, title: "Work History", + onTap: (){}, ), const Divider(), - const MainMenu( + MainMenu( icon: FontAwesome5.walking, title: "Voluntary Work & Civic Services", + onTap: (){}, ), const Divider(), - const MainMenu( + MainMenu( icon: Elusive.lightbulb, title: "Learning & Development", + onTap: (){ + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + return LearningAndDevelopmentScreen(learningDevelopments: state.profileInformation.learningsAndDevelopment); + })); + }, ), const Divider(), - const MainMenu( + MainMenu( icon: Brandico.codepen, title: "Personal References", + onTap: (){ + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + return ReferencesScreen(references: state.profileInformation.references); + })); + }, ), ExpandableGroup( collapsedIcon: diff --git a/lib/sevices/profile/profile_service.dart b/lib/sevices/profile/profile_service.dart index c502434..6f25add 100644 --- a/lib/sevices/profile/profile_service.dart +++ b/lib/sevices/profile/profile_service.dart @@ -2,8 +2,13 @@ import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:unit2/model/login_data/employee_info/employee_info.dart'; import 'package:unit2/model/profile/basic_info.dart'; +import 'package:unit2/model/profile/basic_information/citizenship.dart'; import 'package:unit2/model/profile/basic_information/contact_information.dart'; +import 'package:unit2/model/profile/basic_information/identification_information.dart'; +import 'package:unit2/model/profile/eligibility.dart'; +import 'package:unit2/model/profile/learning_development.dart'; import 'package:unit2/model/profile/profileInfomation.dart'; +import 'package:unit2/model/profile/references.dart'; import 'package:unit2/utils/request.dart'; import 'package:unit2/utils/urls.dart'; @@ -16,19 +21,25 @@ class ProfileService { Future getProfile(String token, int id) async { String url = Url.instance.profileInformation(); String path = url + id.toString(); - ProfileInformation? _profileInformation; + ProfileInformation? profileInformation0; + List references = []; ContactInfo contactInfo; + Identification identification; + List identificationInformation =[]; List contactInformation = []; PrimaryInformation primaryInformation; + List eligibilities = []; + List citizenships = []; + List learningsDevelopments = []; Map headers = { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': "Token $token" }; - try{ + // try{ http.Response response = await Request.instance .getRequest(path: path, param: {}, headers: headers); if (response.statusCode == 200) { - Map data = jsonDecode(response.body); + Map data = jsonDecode(response.body); // get primary information primaryInformation = PrimaryInformation.fromJson( data['data']['basic_information']['primary_information']); @@ -38,13 +49,49 @@ class ProfileService { contactInfo = ContactInfo.fromJson(contact['contact_info']); contactInformation.add(contactInfo); }); - BasicInfo basicInfo = BasicInfo(contactInformation: contactInformation, primaryInformation: primaryInformation); - ProfileInformation profileInformation = ProfileInformation(basicInfo: basicInfo); - _profileInformation = profileInformation; + // get all identifications + data['data']['basic_information']['identification_records'].forEach((var identity){ + identification = Identification.fromJson(identity); + identificationInformation.add(identification); + }); + //get all eligibilities + data['data']['eligibilities'].forEach((var cert){ + EligibityCert eligibility = EligibityCert.fromJson(cert); + eligibilities.add(eligibility); + }); + // get all citizenships + if(data['data']['citizenship']!= null){ + data['data']['citizenships'].forEach((var citizenship){ + Citizenship person = Citizenship.fromJson(citizenship); + citizenships.add(person); + }); + } + // get all references; + data['data']['personal_references'].forEach((var person){ + PersonalReference reference = PersonalReference.fromJson(person); + references.add(reference); + }); + + //get all learning and developments + + data['data']['learning_development'].forEach((var training){ + LearningDevelopement learnings = LearningDevelopement.fromJson(training); + learningsDevelopments.add(learnings); + }); + + + + BasicInfo basicInfo = BasicInfo(contactInformation: contactInformation, + primaryInformation: primaryInformation, + identifications: identificationInformation, + citizenships: citizenships + ); + ProfileInformation profileInformation = ProfileInformation(basicInfo: basicInfo,eligibilities: eligibilities,references: references!,learningsAndDevelopment: learningsDevelopments); + profileInformation0 = profileInformation; } - }catch(e){ - throw(e.toString()); - } - return _profileInformation; + // }catch(e){ + // throw(e.toString()); + // } + return profileInformation0; } } diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index cee712a..525cc13 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -4,8 +4,8 @@ class Url { String host() { // return '192.168.10.221:3003'; - // return 'agusandelnorte.gov.ph'; - return 'devweb.agusandelnorte.gov.ph'; + return 'agusandelnorte.gov.ph'; + // return 'devweb.agusandelnorte.gov.ph'; } String authentication() { diff --git a/macos/Podfile.lock b/macos/Podfile.lock new file mode 100644 index 0000000..aa74d3f --- /dev/null +++ b/macos/Podfile.lock @@ -0,0 +1,51 @@ +PODS: + - FlutterMacOS (1.0.0) + - FMDB (2.7.5): + - FMDB/standard (= 2.7.5) + - FMDB/standard (2.7.5) + - package_info_plus (0.0.1): + - FlutterMacOS + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS + - shared_preferences_foundation (0.0.1): + - Flutter + - FlutterMacOS + - sqflite (0.0.2): + - FlutterMacOS + - FMDB (>= 2.7.5) + +DEPENDENCIES: + - FlutterMacOS (from `Flutter/ephemeral`) + - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) + - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/macos`) + - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos`) + - sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`) + +SPEC REPOS: + trunk: + - FMDB + +EXTERNAL SOURCES: + FlutterMacOS: + :path: Flutter/ephemeral + package_info_plus: + :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos + path_provider_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/macos + shared_preferences_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos + sqflite: + :path: Flutter/ephemeral/.symlinks/plugins/sqflite/macos + +SPEC CHECKSUMS: + FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 + FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a + package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce + path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852 + shared_preferences_foundation: 297b3ebca31b34ec92be11acd7fb0ba932c822ca + sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea + +PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7 + +COCOAPODS: 1.11.3 diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index 83583de..7b7cf22 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 54; objects = { /* Begin PBXAggregateTarget section */ @@ -21,6 +21,7 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 0B42AD366C3C28D47C2AEC89 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D6C623098EB8995A39C4964E /* Pods_Runner.framework */; }; 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 */; }; @@ -52,9 +53,10 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 2023D02FF0211CAEE16878C9 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; 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 /* unit2.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "unit2.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10ED2044A3C60003C045 /* unit2.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = unit2.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 = ""; }; @@ -66,8 +68,11 @@ 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 = ""; }; + 5BC0F6D90E7D68DE5330DF33 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 7FF1BC1DFB4A45BAC2293138 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + D6C623098EB8995A39C4964E /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -75,6 +80,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 0B42AD366C3C28D47C2AEC89 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -99,6 +105,7 @@ 33CEB47122A05771004F2AC0 /* Flutter */, 33CC10EE2044A3C60003C045 /* Products */, D73912EC22F37F3D000D13A0 /* Frameworks */, + 64DE41DBDE00B8F8F94614A7 /* Pods */, ); sourceTree = ""; }; @@ -145,9 +152,21 @@ path = Runner; sourceTree = ""; }; + 64DE41DBDE00B8F8F94614A7 /* Pods */ = { + isa = PBXGroup; + children = ( + 5BC0F6D90E7D68DE5330DF33 /* Pods-Runner.debug.xcconfig */, + 7FF1BC1DFB4A45BAC2293138 /* Pods-Runner.release.xcconfig */, + 2023D02FF0211CAEE16878C9 /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; D73912EC22F37F3D000D13A0 /* Frameworks */ = { isa = PBXGroup; children = ( + D6C623098EB8995A39C4964E /* Pods_Runner.framework */, ); name = Frameworks; sourceTree = ""; @@ -159,11 +178,13 @@ isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + C3E6A8BAF2259AABAF243D8C /* [CP] Check Pods Manifest.lock */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, + F31D2B687E220EE9896D3A25 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -235,6 +256,7 @@ /* Begin PBXShellScriptBuildPhase section */ 3399D490228B24CF009A79C7 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -270,6 +292,45 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; + C3E6A8BAF2259AABAF243D8C /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + F31D2B687E220EE9896D3A25 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -344,7 +405,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; @@ -423,7 +484,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -470,7 +531,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; diff --git a/macos/Runner.xcworkspace/contents.xcworkspacedata b/macos/Runner.xcworkspace/contents.xcworkspacedata index 1d526a1..21a3cc1 100644 --- a/macos/Runner.xcworkspace/contents.xcworkspacedata +++ b/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + From 0185005ef101e0c0702036432878bb22fd342839 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Tue, 7 Feb 2023 09:29:38 +0800 Subject: [PATCH 24/86] integrated work history API --- lib/model/profile/educational_background.dart | 145 ++++++++++++++++ lib/model/profile/profileInfomation.dart | 6 +- lib/model/profile/work_history.dart | 157 ++++++++++++++++++ .../profile/components/education_screen.dart | 71 ++++++++ .../components/work_history_screen.dart | 59 +++++++ lib/screens/profile/profile.dart | 14 +- lib/sevices/profile/profile_service.dart | 86 ++++++---- 7 files changed, 503 insertions(+), 35 deletions(-) create mode 100644 lib/model/profile/educational_background.dart create mode 100644 lib/model/profile/work_history.dart create mode 100644 lib/screens/profile/components/education_screen.dart create mode 100644 lib/screens/profile/components/work_history_screen.dart diff --git a/lib/model/profile/educational_background.dart b/lib/model/profile/educational_background.dart new file mode 100644 index 0000000..83c2086 --- /dev/null +++ b/lib/model/profile/educational_background.dart @@ -0,0 +1,145 @@ +// To parse this JSON data, do +// +// final educationalBackground = educationalBackgroundFromJson(jsonString); + +import 'dart:convert'; + +EducationalBackground educationalBackgroundFromJson(String str) => EducationalBackground.fromJson(json.decode(str)); + +String educationalBackgroundToJson(EducationalBackground data) => json.encode(data.toJson()); + +class EducationalBackground { + EducationalBackground({ + this.id, + this.honors, + this.education, + this.periodTo, + this.attachments, + this.periodFrom, + this.unitsEarned, + this.yearGraduated, + }); + + final int? id; + final List? honors; + final Education? education; + final String? periodTo; + final dynamic attachments; + final String? periodFrom; + final dynamic unitsEarned; + final String? yearGraduated; + + factory EducationalBackground.fromJson(Map json) => EducationalBackground( + id: json["id"], + honors: json["honors"] == null ? [] : List.from(json["honors"]!.map((x) => Honor.fromJson(x))), + education: json["education"] == null ? null : Education.fromJson(json["education"]), + periodTo: json["period_to"], + attachments: json["attachments"], + periodFrom: json["period_from"], + unitsEarned: json["units_earned"], + yearGraduated: json["year_graduated"], + ); + + Map toJson() => { + "id": id, + "honors": honors == null ? [] : List.from(honors!.map((x) => x.toJson())), + "education": education?.toJson(), + "period_to": periodTo, + "attachments": attachments, + "period_from": periodFrom, + "units_earned": unitsEarned, + "year_graduated": yearGraduated, + }; +} + +class Education { + Education({ + this.id, + this.level, + this.course, + this.school, + }); + + final int? id; + final String? level; + final Course? course; + final School? school; + + factory Education.fromJson(Map json) => Education( + id: json["id"], + level: json["level"], + course: json["course"] == null ? null : Course.fromJson(json["course"]), + school: json["school"] == null ? null : School.fromJson(json["school"]), + ); + + Map toJson() => { + "id": id, + "level": level, + "course": course?.toJson(), + "school": school?.toJson(), + }; +} + +class Course { + Course({ + this.id, + this.program, + }); + + final int? id; + final String? program; + + factory Course.fromJson(Map json) => Course( + id: json["id"], + program: json["program"], + ); + + Map toJson() => { + "id": id, + "program": program, + }; +} + +class School { + School({ + this.id, + this.name, + }); + + final int? id; + final String? name; + + factory School.fromJson(Map json) => School( + id: json["id"], + name: json["name"], + ); + + Map toJson() => { + "id": id, + "name": name, + }; +} + +class Honor { + Honor({ + this.id, + this.name, + this.academ, + }); + + final int? id; + final String? name; + final bool? academ; + + factory Honor.fromJson(Map json) => Honor( + id: json["id"], + name: json["name"], + academ: json["academ"], + ); + + Map toJson() => { + "id": id, + "name": name, + "academ": academ, + }; +} diff --git a/lib/model/profile/profileInfomation.dart b/lib/model/profile/profileInfomation.dart index 1e7fa36..d05987f 100644 --- a/lib/model/profile/profileInfomation.dart +++ b/lib/model/profile/profileInfomation.dart @@ -1,13 +1,17 @@ import 'package:unit2/model/profile/basic_info.dart'; import 'package:unit2/model/profile/basic_information/primary-information.dart'; +import 'package:unit2/model/profile/educational_background.dart'; import 'package:unit2/model/profile/eligibility.dart'; import 'package:unit2/model/profile/learning_development.dart'; import 'package:unit2/model/profile/references.dart'; +import 'package:unit2/model/profile/work_history.dart'; class ProfileInformation{ BasicInfo basicInfo; List eligibilities; List references; List learningsAndDevelopment; - ProfileInformation({required this.basicInfo,required this.eligibilities,required this.references, required this.learningsAndDevelopment}); + List educationalBackgrounds; + ListworkExperiences; + ProfileInformation({required this.workExperiences, required this.basicInfo,required this.eligibilities,required this.references, required this.learningsAndDevelopment,required this.educationalBackgrounds}); } \ No newline at end of file diff --git a/lib/model/profile/work_history.dart b/lib/model/profile/work_history.dart new file mode 100644 index 0000000..3fbd78d --- /dev/null +++ b/lib/model/profile/work_history.dart @@ -0,0 +1,157 @@ +// To parse this JSON data, do +// +// final workHistory = workHistoryFromJson(jsonString); + +import 'dart:convert'; + +WorkHistory workHistoryFromJson(String str) => WorkHistory.fromJson(json.decode(str)); + +String workHistoryToJson(WorkHistory data) => json.encode(data.toJson()); + +class WorkHistory { + WorkHistory({ + this.id, + this.agency, + this.sgStep, + this.toDate, + this.position, + this.fromDate, + // this.attachments, + this.salaryGrade, + this.monthlySalary, + this.appointmentStatus, + }); + + final int? id; + final Agency? agency; + final int? sgStep; + final DateTime? toDate; + final Position? position; + final DateTime? fromDate; + // final dynamic attachments; + final int? salaryGrade; + final int? monthlySalary; + final String? appointmentStatus; + + factory WorkHistory.fromJson(Map json) => WorkHistory( + id: json["id"], + agency: json["agency"] == null ? null : Agency.fromJson(json["agency"]), + sgStep: json["sg_step"], + toDate: json["to_date"] == null ? null : DateTime.parse(json["to_date"]), + position: json["position"] == null ? null : Position.fromJson(json["position"]), + fromDate: json["from_date"] == null ? null : DateTime.parse(json["from_date"]), + // attachments: json["attachments"], + salaryGrade: json["salary_grade"], + monthlySalary: json["monthly_salary"], + appointmentStatus: json["appointment_status"], + ); + + Map toJson() => { + "id": id, + "agency": agency?.toJson(), + "sg_step": sgStep, + "to_date": "${toDate!.year.toString().padLeft(4, '0')}-${toDate!.month.toString().padLeft(2, '0')}-${toDate!.day.toString().padLeft(2, '0')}", + "position": position?.toJson(), + "from_date": "${fromDate!.year.toString().padLeft(4, '0')}-${fromDate!.month.toString().padLeft(2, '0')}-${fromDate!.day.toString().padLeft(2, '0')}", + // "attachments": attachments, + "salary_grade": salaryGrade, + "monthly_salary": monthlySalary, + "appointment_status": appointmentStatus, + }; +} + +class Agency { + Agency({ + this.id, + this.name, + this.category, + this.privateEntity, + }); + + final int? id; + final String? name; + final Category? category; + final bool? privateEntity; + + factory Agency.fromJson(Map json) => Agency( + id: json["id"], + name: json["name"], + category: json["category"] == null ? null : Category.fromJson(json["category"]), + privateEntity: json["private_entity"], + ); + + Map toJson() => { + "id": id, + "name": name, + "category": category?.toJson(), + "private_entity": privateEntity, + }; +} + +class Category { + Category({ + this.id, + this.name, + this.industryClass, + }); + + final int? id; + final String? name; + final IndustryClass? industryClass; + + factory Category.fromJson(Map json) => Category( + id: json["id"], + name: json["name"], + industryClass: json["industry_class"] == null ? null : IndustryClass.fromJson(json["industry_class"]), + ); + + Map toJson() => { + "id": id, + "name": name, + "industry_class": industryClass?.toJson(), + }; +} + +class IndustryClass { + IndustryClass({ + this.id, + this.name, + this.description, + }); + + final int? id; + final String? name; + final dynamic description; + + factory IndustryClass.fromJson(Map json) => IndustryClass( + id: json["id"], + name: json["name"], + description: json["description"], + ); + + Map toJson() => { + "id": id, + "name": name, + "description": description, + }; +} + +class Position { + Position({ + this.id, + this.title, + }); + + final int? id; + final String? title; + + factory Position.fromJson(Map json) => Position( + id: json["id"], + title: json["title"], + ); + + Map toJson() => { + "id": id, + "title": title, + }; +} diff --git a/lib/screens/profile/components/education_screen.dart b/lib/screens/profile/components/education_screen.dart new file mode 100644 index 0000000..0cd6db4 --- /dev/null +++ b/lib/screens/profile/components/education_screen.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:unit2/model/profile/educational_background.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; + +class EducationScreen extends StatefulWidget { + final List educationBackgrounds; + const EducationScreen({super.key, required this.educationBackgrounds}); + + @override + State createState() => _EducationScreenState(); +} + +class _EducationScreenState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("Educational Background"), + centerTitle: true, + backgroundColor: primary, + ), + body: ListView.builder( + itemCount: widget.educationBackgrounds.length, + itemBuilder: (BuildContext context, int index) { + String level = widget.educationBackgrounds[index].education!.level!; + String periodFrom = widget.educationBackgrounds[index].periodFrom!; + String periodTo = widget.educationBackgrounds[index].periodTo!; + String? program = widget.educationBackgrounds[index].education!.course == null? null: widget.educationBackgrounds[index].education!.course!.program!; + List? honors = widget.educationBackgrounds[index].honors!.toList(); + String school = widget.educationBackgrounds[index].education!.school!.name!; + return Column( + children: [ + Container( + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: + const BorderRadius.all(Radius.circular(12))), + padding: + const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: Row( + children: [ + Expanded( + child: Column(children: [ + Row( + children: [ + Text(level), + Text("$periodFrom $periodTo"), + ], + ), + Text(program??=''), + Text(school), + Container( + child: honors.isNotEmpty? Column( + children: honors.map((Honor honor) => Text(honor.name!)).toList(), + ):const SizedBox() + ) + ]), + ), + IconButton( + onPressed: () {}, icon: const Icon(Icons.more_vert)) + ], + ), + ) + ], + ); + }), + ); + } +} diff --git a/lib/screens/profile/components/work_history_screen.dart b/lib/screens/profile/components/work_history_screen.dart new file mode 100644 index 0000000..13442bb --- /dev/null +++ b/lib/screens/profile/components/work_history_screen.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:intl/intl.dart'; +import 'package:unit2/model/profile/work_history.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; + +import '../../../utils/global.dart'; + +class WorkHistoryScreen extends StatefulWidget { + final ListworkExperiences; + const WorkHistoryScreen({super.key,required this.workExperiences}); + + @override + State createState() => _WorkHistoryScreenState(); +} + +class _WorkHistoryScreenState extends State { + DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text("Work History"),backgroundColor: primary,centerTitle: true,), + body: ListView.builder( + itemCount: widget.workExperiences.length, + itemBuilder: (BuildContext context, int index){ + String position = widget.workExperiences[index].position!.title!; + String agency = widget.workExperiences[index].agency!.name!; + String from = dteFormat2.format(widget.workExperiences[index].fromDate!); + String? to = widget.workExperiences[index].toDate == null? "Present" : dteFormat2.format(widget.workExperiences[index].toDate!); + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: screenWidth, + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: const BorderRadius.all(Radius.circular(12))), + padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), + child: Row(children: [ + Expanded(child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(position), + Text(agency), + Text("$from to $to"), + ],)), + IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)) + ]), + ), + const SizedBox(height: 5,), + ], + ); + }), + ); + } +} \ No newline at end of file diff --git a/lib/screens/profile/profile.dart b/lib/screens/profile/profile.dart index 3cd5c79..8cea220 100644 --- a/lib/screens/profile/profile.dart +++ b/lib/screens/profile/profile.dart @@ -12,10 +12,12 @@ import 'package:unit2/screens/profile/components/basic_information/citizenship_s import 'package:unit2/screens/profile/components/basic_information/contact_information.dart'; import 'package:unit2/screens/profile/components/basic_information/identification_information.dart'; import 'package:unit2/screens/profile/components/basic_information/primary_information.dart'; +import 'package:unit2/screens/profile/components/education_screen.dart'; import 'package:unit2/screens/profile/components/eligibility.dart'; import 'package:unit2/screens/profile/components/learning_and_development_screen.dart'; import 'package:unit2/screens/profile/components/loading_screen.dart'; import 'package:unit2/screens/profile/components/references_screen.dart'; +import 'package:unit2/screens/profile/components/work_history_screen.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import '../../bloc/user/user_bloc.dart'; import 'components/main_menu.dart'; @@ -116,7 +118,11 @@ class _ProfileInfoState extends State { MainMenu( icon: FontAwesome5.graduation_cap, title: "Education", - onTap: (){}, + onTap: (){ + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + return EducationScreen(educationBackgrounds: state.profileInformation.educationalBackgrounds); + })); + }, ), const Divider(), MainMenu( @@ -132,7 +138,11 @@ class _ProfileInfoState extends State { MainMenu( icon: FontAwesome5.shopping_bag, title: "Work History", - onTap: (){}, + onTap: (){ + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + return WorkHistoryScreen(workExperiences: state.profileInformation.workExperiences); + })); + }, ), const Divider(), MainMenu( diff --git a/lib/sevices/profile/profile_service.dart b/lib/sevices/profile/profile_service.dart index 6f25add..06658f9 100644 --- a/lib/sevices/profile/profile_service.dart +++ b/lib/sevices/profile/profile_service.dart @@ -5,10 +5,12 @@ import 'package:unit2/model/profile/basic_info.dart'; import 'package:unit2/model/profile/basic_information/citizenship.dart'; import 'package:unit2/model/profile/basic_information/contact_information.dart'; import 'package:unit2/model/profile/basic_information/identification_information.dart'; +import 'package:unit2/model/profile/educational_background.dart'; import 'package:unit2/model/profile/eligibility.dart'; import 'package:unit2/model/profile/learning_development.dart'; import 'package:unit2/model/profile/profileInfomation.dart'; import 'package:unit2/model/profile/references.dart'; +import 'package:unit2/model/profile/work_history.dart'; import 'package:unit2/utils/request.dart'; import 'package:unit2/utils/urls.dart'; @@ -21,16 +23,16 @@ class ProfileService { Future getProfile(String token, int id) async { String url = Url.instance.profileInformation(); String path = url + id.toString(); - ProfileInformation? profileInformation0; - List references = []; - ContactInfo contactInfo; - Identification identification; - List identificationInformation =[]; - List contactInformation = []; + ProfileInformation? profileInformation0; PrimaryInformation primaryInformation; + List workExperiences =[]; + List references = []; + List identificationInformation = []; + List contactInformation = []; List eligibilities = []; List citizenships = []; - List learningsDevelopments = []; + List learningsDevelopments = []; + List educationalBackgrounds = []; Map headers = { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': "Token $token" @@ -39,55 +41,75 @@ class ProfileService { http.Response response = await Request.instance .getRequest(path: path, param: {}, headers: headers); if (response.statusCode == 200) { - Map data = jsonDecode(response.body); + Map data = jsonDecode(response.body); // get primary information primaryInformation = PrimaryInformation.fromJson( data['data']['basic_information']['primary_information']); // get all contacts data['data']['basic_information']['contact_information'] .forEach((var contact) { - contactInfo = ContactInfo.fromJson(contact['contact_info']); + ContactInfo contactInfo = ContactInfo.fromJson(contact['contact_info']); contactInformation.add(contactInfo); }); // get all identifications - data['data']['basic_information']['identification_records'].forEach((var identity){ - identification = Identification.fromJson(identity); + data['data']['basic_information']['identification_records'] + .forEach((var identity) { + Identification identification = Identification.fromJson(identity); identificationInformation.add(identification); }); //get all eligibilities - data['data']['eligibilities'].forEach((var cert){ + data['data']['eligibilities'].forEach((var cert) { EligibityCert eligibility = EligibityCert.fromJson(cert); eligibilities.add(eligibility); }); // get all citizenships - if(data['data']['citizenship']!= null){ - data['data']['citizenships'].forEach((var citizenship){ + if (data['data']['citizenship'] != null) { + data['data']['citizenships'].forEach((var citizenship) { Citizenship person = Citizenship.fromJson(citizenship); citizenships.add(person); - }); + }); } // get all references; - data['data']['personal_references'].forEach((var person){ - PersonalReference reference = PersonalReference.fromJson(person); - references.add(reference); - }); + data['data']['personal_references'].forEach((var person) { + PersonalReference reference = PersonalReference.fromJson(person); + references.add(reference); + }); - //get all learning and developments - - data['data']['learning_development'].forEach((var training){ - LearningDevelopement learnings = LearningDevelopement.fromJson(training); - learningsDevelopments.add(learnings); - }); + //get all learning and developments + data['data']['learning_development'].forEach((var training) { + LearningDevelopement learnings = + LearningDevelopement.fromJson(training); + learningsDevelopments.add(learnings); + }); + //get all educational background + data['data']['education_background'].forEach((var education) { + EducationalBackground educationalBackground = + EducationalBackground.fromJson(education); + educationalBackgrounds.add(educationalBackground); + }); - BasicInfo basicInfo = BasicInfo(contactInformation: contactInformation, - primaryInformation: primaryInformation, - identifications: identificationInformation, - citizenships: citizenships - ); - ProfileInformation profileInformation = ProfileInformation(basicInfo: basicInfo,eligibilities: eligibilities,references: references!,learningsAndDevelopment: learningsDevelopments); - profileInformation0 = profileInformation; + // get all work history + + data['data']['work_experiences'].forEach((var work){ + WorkHistory experience = WorkHistory.fromJson(work); + workExperiences.add(experience); + }); + + BasicInfo basicInfo = BasicInfo( + contactInformation: contactInformation, + primaryInformation: primaryInformation, + identifications: identificationInformation, + citizenships: citizenships); + ProfileInformation profileInformation = ProfileInformation( + workExperiences: workExperiences, + basicInfo: basicInfo, + eligibilities: eligibilities, + references: references, + learningsAndDevelopment: learningsDevelopments, + educationalBackgrounds: educationalBackgrounds); + profileInformation0 = profileInformation; } // }catch(e){ // throw(e.toString()); From 4e9b66051c1ab38159adc6589af11dc2560b103e Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Tue, 7 Feb 2023 10:37:15 +0800 Subject: [PATCH 25/86] integrated voluntary work api --- lib/model/profile/profileInfomation.dart | 4 +- lib/model/profile/voluntary_works.dart | 313 +++++++++++++++++++++++ lib/screens/profile/profile.dart | 7 +- lib/screens/profile/voluntary_works.dart | 64 +++++ lib/sevices/profile/profile_service.dart | 13 +- 5 files changed, 398 insertions(+), 3 deletions(-) create mode 100644 lib/model/profile/voluntary_works.dart create mode 100644 lib/screens/profile/voluntary_works.dart diff --git a/lib/model/profile/profileInfomation.dart b/lib/model/profile/profileInfomation.dart index d05987f..61e0155 100644 --- a/lib/model/profile/profileInfomation.dart +++ b/lib/model/profile/profileInfomation.dart @@ -4,6 +4,7 @@ import 'package:unit2/model/profile/educational_background.dart'; import 'package:unit2/model/profile/eligibility.dart'; import 'package:unit2/model/profile/learning_development.dart'; import 'package:unit2/model/profile/references.dart'; +import 'package:unit2/model/profile/voluntary_works.dart'; import 'package:unit2/model/profile/work_history.dart'; class ProfileInformation{ @@ -13,5 +14,6 @@ class ProfileInformation{ List learningsAndDevelopment; List educationalBackgrounds; ListworkExperiences; - ProfileInformation({required this.workExperiences, required this.basicInfo,required this.eligibilities,required this.references, required this.learningsAndDevelopment,required this.educationalBackgrounds}); + List voluntaryWorks; + ProfileInformation({required this.voluntaryWorks, required this.workExperiences, required this.basicInfo,required this.eligibilities,required this.references, required this.learningsAndDevelopment,required this.educationalBackgrounds}); } \ No newline at end of file diff --git a/lib/model/profile/voluntary_works.dart b/lib/model/profile/voluntary_works.dart new file mode 100644 index 0000000..4938116 --- /dev/null +++ b/lib/model/profile/voluntary_works.dart @@ -0,0 +1,313 @@ +// To parse this JSON data, do +// +// final voluntaryWork = voluntaryWorkFromJson(jsonString); + +import 'dart:convert'; + +VoluntaryWork voluntaryWorkFromJson(String str) => VoluntaryWork.fromJson(json.decode(str)); + +String voluntaryWorkToJson(VoluntaryWork data) => json.encode(data.toJson()); + +class VoluntaryWork { + VoluntaryWork({ + this.agency, + this.address, + this.toDate, + this.position, + this.fromDate, + this.totalHours, + }); + + final Agency? agency; + final Address? address; + final DateTime? toDate; + final Position? position; + final DateTime? fromDate; + final int? totalHours; + + factory VoluntaryWork.fromJson(Map json) => VoluntaryWork( + agency: json["agency"] == null ? null : Agency.fromJson(json["agency"]), + address: json["address"] == null ? null : Address.fromJson(json["address"]), + toDate: json["to_date"], + position: json["position"] == null ? null : Position.fromJson(json["position"]), + fromDate: json["from_date"] == null ? null : DateTime.parse(json["from_date"]), + totalHours: json["total_hours"], + ); + + Map toJson() => { + "agency": agency?.toJson(), + "address": address?.toJson(), + "to_date": toDate, + "position": position?.toJson(), + "from_date": "${fromDate!.year.toString().padLeft(4, '0')}-${fromDate!.month.toString().padLeft(2, '0')}-${fromDate!.day.toString().padLeft(2, '0')}", + "total_hours": totalHours, + }; +} + +class Address { + Address({ + this.id, + this.addressClass, + this.country, + this.barangay, + this.addressCategory, + this.cityMunicipality, + }); + + final int? id; + final dynamic addressClass; + final Country? country; + final dynamic barangay; + final AddressCategory? addressCategory; + final CityMunicipality? cityMunicipality; + + factory Address.fromJson(Map json) => Address( + id: json["id"], + addressClass: json["class"], + country: json["country"] == null ? null : Country.fromJson(json["country"]), + barangay: json["barangay"], + addressCategory: json["address_category"] == null ? null : AddressCategory.fromJson(json["address_category"]), + cityMunicipality: json["city_municipality"] == null ? null : CityMunicipality.fromJson(json["city_municipality"]), + ); + + Map toJson() => { + "id": id, + "class": addressClass, + "country": country?.toJson(), + "barangay": barangay, + "address_category": addressCategory?.toJson(), + "city_municipality": cityMunicipality?.toJson(), + }; +} + +class AddressCategory { + AddressCategory({ + this.id, + this.name, + this.type, + }); + + final int? id; + final String? name; + final String? type; + + factory AddressCategory.fromJson(Map json) => AddressCategory( + id: json["id"], + name: json["name"], + type: json["type"], + ); + + Map toJson() => { + "id": id, + "name": name, + "type": type, + }; +} + +class CityMunicipality { + CityMunicipality({ + this.code, + this.zipcode, + this.province, + this.psgcCode, + this.description, + }); + + final String? code; + final String? zipcode; + final Province? province; + final String? psgcCode; + final String? description; + + factory CityMunicipality.fromJson(Map json) => CityMunicipality( + code: json["code"], + zipcode: json["zipcode"], + province: json["province"] == null ? null : Province.fromJson(json["province"]), + psgcCode: json["psgc_code"], + description: json["description"], + ); + + Map toJson() => { + "code": code, + "zipcode": zipcode, + "province": province?.toJson(), + "psgc_code": psgcCode, + "description": description, + }; +} + +class Province { + Province({ + this.code, + this.region, + this.psgcCode, + this.shortname, + this.description, + }); + + final String? code; + final Region? region; + final String? psgcCode; + final String? shortname; + final String? description; + + factory Province.fromJson(Map json) => Province( + code: json["code"], + region: json["region"] == null ? null : Region.fromJson(json["region"]), + psgcCode: json["psgc_code"], + shortname: json["shortname"], + description: json["description"], + ); + + Map toJson() => { + "code": code, + "region": region?.toJson(), + "psgc_code": psgcCode, + "shortname": shortname, + "description": description, + }; +} + +class Region { + Region({ + this.code, + this.psgcCode, + this.description, + }); + + final int? code; + final String? psgcCode; + final String? description; + + factory Region.fromJson(Map json) => Region( + code: json["code"], + psgcCode: json["psgc_code"], + description: json["description"], + ); + + Map toJson() => { + "code": code, + "psgc_code": psgcCode, + "description": description, + }; +} + +class Country { + Country({ + this.id, + this.code, + this.name, + }); + + final int? id; + final String? code; + final String? name; + + factory Country.fromJson(Map json) => Country( + id: json["id"], + code: json["code"], + name: json["name"], + ); + + Map toJson() => { + "id": id, + "code": code, + "name": name, + }; +} + +class Agency { + Agency({ + this.id, + this.name, + this.category, + this.privateEntity, + }); + + final int? id; + final String? name; + final Category? category; + final bool? privateEntity; + + factory Agency.fromJson(Map json) => Agency( + id: json["id"], + name: json["name"], + category: json["category"] == null ? null : Category.fromJson(json["category"]), + privateEntity: json["private_entity"], + ); + + Map toJson() => { + "id": id, + "name": name, + "category": category?.toJson(), + "private_entity": privateEntity, + }; +} + +class Category { + Category({ + this.id, + this.name, + this.industryClass, + }); + + final int? id; + final String? name; + final IndustryClass? industryClass; + + factory Category.fromJson(Map json) => Category( + id: json["id"], + name: json["name"], + industryClass: json["industry_class"] == null ? null : IndustryClass.fromJson(json["industry_class"]), + ); + + Map toJson() => { + "id": id, + "name": name, + "industry_class": industryClass?.toJson(), + }; +} + +class IndustryClass { + IndustryClass({ + this.id, + this.name, + this.description, + }); + + final int? id; + final String? name; + final dynamic description; + + factory IndustryClass.fromJson(Map json) => IndustryClass( + id: json["id"], + name: json["name"], + description: json["description"], + ); + + Map toJson() => { + "id": id, + "name": name, + "description": description, + }; +} + +class Position { + Position({ + this.id, + this.title, + }); + + final int? id; + final String? title; + + factory Position.fromJson(Map json) => Position( + id: json["id"], + title: json["title"], + ); + + Map toJson() => { + "id": id, + "title": title, + }; +} diff --git a/lib/screens/profile/profile.dart b/lib/screens/profile/profile.dart index 8cea220..32c9216 100644 --- a/lib/screens/profile/profile.dart +++ b/lib/screens/profile/profile.dart @@ -18,6 +18,7 @@ import 'package:unit2/screens/profile/components/learning_and_development_screen import 'package:unit2/screens/profile/components/loading_screen.dart'; import 'package:unit2/screens/profile/components/references_screen.dart'; import 'package:unit2/screens/profile/components/work_history_screen.dart'; +import 'package:unit2/screens/profile/voluntary_works.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import '../../bloc/user/user_bloc.dart'; import 'components/main_menu.dart'; @@ -148,7 +149,11 @@ class _ProfileInfoState extends State { MainMenu( icon: FontAwesome5.walking, title: "Voluntary Work & Civic Services", - onTap: (){}, + onTap: (){ + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + return VolunataryWorkScreen(voluntaryWorks: state.profileInformation.voluntaryWorks); + })); + }, ), const Divider(), MainMenu( diff --git a/lib/screens/profile/voluntary_works.dart b/lib/screens/profile/voluntary_works.dart new file mode 100644 index 0000000..eb06273 --- /dev/null +++ b/lib/screens/profile/voluntary_works.dart @@ -0,0 +1,64 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:intl/intl.dart'; +import 'package:unit2/model/profile/voluntary_works.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; + +class VolunataryWorkScreen extends StatefulWidget { + final List voluntaryWorks; + const VolunataryWorkScreen({super.key, required this.voluntaryWorks}); + + @override + State createState() => _VolunataryWorkScreenState(); +} + +class _VolunataryWorkScreenState extends State { + DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text("Volunatary Work & Civic Services"),backgroundColor: primary,), + body: ListView.builder( + itemCount:widget.voluntaryWorks.length , + itemBuilder: (BuildContext context, int index){ + String position = widget.voluntaryWorks[index].position!.title!; + String agency = widget.voluntaryWorks[index].agency!.name!; + String from = dteFormat2.format(widget.voluntaryWorks[index].fromDate!); + String hours = widget.voluntaryWorks[index].totalHours.toString(); + String? to = widget.voluntaryWorks[index].toDate == null? "Present" : dteFormat2.format(widget.voluntaryWorks[index].toDate!); + return Column( + children: [ + Container( + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: const BorderRadius.all(Radius.circular(12))), + padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(position), + Text(agency), + Text("$from to $to"), + Text("Worked/Involved for: $hours hours"), + ]), + ), + IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)) + ], + ), + + + ), + const SizedBox(height: 5,), + + ], + ); + + }), + ); + } +} \ No newline at end of file diff --git a/lib/sevices/profile/profile_service.dart b/lib/sevices/profile/profile_service.dart index 06658f9..50d098a 100644 --- a/lib/sevices/profile/profile_service.dart +++ b/lib/sevices/profile/profile_service.dart @@ -10,6 +10,7 @@ import 'package:unit2/model/profile/eligibility.dart'; import 'package:unit2/model/profile/learning_development.dart'; import 'package:unit2/model/profile/profileInfomation.dart'; import 'package:unit2/model/profile/references.dart'; +import 'package:unit2/model/profile/voluntary_works.dart'; import 'package:unit2/model/profile/work_history.dart'; import 'package:unit2/utils/request.dart'; import 'package:unit2/utils/urls.dart'; @@ -33,6 +34,7 @@ class ProfileService { List citizenships = []; List learningsDevelopments = []; List educationalBackgrounds = []; + List voluntaryWorks =[]; Map headers = { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': "Token $token" @@ -97,6 +99,13 @@ class ProfileService { workExperiences.add(experience); }); + // get all voluntary works + data['data']['voluntary_works'].forEach((var work){ + VoluntaryWork vwork = VoluntaryWork.fromJson(work); + voluntaryWorks.add(vwork); + }); + + BasicInfo basicInfo = BasicInfo( contactInformation: contactInformation, primaryInformation: primaryInformation, @@ -108,7 +117,9 @@ class ProfileService { eligibilities: eligibilities, references: references, learningsAndDevelopment: learningsDevelopments, - educationalBackgrounds: educationalBackgrounds); + educationalBackgrounds: educationalBackgrounds, + voluntaryWorks: voluntaryWorks + ); profileInformation0 = profileInformation; } // }catch(e){ From f74ad836670e5601791c2cc95528d4cb3523bfcc Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Tue, 7 Feb 2023 14:17:49 +0800 Subject: [PATCH 26/86] integrated organization membership --- lib/model/profile/other_info.dart | 8 + .../organization_memberships.dart | 101 +++++++ .../other_information/skills_and_hobbies.dart | 29 ++ lib/model/profile/profileInfomation.dart | 4 +- .../profile/components/loading_screen.dart | 273 +++++++++--------- .../other_information/org_membership.dart | 63 ++++ .../skills_and_hobbies_screen.dart | 49 ++++ lib/screens/profile/profile.dart | 14 +- lib/sevices/profile/profile_service.dart | 20 ++ 9 files changed, 427 insertions(+), 134 deletions(-) create mode 100644 lib/model/profile/other_info.dart create mode 100644 lib/model/profile/other_information/organization_memberships.dart create mode 100644 lib/model/profile/other_information/skills_and_hobbies.dart create mode 100644 lib/screens/profile/components/other_information/org_membership.dart create mode 100644 lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart diff --git a/lib/model/profile/other_info.dart b/lib/model/profile/other_info.dart new file mode 100644 index 0000000..fe43df3 --- /dev/null +++ b/lib/model/profile/other_info.dart @@ -0,0 +1,8 @@ +import 'package:unit2/model/profile/other_information/organization_memberships.dart'; +import 'package:unit2/model/profile/other_information/skills_and_hobbies.dart'; + +class OtherInformation{ + List skillsAndHobbies; + ListorgMemberships; + OtherInformation({required this.skillsAndHobbies, required this.orgMemberships}); +} \ No newline at end of file diff --git a/lib/model/profile/other_information/organization_memberships.dart b/lib/model/profile/other_information/organization_memberships.dart new file mode 100644 index 0000000..7776380 --- /dev/null +++ b/lib/model/profile/other_information/organization_memberships.dart @@ -0,0 +1,101 @@ +// To parse this JSON data, do +// +// final organizationMembership = organizationMembershipFromJson(jsonString); + +import 'dart:convert'; + +OrganizationMembership organizationMembershipFromJson(String str) => OrganizationMembership.fromJson(json.decode(str)); + +String organizationMembershipToJson(OrganizationMembership data) => json.encode(data.toJson()); + +class OrganizationMembership { + OrganizationMembership({ + this.agency, + }); + + final Agency? agency; + + factory OrganizationMembership.fromJson(Map json) => OrganizationMembership( + agency: json["agency"] == null ? null : Agency.fromJson(json["agency"]), + ); + + Map toJson() => { + "agency": agency?.toJson(), + }; +} + +class Agency { + Agency({ + this.id, + this.name, + this.category, + this.privateEntity, + }); + + final int? id; + final String? name; + final Category? category; + final bool? privateEntity; + + factory Agency.fromJson(Map json) => Agency( + id: json["id"], + name: json["name"], + category: json["category"] == null ? null : Category.fromJson(json["category"]), + privateEntity: json["private_entity"], + ); + + Map toJson() => { + "id": id, + "name": name, + "category": category?.toJson(), + "private_entity": privateEntity, + }; +} + +class Category { + Category({ + this.id, + this.name, + this.industryClass, + }); + + final int? id; + final String? name; + final IndustryClass? industryClass; + + factory Category.fromJson(Map json) => Category( + id: json["id"], + name: json["name"], + industryClass: json["industry_class"] == null ? null : IndustryClass.fromJson(json["industry_class"]), + ); + + Map toJson() => { + "id": id, + "name": name, + "industry_class": industryClass?.toJson(), + }; +} + +class IndustryClass { + IndustryClass({ + this.id, + this.name, + this.description, + }); + + final int? id; + final String? name; + final dynamic description; + + factory IndustryClass.fromJson(Map json) => IndustryClass( + id: json["id"], + name: json["name"], + description: json["description"], + ); + + Map toJson() => { + "id": id, + "name": name, + "description": description, + }; +} diff --git a/lib/model/profile/other_information/skills_and_hobbies.dart b/lib/model/profile/other_information/skills_and_hobbies.dart new file mode 100644 index 0000000..4d7cff3 --- /dev/null +++ b/lib/model/profile/other_information/skills_and_hobbies.dart @@ -0,0 +1,29 @@ +// To parse this JSON data, do +// +// final skillsHobbies = skillsHobbiesFromJson(jsonString); + +import 'dart:convert'; + +SkillsHobbies skillsHobbiesFromJson(String str) => SkillsHobbies.fromJson(json.decode(str)); + +String skillsHobbiesToJson(SkillsHobbies data) => json.encode(data.toJson()); + +class SkillsHobbies { + SkillsHobbies({ + this.id, + this.name, + }); + + final int? id; + final String? name; + + factory SkillsHobbies.fromJson(Map json) => SkillsHobbies( + id: json["id"], + name: json["name"], + ); + + Map toJson() => { + "id": id, + "name": name, + }; +} diff --git a/lib/model/profile/profileInfomation.dart b/lib/model/profile/profileInfomation.dart index 61e0155..63bd116 100644 --- a/lib/model/profile/profileInfomation.dart +++ b/lib/model/profile/profileInfomation.dart @@ -3,17 +3,19 @@ import 'package:unit2/model/profile/basic_information/primary-information.dart'; import 'package:unit2/model/profile/educational_background.dart'; import 'package:unit2/model/profile/eligibility.dart'; import 'package:unit2/model/profile/learning_development.dart'; +import 'package:unit2/model/profile/other_info.dart'; import 'package:unit2/model/profile/references.dart'; import 'package:unit2/model/profile/voluntary_works.dart'; import 'package:unit2/model/profile/work_history.dart'; class ProfileInformation{ BasicInfo basicInfo; + OtherInformation otherInformation; List eligibilities; List references; List learningsAndDevelopment; List educationalBackgrounds; ListworkExperiences; List voluntaryWorks; - ProfileInformation({required this.voluntaryWorks, required this.workExperiences, required this.basicInfo,required this.eligibilities,required this.references, required this.learningsAndDevelopment,required this.educationalBackgrounds}); + ProfileInformation({required this.otherInformation, required this.voluntaryWorks, required this.workExperiences, required this.basicInfo,required this.eligibilities,required this.references, required this.learningsAndDevelopment,required this.educationalBackgrounds}); } \ No newline at end of file diff --git a/lib/screens/profile/components/loading_screen.dart b/lib/screens/profile/components/loading_screen.dart index eda754a..d9b11f3 100644 --- a/lib/screens/profile/components/loading_screen.dart +++ b/lib/screens/profile/components/loading_screen.dart @@ -7,6 +7,7 @@ import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:fluttericon/modern_pictograms_icons.dart'; import 'package:unit2/screens/profile/components/main_menu.dart'; import 'package:unit2/screens/profile/components/submenu.dart'; +import 'package:unit2/utils/global.dart'; import '../../../theme-data.dart/colors.dart'; @@ -15,139 +16,149 @@ class LoadingScreen extends StatelessWidget { @override Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.symmetric( - vertical: 12, horizontal: 12), - child: ListView( - children: [ - const Text( - "View and Update your Profile Information"), - ExpandableGroup( - collapsedIcon: - const Icon(Icons.keyboard_arrow_down), - expandedIcon: - const Icon(Icons.keyboard_arrow_up), - header: const ListTile( - leading: Icon( - Elusive.address_book, - color: primary, - ), - title: Text( - "Basic Information", - style: - TextStyle(fontWeight: FontWeight.bold), - ), + return Stack( + children: [ + + Container( + padding: const EdgeInsets.symmetric( + vertical: 12, horizontal: 12), + child: ListView( + children: [ + const Text( + "View and Update your Profile Information"), + ExpandableGroup( + collapsedIcon: + const Icon(Icons.keyboard_arrow_down), + expandedIcon: + const Icon(Icons.keyboard_arrow_up), + header: const ListTile( + leading: Icon( + Elusive.address_book, + color: primary, + ), + title: Text( + "Basic Information", + style: + TextStyle(fontWeight: FontWeight.bold), + ), + ), + items: [ + subMenu(Icons.person, "Primary",(){}), + subMenu(Icons.home, "Home Addresses",(){}), + subMenu( + Icons.contact_mail, "Identifications",(){}), + subMenu(Icons.contact_phone, "Contact Info",(){}), + subMenu(Icons.flag, "Citizenships",(){}), + ]), + const Divider(), + MainMenu( + icon: Elusive.group, + title: "Family", + onTap: () { + + }, ), - items: [ - subMenu(Icons.person, "Primary",(){}), - subMenu(Icons.home, "Home Addresses",(){}), - subMenu( - Icons.contact_mail, "Identifications",(){}), - subMenu(Icons.contact_phone, "Contact Info",(){}), - subMenu(Icons.flag, "Citizenships",(){}), - ]), - const Divider(), - MainMenu( - icon: Elusive.group, - title: "Family", - onTap: () { - - }, - ), - const Divider(), - MainMenu( - icon: FontAwesome5.graduation_cap, - title: "Education", - onTap: () { - - }, - ), - const Divider(), - MainMenu( - icon: Icons.stars, - title: "Eligibility", - onTap: () { - - }, - ), - const Divider(), - MainMenu( - icon: FontAwesome5.shopping_bag, - title: "Work History", - onTap: () { - - }, - ), - const Divider(), - MainMenu( - icon: FontAwesome5.walking, - title: "Voluntary Work & Civic Services", - onTap: () { - - }, - ), - const Divider(), - MainMenu( - icon: Elusive.lightbulb, - title: "Learning & Development", - onTap: () { - - }, - ), - const Divider(), - MainMenu( - icon: Brandico.codepen, - title: "Personal References", - onTap: () { - - }, - ), - ExpandableGroup( - collapsedIcon: - const Icon(Icons.keyboard_arrow_down), - expandedIcon: - const Icon(Icons.keyboard_arrow_up), - header: const ListTile( - leading: Icon( - Icons.info, - color: primary, - ), - title: Text( - "Other Information", - style: - TextStyle(fontWeight: FontWeight.bold), - ), + const Divider(), + MainMenu( + icon: FontAwesome5.graduation_cap, + title: "Education", + onTap: () { + + }, ), - items: [ - subMenu( - Icons.fitness_center, "Skills & Hobbies",(){}), - subMenu(FontAwesome5.certificate, - "Organization Memberships",(){}), - subMenu(Entypo.doc_text, - "Non-Academic Recognitions",(){}), - ]), - ExpandableGroup( - collapsedIcon: - const Icon(Icons.keyboard_arrow_down), - expandedIcon: - const Icon(Icons.keyboard_arrow_up), - header: const ListTile( - leading: Icon( - FontAwesome5.laptop_house, - color: primary, - ), - title: Text( - "Assets", - style: - TextStyle(fontWeight: FontWeight.bold), - ), + const Divider(), + MainMenu( + icon: Icons.stars, + title: "Eligibility", + onTap: () { + + }, ), - items: [ - subMenu(ModernPictograms.home, - "Real Property Tax",(){}), - ]), - ], - ), - ); + const Divider(), + MainMenu( + icon: FontAwesome5.shopping_bag, + title: "Work History", + onTap: () { + + }, + ), + const Divider(), + MainMenu( + icon: FontAwesome5.walking, + title: "Voluntary Work & Civic Services", + onTap: () { + + }, + ), + const Divider(), + MainMenu( + icon: Elusive.lightbulb, + title: "Learning & Development", + onTap: () { + + }, + ), + const Divider(), + MainMenu( + icon: Brandico.codepen, + title: "Personal References", + onTap: () { + + }, + ), + ExpandableGroup( + collapsedIcon: + const Icon(Icons.keyboard_arrow_down), + expandedIcon: + const Icon(Icons.keyboard_arrow_up), + header: const ListTile( + leading: Icon( + Icons.info, + color: primary, + ), + title: Text( + "Other Information", + style: + TextStyle(fontWeight: FontWeight.bold), + ), + ), + items: [ + subMenu( + Icons.fitness_center, "Skills & Hobbies",(){}), + subMenu(FontAwesome5.certificate, + "Organization Memberships",(){}), + subMenu(Entypo.doc_text, + "Non-Academic Recognitions",(){}), + ]), + ExpandableGroup( + collapsedIcon: + const Icon(Icons.keyboard_arrow_down), + expandedIcon: + const Icon(Icons.keyboard_arrow_up), + header: const ListTile( + leading: Icon( + FontAwesome5.laptop_house, + color: primary, + ), + title: Text( + "Assets", + style: + TextStyle(fontWeight: FontWeight.bold), + ), + ), + items: [ + subMenu(ModernPictograms.home, + "Real Property Tax",(){}), + ]), + ], + ), + ), + Container( + width: screenWidth, + height: screenHeight, + color: Colors.white70, + ), + ], + ); } } \ No newline at end of file diff --git a/lib/screens/profile/components/other_information/org_membership.dart b/lib/screens/profile/components/other_information/org_membership.dart new file mode 100644 index 0000000..a110286 --- /dev/null +++ b/lib/screens/profile/components/other_information/org_membership.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:unit2/model/profile/other_information/organization_memberships.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; + +import '../../../../utils/global.dart'; + +class OrgMembershipsScreen extends StatefulWidget { + final List orgMemberships; + const OrgMembershipsScreen({super.key, required this.orgMemberships}); + + @override + State createState() => _OrgMembershipsScreenState(); +} + +class _OrgMembershipsScreenState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("Organization Memberships"), + backgroundColor: primary, + centerTitle: true, + ), + body: ListView.builder( + itemCount: widget.orgMemberships.length, + itemBuilder: (BuildContext context, int index) { + String entity = + widget.orgMemberships[index].agency!.privateEntity == false + ? "Government" + : "Private"; + String agencyName = widget.orgMemberships[index].agency!.name!; + return Column( + children: [ + Container( + width: screenWidth, + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: + const BorderRadius.all(Radius.circular(12))), + padding: + const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: Row(children: [ + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(entity), + Text(agencyName), + ], + )), + IconButton( + onPressed: () {}, icon: const Icon(Icons.more_vert)) + ]), + ) + ], + ); + }), + ); + } +} diff --git a/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart b/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart new file mode 100644 index 0000000..332a02f --- /dev/null +++ b/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:unit2/model/profile/other_information/skills_and_hobbies.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; + +class SkillHobbiesScreen extends StatefulWidget { + final ListskillsHobbies; + const SkillHobbiesScreen({super.key,required this.skillsHobbies}); + + @override + State createState() => _SkillHobbiesScreenState(); +} + +class _SkillHobbiesScreenState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text("Skills and Hobbies"), + backgroundColor: primary, + centerTitle: true, + + ), + body: Wrap( + spacing: 5, + runSpacing: 5, + clipBehavior: Clip.none, + verticalDirection: VerticalDirection.down, + children:widget.skillsHobbies.map((SkillsHobbies sh){ + return Badge( + padding: const EdgeInsets.all(8), + alignment: AlignmentDirectional.topStart, + backgroundColor: Colors.grey.shade300, + child: Row( + children: [ + Text(sh.name!), + IconButton( + onPressed: () {}, + icon: const Icon(Icons.close)), + ], + ), + + ); + }).toList() + + ), + ); + } +} \ No newline at end of file diff --git a/lib/screens/profile/profile.dart b/lib/screens/profile/profile.dart index 32c9216..53c4fed 100644 --- a/lib/screens/profile/profile.dart +++ b/lib/screens/profile/profile.dart @@ -16,6 +16,8 @@ import 'package:unit2/screens/profile/components/education_screen.dart'; import 'package:unit2/screens/profile/components/eligibility.dart'; import 'package:unit2/screens/profile/components/learning_and_development_screen.dart'; import 'package:unit2/screens/profile/components/loading_screen.dart'; +import 'package:unit2/screens/profile/components/other_information/org_membership.dart'; +import 'package:unit2/screens/profile/components/other_information/skills_and_hobbies_screen.dart'; import 'package:unit2/screens/profile/components/references_screen.dart'; import 'package:unit2/screens/profile/components/work_history_screen.dart'; import 'package:unit2/screens/profile/voluntary_works.dart'; @@ -193,9 +195,17 @@ class _ProfileInfoState extends State { ), items: [ subMenu(Icons.fitness_center, - "Skills & Hobbies",(){}), + "Skills & Hobbies",(){ + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + return SkillHobbiesScreen(skillsHobbies: state.profileInformation.otherInformation.skillsAndHobbies); + })); + }), subMenu(FontAwesome5.certificate, - "Organization Memberships",(){}), + "Organization Memberships",(){ + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + return OrgMembershipsScreen(orgMemberships: state.profileInformation.otherInformation.orgMemberships); + })); + }), subMenu(Entypo.doc_text, "Non-Academic Recognitions",(){}), ]), diff --git a/lib/sevices/profile/profile_service.dart b/lib/sevices/profile/profile_service.dart index 50d098a..e7ecf66 100644 --- a/lib/sevices/profile/profile_service.dart +++ b/lib/sevices/profile/profile_service.dart @@ -8,14 +8,17 @@ import 'package:unit2/model/profile/basic_information/identification_information import 'package:unit2/model/profile/educational_background.dart'; import 'package:unit2/model/profile/eligibility.dart'; import 'package:unit2/model/profile/learning_development.dart'; +import 'package:unit2/model/profile/other_info.dart'; import 'package:unit2/model/profile/profileInfomation.dart'; import 'package:unit2/model/profile/references.dart'; +import 'package:unit2/model/profile/other_information/skills_and_hobbies.dart'; import 'package:unit2/model/profile/voluntary_works.dart'; import 'package:unit2/model/profile/work_history.dart'; import 'package:unit2/utils/request.dart'; import 'package:unit2/utils/urls.dart'; import '../../model/profile/basic_information/primary-information.dart'; +import '../../model/profile/other_information/organization_memberships.dart'; class ProfileService { static final ProfileService _instance = ProfileService(); @@ -35,6 +38,8 @@ class ProfileService { List learningsDevelopments = []; List educationalBackgrounds = []; List voluntaryWorks =[]; + List skillsHobbies = []; + List orgMemberships = []; Map headers = { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': "Token $token" @@ -105,13 +110,27 @@ class ProfileService { voluntaryWorks.add(vwork); }); + // get all hobbies + data['data']['other_information']['skills_hobbies'].forEach((var skills_hobbies){ + SkillsHobbies skillsAndHobbies = SkillsHobbies.fromJson(skills_hobbies); + skillsHobbies.add(skillsAndHobbies); + }); + + data['data']['other_information']['organization_memberships'].forEach((var org) { + OrganizationMembership organization = + OrganizationMembership.fromJson(org); + orgMemberships.add(organization); + }); + BasicInfo basicInfo = BasicInfo( contactInformation: contactInformation, primaryInformation: primaryInformation, identifications: identificationInformation, citizenships: citizenships); + OtherInformation otherInformation = OtherInformation(skillsAndHobbies: skillsHobbies,orgMemberships: orgMemberships); ProfileInformation profileInformation = ProfileInformation( + otherInformation: otherInformation, workExperiences: workExperiences, basicInfo: basicInfo, eligibilities: eligibilities, @@ -119,6 +138,7 @@ class ProfileService { learningsAndDevelopment: learningsDevelopments, educationalBackgrounds: educationalBackgrounds, voluntaryWorks: voluntaryWorks + ); profileInformation0 = profileInformation; } From d2a34d8e22b4c20379a68a2ec9dbca7629a0adab Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Wed, 8 Feb 2023 11:07:30 +0800 Subject: [PATCH 27/86] integrated address API --- lib/model/profile/basic_info.dart | 6 +- .../profile/basic_information/adress.dart | 249 ++++++++++++++++++ lib/model/profile/family_backround.dart | 0 lib/model/profile/other_info.dart | 4 +- .../non_acedimic_recognition.dart | 109 ++++++++ .../basic_information/address_screen.dart | 50 ++++ .../non_academic_recognition.dart | 54 ++++ lib/screens/profile/profile.dart | 18 +- lib/sevices/profile/profile_service.dart | 176 ++++++++----- 9 files changed, 600 insertions(+), 66 deletions(-) create mode 100644 lib/model/profile/basic_information/adress.dart create mode 100644 lib/model/profile/family_backround.dart create mode 100644 lib/model/profile/other_information/non_acedimic_recognition.dart create mode 100644 lib/screens/profile/components/basic_information/address_screen.dart create mode 100644 lib/screens/profile/components/other_information/non_academic_recognition.dart diff --git a/lib/model/profile/basic_info.dart b/lib/model/profile/basic_info.dart index 29b1db2..243070e 100644 --- a/lib/model/profile/basic_info.dart +++ b/lib/model/profile/basic_info.dart @@ -1,13 +1,15 @@ +import 'package:unit2/model/profile/basic_information/adress.dart'; import 'package:unit2/model/profile/basic_information/citizenship.dart'; import 'package:unit2/model/profile/basic_information/contact_information.dart'; import 'package:unit2/model/profile/basic_information/identification_information.dart'; import 'package:unit2/model/profile/basic_information/primary-information.dart'; class BasicInfo{ - PrimaryInformation primaryInformation; + PrimaryInformation? primaryInformation; List contactInformation; List identifications; List citizenships; - BasicInfo({required this.contactInformation, required this.primaryInformation,required this.identifications,required this.citizenships}); + List addresses; + BasicInfo({required this.addresses, required this.contactInformation, required this.primaryInformation,required this.identifications,required this.citizenships}); } \ No newline at end of file diff --git a/lib/model/profile/basic_information/adress.dart b/lib/model/profile/basic_information/adress.dart new file mode 100644 index 0000000..68e18d6 --- /dev/null +++ b/lib/model/profile/basic_information/adress.dart @@ -0,0 +1,249 @@ + + +class MainAdress { + MainAdress({ + this.id, + this.address, + this.details, + this.subdivision, + }); + + final int? id; + final AddressClass? address; + final String? details; + final Subdivision? subdivision; + + factory MainAdress.fromJson(Map json) => MainAdress( + id: json["id"], + address: json["address"] == null ? null : AddressClass.fromJson(json["address"]), + details: json["details"], + subdivision: json["subdivision"] == null ? null : Subdivision.fromJson(json["subdivision"]), + ); + + Map toJson() => { + "id": id, + "address": address?.toJson(), + "details": details, + "subdivision": subdivision?.toJson(), + }; +} + +class AddressClass { + AddressClass({ + this.id, + this.country, + this.barangay, + this.category, + this.areaClass, + this.cityMunicipality, + }); + + final int? id; + final Country? country; + final Barangay? barangay; + final Category? category; + final String? areaClass; + final CityMunicipality? cityMunicipality; + + factory AddressClass.fromJson(Map json) => AddressClass( + id: json["id"], + country: json["country"] == null ? null : Country.fromJson(json["country"]), + barangay: json["barangay"] == null ? null : Barangay.fromJson(json["barangay"]), + category: json["category"] == null ? null : Category.fromJson(json["category"]), + areaClass: json["area_class"], + cityMunicipality: json["city_municipality"] == null ? null : CityMunicipality.fromJson(json["city_municipality"]), + ); + + Map toJson() => { + "id": id, + "country": country?.toJson(), + "barangay": barangay?.toJson(), + "category": category?.toJson(), + "area_class": areaClass, + "city_municipality": cityMunicipality?.toJson(), + }; +} + +class Barangay { + Barangay({ + this.code, + this.description, + this.cityMunicipality, + }); + + final String? code; + final String? description; + final CityMunicipality? cityMunicipality; + + factory Barangay.fromJson(Map json) => Barangay( + code: json["code"], + description: json["description"], + cityMunicipality: json["city_municipality"] == null ? null : CityMunicipality.fromJson(json["city_municipality"]), + ); + + Map toJson() => { + "code": code, + "description": description, + "city_municipality": cityMunicipality?.toJson(), + }; +} + +class CityMunicipality { + CityMunicipality({ + this.code, + this.zipcode, + this.province, + this.psgcCode, + this.description, + }); + + final String? code; + final String? zipcode; + final Province? province; + final String? psgcCode; + final String? description; + + factory CityMunicipality.fromJson(Map json) => CityMunicipality( + code: json["code"], + zipcode: json["zipcode"], + province: json["province"] == null ? null : Province.fromJson(json["province"]), + psgcCode: json["psgc_code"], + description: json["description"], + ); + + Map toJson() => { + "code": code, + "zipcode": zipcode, + "province": province?.toJson(), + "psgc_code": psgcCode, + "description": description, + }; +} + +class Province { + Province({ + this.code, + this.region, + this.psgcCode, + this.shortname, + this.description, + }); + + final String? code; + final Region? region; + final String? psgcCode; + final String? shortname; + final String? description; + + factory Province.fromJson(Map json) => Province( + code: json["code"], + region: json["region"] == null ? null : Region.fromJson(json["region"]), + psgcCode: json["psgc_code"], + shortname: json["shortname"], + description: json["description"], + ); + + Map toJson() => { + "code": code, + "region": region?.toJson(), + "psgc_code": psgcCode, + "shortname": shortname, + "description": description, + }; +} + +class Region { + Region({ + this.code, + this.psgcCode, + this.description, + }); + + final int? code; + final String? psgcCode; + final String? description; + + factory Region.fromJson(Map json) => Region( + code: json["code"], + psgcCode: json["psgc_code"], + description: json["description"], + ); + + Map toJson() => { + "code": code, + "psgc_code": psgcCode, + "description": description, + }; +} + +class Category { + Category({ + this.id, + this.name, + this.type, + }); + + final int? id; + final String? name; + final String? type; + + factory Category.fromJson(Map json) => Category( + id: json["id"], + name: json["name"], + type: json["type"], + ); + + Map toJson() => { + "id": id, + "name": name, + "type": type, + }; +} + +class Country { + Country({ + this.id, + this.code, + this.name, + }); + + final int? id; + final String? code; + final String? name; + + factory Country.fromJson(Map json) => Country( + id: json["id"], + code: json["code"], + name: json["name"], + ); + + Map toJson() => { + "id": id, + "code": code, + "name": name, + }; +} + +class Subdivision { + Subdivision({ + this.id, + this.lotNo, + this.blockNo, + }); + + final int? id; + final int? lotNo; + final int? blockNo; + + factory Subdivision.fromJson(Map json) => Subdivision( + id: json["id"], + lotNo: json["lot_no"], + blockNo: json["block_no"], + ); + + Map toJson() => { + "id": id, + "lot_no": lotNo, + "block_no": blockNo, + }; +} diff --git a/lib/model/profile/family_backround.dart b/lib/model/profile/family_backround.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/model/profile/other_info.dart b/lib/model/profile/other_info.dart index fe43df3..ab2a6b7 100644 --- a/lib/model/profile/other_info.dart +++ b/lib/model/profile/other_info.dart @@ -1,8 +1,10 @@ +import 'package:unit2/model/profile/other_information/non_acedimic_recognition.dart'; import 'package:unit2/model/profile/other_information/organization_memberships.dart'; import 'package:unit2/model/profile/other_information/skills_and_hobbies.dart'; class OtherInformation{ List skillsAndHobbies; ListorgMemberships; - OtherInformation({required this.skillsAndHobbies, required this.orgMemberships}); + List nonAcademicRecognition; + OtherInformation({required this.skillsAndHobbies, required this.orgMemberships, required this.nonAcademicRecognition}); } \ No newline at end of file diff --git a/lib/model/profile/other_information/non_acedimic_recognition.dart b/lib/model/profile/other_information/non_acedimic_recognition.dart new file mode 100644 index 0000000..119b762 --- /dev/null +++ b/lib/model/profile/other_information/non_acedimic_recognition.dart @@ -0,0 +1,109 @@ +// To parse this JSON data, do +// +// final nonAcademicRecognition = nonAcademicRecognitionFromJson(jsonString); + +import 'dart:convert'; + +NonAcademicRecognition nonAcademicRecognitionFromJson(String str) => NonAcademicRecognition.fromJson(json.decode(str)); + +String nonAcademicRecognitionToJson(NonAcademicRecognition data) => json.encode(data.toJson()); + +class NonAcademicRecognition { + NonAcademicRecognition({ + this.id, + this.title, + this.presenter, + }); + + final int? id; + final String? title; + final Presenter? presenter; + + factory NonAcademicRecognition.fromJson(Map json) => NonAcademicRecognition( + id: json["id"], + title: json["title"], + presenter: json["presenter"] == null ? null : Presenter.fromJson(json["presenter"]), + ); + + Map toJson() => { + "id": id, + "title": title, + "presenter": presenter?.toJson(), + }; +} + +class Presenter { + Presenter({ + this.id, + this.name, + this.category, + this.privateEntity, + }); + + final int? id; + final String? name; + final Category? category; + final bool? privateEntity; + + factory Presenter.fromJson(Map json) => Presenter( + id: json["id"], + name: json["name"], + category: json["category"] == null ? null : Category.fromJson(json["category"]), + privateEntity: json["private_entity"], + ); + + Map toJson() => { + "id": id, + "name": name, + "category": category?.toJson(), + "private_entity": privateEntity, + }; +} + +class Category { + Category({ + this.id, + this.name, + this.industryClass, + }); + + final int? id; + final String? name; + final IndustryClass? industryClass; + + factory Category.fromJson(Map json) => Category( + id: json["id"], + name: json["name"], + industryClass: json["industry_class"] == null ? null : IndustryClass.fromJson(json["industry_class"]), + ); + + Map toJson() => { + "id": id, + "name": name, + "industry_class": industryClass?.toJson(), + }; +} + +class IndustryClass { + IndustryClass({ + this.id, + this.name, + this.description, + }); + + final int? id; + final String? name; + final dynamic description; + + factory IndustryClass.fromJson(Map json) => IndustryClass( + id: json["id"], + name: json["name"], + description: json["description"], + ); + + Map toJson() => { + "id": id, + "name": name, + "description": description, + }; +} diff --git a/lib/screens/profile/components/basic_information/address_screen.dart b/lib/screens/profile/components/basic_information/address_screen.dart new file mode 100644 index 0000000..f5c9bff --- /dev/null +++ b/lib/screens/profile/components/basic_information/address_screen.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:unit2/model/profile/basic_information/adress.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/global.dart'; + +class AddressScreen extends StatefulWidget { + final List addresses; + const AddressScreen({super.key, required this.addresses}); + + @override + State createState() => _AddressScreenState(); +} + +class _AddressScreenState extends State { + @override + Widget build(BuildContext context) { + + return Scaffold( + appBar: AppBar(title: const Text("Addresses"),centerTitle: true, backgroundColor: primary,), + body: ListView.builder( + itemCount: widget.addresses.length, + itemBuilder: ( + BuildContext context, int index){ + String? subdivision = widget.addresses[index].details??''; + String category = widget.addresses[index].address!.category!.name!; + String? barangay = widget.addresses[index].address!.barangay != null?widget.addresses[index].address!.barangay!.description:''; + String cityMunicipality = widget.addresses[index].address!.cityMunicipality!.description!; + String province = widget.addresses[index].address!.cityMunicipality!.province!.description!; + String region = widget.addresses[index].address!.cityMunicipality!.province!.region!.description!; + return Column(children: [ + Container( + width: screenWidth, + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: const BorderRadius.all(Radius.circular(12))), + padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), + child: Row(children: [ + Expanded(child: Column(children: [ + Row(children: [Text(subdivision), const SizedBox(width: 5,), Text(category)],), + Text("$barangay $cityMunicipality $province $region"), + ],)), + IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)) + ]), + ), + const SizedBox(height: 5,) + ],); + }), + ); + } +} \ No newline at end of file diff --git a/lib/screens/profile/components/other_information/non_academic_recognition.dart b/lib/screens/profile/components/other_information/non_academic_recognition.dart new file mode 100644 index 0000000..d8420ee --- /dev/null +++ b/lib/screens/profile/components/other_information/non_academic_recognition.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:unit2/model/profile/other_information/non_acedimic_recognition.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/global.dart'; + +class NonAcademicRecognitionScreen extends StatefulWidget { + final List nonAcademicRecognitions; + const NonAcademicRecognitionScreen({super.key, required this.nonAcademicRecognitions}); + + @override + State createState() => _NonAcademicRecognitionScreenState(); +} + +class _NonAcademicRecognitionScreenState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text("Non Academic Recognition"), centerTitle: true, backgroundColor: primary,), + body: ListView.builder( + itemCount: widget.nonAcademicRecognitions.length, + itemBuilder: (BuildContext context, int index){ + String award = widget.nonAcademicRecognitions[index].title!; + String presenter = widget.nonAcademicRecognitions[index].presenter!.name!; + return Column( + children: [ + Container( + width: screenWidth, + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: const BorderRadius.all(Radius.circular(12))), + padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), + child: Row( + children: [ + Expanded(child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(award), + Text(presenter), + ],)), + + IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)) + ], + ), + ), + const SizedBox(height: 5,), + ], + ); + }), + ); + } +} \ No newline at end of file diff --git a/lib/screens/profile/profile.dart b/lib/screens/profile/profile.dart index 53c4fed..d9280f6 100644 --- a/lib/screens/profile/profile.dart +++ b/lib/screens/profile/profile.dart @@ -8,6 +8,7 @@ import 'package:fluttericon/entypo_icons.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:fluttericon/modern_pictograms_icons.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/screens/profile/components/basic_information/address_screen.dart'; import 'package:unit2/screens/profile/components/basic_information/citizenship_screen.dart'; import 'package:unit2/screens/profile/components/basic_information/contact_information.dart'; import 'package:unit2/screens/profile/components/basic_information/identification_information.dart'; @@ -16,6 +17,7 @@ import 'package:unit2/screens/profile/components/education_screen.dart'; import 'package:unit2/screens/profile/components/eligibility.dart'; import 'package:unit2/screens/profile/components/learning_and_development_screen.dart'; import 'package:unit2/screens/profile/components/loading_screen.dart'; +import 'package:unit2/screens/profile/components/other_information/non_academic_recognition.dart'; import 'package:unit2/screens/profile/components/other_information/org_membership.dart'; import 'package:unit2/screens/profile/components/other_information/skills_and_hobbies_screen.dart'; import 'package:unit2/screens/profile/components/references_screen.dart'; @@ -89,10 +91,15 @@ class _ProfileInfoState extends State { items: [ subMenu(Icons.person, "Primary",(){ Navigator.push(context,MaterialPageRoute(builder: (BuildContext context){ - return PrimaryInfo(primaryInformation: state.profileInformation.basicInfo.primaryInformation); + return PrimaryInfo(primaryInformation: state.profileInformation.basicInfo.primaryInformation!); }) ); }), - subMenu(Icons.home, "Home Addresses",(){}), + subMenu(Icons.home, "Home Addresses",(){ + Navigator.push(context,MaterialPageRoute(builder: (BuildContext context){ + return AddressScreen(addresses: state.profileInformation.basicInfo.addresses); + }) ); + + }), subMenu( Icons.contact_mail, "Identifications",(){ Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ @@ -207,7 +214,12 @@ class _ProfileInfoState extends State { })); }), subMenu(Entypo.doc_text, - "Non-Academic Recognitions",(){}), + "Non-Academic Recognitions",(){ + + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + return NonAcademicRecognitionScreen(nonAcademicRecognitions: state.profileInformation.otherInformation.nonAcademicRecognition); + })); + }), ]), ExpandableGroup( collapsedIcon: diff --git a/lib/sevices/profile/profile_service.dart b/lib/sevices/profile/profile_service.dart index e7ecf66..d6ffdfe 100644 --- a/lib/sevices/profile/profile_service.dart +++ b/lib/sevices/profile/profile_service.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:unit2/model/login_data/employee_info/employee_info.dart'; import 'package:unit2/model/profile/basic_info.dart'; +import 'package:unit2/model/profile/basic_information/adress.dart'; import 'package:unit2/model/profile/basic_information/citizenship.dart'; import 'package:unit2/model/profile/basic_information/contact_information.dart'; import 'package:unit2/model/profile/basic_information/identification_information.dart'; @@ -9,6 +10,7 @@ import 'package:unit2/model/profile/educational_background.dart'; import 'package:unit2/model/profile/eligibility.dart'; import 'package:unit2/model/profile/learning_development.dart'; import 'package:unit2/model/profile/other_info.dart'; +import 'package:unit2/model/profile/other_information/non_acedimic_recognition.dart'; import 'package:unit2/model/profile/profileInfomation.dart'; import 'package:unit2/model/profile/references.dart'; import 'package:unit2/model/profile/other_information/skills_and_hobbies.dart'; @@ -28,18 +30,20 @@ class ProfileService { String url = Url.instance.profileInformation(); String path = url + id.toString(); ProfileInformation? profileInformation0; - PrimaryInformation primaryInformation; - List workExperiences =[]; + PrimaryInformation? primaryInformation; + List workExperiences = []; List references = []; + List addresses = []; List identificationInformation = []; List contactInformation = []; List eligibilities = []; List citizenships = []; List learningsDevelopments = []; List educationalBackgrounds = []; - List voluntaryWorks =[]; + List voluntaryWorks = []; List skillsHobbies = []; List orgMemberships = []; + List nonAcademicRecognitions = []; Map headers = { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': "Token $token" @@ -49,97 +53,149 @@ class ProfileService { .getRequest(path: path, param: {}, headers: headers); if (response.statusCode == 200) { Map data = jsonDecode(response.body); + // get primary information - primaryInformation = PrimaryInformation.fromJson( - data['data']['basic_information']['primary_information']); + if (data['data']['basic_information']['primary_information'] != null) { + primaryInformation = PrimaryInformation.fromJson( + data['data']['basic_information']['primary_information']); + } else { + primaryInformation = null; + } + // get all contacts - data['data']['basic_information']['contact_information'] - .forEach((var contact) { - ContactInfo contactInfo = ContactInfo.fromJson(contact['contact_info']); - contactInformation.add(contactInfo); - }); + if (data['data']['basic_information']['contact_information'] != null) { + data['data']['basic_information']['contact_information'] + .forEach((var contact) { + ContactInfo contactInfo = + ContactInfo.fromJson(contact['contact_info']); + contactInformation.add(contactInfo); + }); + } + + // get all addresses + if (data['data']['basic_information']['addresses'] != null) { + data['data']['basic_information']['addresses'].forEach((var address) { + MainAdress mainAdress = MainAdress.fromJson(address); + addresses.add(mainAdress); + }); + } + // get all identifications - data['data']['basic_information']['identification_records'] - .forEach((var identity) { - Identification identification = Identification.fromJson(identity); - identificationInformation.add(identification); - }); + if (data['data']['basic_information']['identification_records'] != null) { + data['data']['basic_information']['identification_records']! + .forEach((var identity) { + Identification identification = Identification.fromJson(identity); + identificationInformation.add(identification); + }); + } + //get all eligibilities - data['data']['eligibilities'].forEach((var cert) { - EligibityCert eligibility = EligibityCert.fromJson(cert); - eligibilities.add(eligibility); - }); + if (data['data']['eligibilities'] != null) { + data['data']['eligibilities']!.forEach((var cert) { + EligibityCert eligibility = EligibityCert.fromJson(cert); + eligibilities.add(eligibility); + }); + } + // get all citizenships if (data['data']['citizenship'] != null) { - data['data']['citizenships'].forEach((var citizenship) { + data['data']['citizenships']!.forEach((var citizenship) { Citizenship person = Citizenship.fromJson(citizenship); citizenships.add(person); }); } // get all references; - data['data']['personal_references'].forEach((var person) { - PersonalReference reference = PersonalReference.fromJson(person); - references.add(reference); - }); + if (data['data']['personal_references'] != null) { + data['data']['personal_references'].forEach((var person) { + PersonalReference reference = PersonalReference.fromJson(person); + references.add(reference); + }); + } //get all learning and developments - - data['data']['learning_development'].forEach((var training) { - LearningDevelopement learnings = - LearningDevelopement.fromJson(training); - learningsDevelopments.add(learnings); - }); + if (data['data']['learning_development'] != null) { + data['data']['learning_development'].forEach((var training) { + LearningDevelopement learnings = + LearningDevelopement.fromJson(training); + learningsDevelopments.add(learnings); + }); + } //get all educational background - data['data']['education_background'].forEach((var education) { - EducationalBackground educationalBackground = - EducationalBackground.fromJson(education); - educationalBackgrounds.add(educationalBackground); - }); + if (data['data']['education_background'] != null) { + data['data']['education_background'].forEach((var education) { + EducationalBackground educationalBackground = + EducationalBackground.fromJson(education); + educationalBackgrounds.add(educationalBackground); + }); + } // get all work history - - data['data']['work_experiences'].forEach((var work){ - WorkHistory experience = WorkHistory.fromJson(work); - workExperiences.add(experience); - }); + if (data['data']['work_experiences'] != null) { + data['data']['work_experiences'].forEach((var work) { + WorkHistory experience = WorkHistory.fromJson(work); + workExperiences.add(experience); + }); + } // get all voluntary works - data['data']['voluntary_works'].forEach((var work){ - VoluntaryWork vwork = VoluntaryWork.fromJson(work); - voluntaryWorks.add(vwork); - }); + if (data['data']['voluntary_works'] != null) { + data['data']['voluntary_works'].forEach((var work) { + VoluntaryWork vwork = VoluntaryWork.fromJson(work); + voluntaryWorks.add(vwork); + }); + } - // get all hobbies - data['data']['other_information']['skills_hobbies'].forEach((var skills_hobbies){ - SkillsHobbies skillsAndHobbies = SkillsHobbies.fromJson(skills_hobbies); - skillsHobbies.add(skillsAndHobbies); - }); + // get all hobbies + if (data['data']['other_information']['skills_hobbies'] != null) { + data['data']['other_information']['skills_hobbies'] + .forEach((var skills_hobbies) { + SkillsHobbies skillsAndHobbies = + SkillsHobbies.fromJson(skills_hobbies); + skillsHobbies.add(skillsAndHobbies); + }); + } - data['data']['other_information']['organization_memberships'].forEach((var org) { - OrganizationMembership organization = - OrganizationMembership.fromJson(org); - orgMemberships.add(organization); - }); + //get all organization memberships + if (data['data']['other_information']['organization_memberships'] != + null) { + data['data']['other_information']['organization_memberships'] + .forEach((var org) { + OrganizationMembership organization = + OrganizationMembership.fromJson(org); + orgMemberships.add(organization); + }); + } +//get all non academic recognition + if (data['data']['other_information']['non_academic_records'] != null) { + data['data']['other_information']['non_academic_records'] + .forEach((var recognition) { + NonAcademicRecognition nonAcademicRecognition = + NonAcademicRecognition.fromJson(recognition); + nonAcademicRecognitions.add(nonAcademicRecognition); + }); + } BasicInfo basicInfo = BasicInfo( contactInformation: contactInformation, primaryInformation: primaryInformation, identifications: identificationInformation, - citizenships: citizenships); - OtherInformation otherInformation = OtherInformation(skillsAndHobbies: skillsHobbies,orgMemberships: orgMemberships); + citizenships: citizenships, + addresses: addresses); + OtherInformation otherInformation = OtherInformation( + skillsAndHobbies: skillsHobbies, + orgMemberships: orgMemberships, + nonAcademicRecognition: nonAcademicRecognitions); ProfileInformation profileInformation = ProfileInformation( - otherInformation: otherInformation, + otherInformation: otherInformation, workExperiences: workExperiences, basicInfo: basicInfo, eligibilities: eligibilities, references: references, learningsAndDevelopment: learningsDevelopments, educationalBackgrounds: educationalBackgrounds, - voluntaryWorks: voluntaryWorks - - ); + voluntaryWorks: voluntaryWorks); profileInformation0 = profileInformation; } // }catch(e){ From 7795b5883baccec091583f0f4884a140607e6762 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Wed, 8 Feb 2023 16:06:27 +0800 Subject: [PATCH 28/86] integrated family background API --- lib/model/profile/family_backround.dart | 310 ++++++++++++++++++ lib/model/profile/profileInfomation.dart | 4 +- .../components/family_background_screen.dart | 189 +++++++++++ lib/screens/profile/profile.dart | 7 +- .../homepage.dart/components/menu-screen.dart | 84 +++-- .../unit2/homepage.dart/module-screen.dart | 49 ++- lib/sevices/profile/profile_service.dart | 11 + 7 files changed, 584 insertions(+), 70 deletions(-) create mode 100644 lib/screens/profile/components/family_background_screen.dart diff --git a/lib/model/profile/family_backround.dart b/lib/model/profile/family_backround.dart index e69de29..9083cb7 100644 --- a/lib/model/profile/family_backround.dart +++ b/lib/model/profile/family_backround.dart @@ -0,0 +1,310 @@ +// To parse this JSON data, do +// +// final familyBackground = familyBackgroundFromJson(jsonString); + +import 'dart:convert'; +import 'dart:ffi'; + +FamilyBackground familyBackgroundFromJson(String str) => FamilyBackground.fromJson(json.decode(str)); + +String familyBackgroundToJson(FamilyBackground data) => json.encode(data.toJson()); + +class FamilyBackground { + FamilyBackground({ + this.company, + this.position, + this.relationship, + this.relatedPerson, + this.companyAddress, + this.emergencyContact, + this.incaseOfEmergency, + this.companyContactNumber, + }); + + final Company? company; + final Position? position; + final Relationship? relationship; + final RelatedPerson? relatedPerson; + final String? companyAddress; + final List? emergencyContact; + final bool? incaseOfEmergency; + final String? companyContactNumber; + + factory FamilyBackground.fromJson(Map json) => FamilyBackground( + company: json["company"] == null ? null : Company.fromJson(json["company"]), + position: json["position"] == null ? null : Position.fromJson(json["position"]), + relationship: json["relationship"] == null ? null : Relationship.fromJson(json["relationship"]), + relatedPerson: json["related_person"] == null ? null : RelatedPerson.fromJson(json["related_person"]), + companyAddress: json["company_address"], + emergencyContact: json["emergency_contact"] == null ? [] : List.from(json["emergency_contact"]!.map((x) => EmergencyContact.fromJson(x))), + incaseOfEmergency: json["incase_of_emergency"], + companyContactNumber: json["company_contact_number"], + ); + + Map toJson() => { + "company": company?.toJson(), + "position": position?.toJson(), + "relationship": relationship?.toJson(), + "related_person": relatedPerson?.toJson(), + "company_address": companyAddress, + "emergency_contact": emergencyContact == null ? [] : List.from(emergencyContact!.map((x) => x.toJson())), + "incase_of_emergency": incaseOfEmergency, + "company_contact_number": companyContactNumber, + }; +} + +class Company { + Company({ + this.id, + this.name, + this.category, + this.privateEntity, + }); + + final int? id; + final String? name; + final Category? category; + final bool? privateEntity; + + factory Company.fromJson(Map json) => Company( + id: json["id"], + name: json["name"], + category: json["category"] == null ? null : Category.fromJson(json["category"]), + privateEntity: json["private_entity"], + ); + + Map toJson() => { + "id": id, + "name": name, + "category": category?.toJson(), + "private_entity": privateEntity, + }; +} + +class Category { + Category({ + this.id, + this.name, + this.industryClass, + }); + + final int? id; + final String? name; + final IndustryClass? industryClass; + + factory Category.fromJson(Map json) => Category( + id: json["id"], + name: json["name"], + industryClass: json["industry_class"] == null ? null : IndustryClass.fromJson(json["industry_class"]), + ); + + Map toJson() => { + "id": id, + "name": name, + "industry_class": industryClass?.toJson(), + }; +} + +class IndustryClass { + IndustryClass({ + this.id, + this.name, + this.description, + }); + + final int? id; + final String? name; + final String? description; + + factory IndustryClass.fromJson(Map json) => IndustryClass( + id: json["id"], + name: json["name"], + description: json["description"], + ); + + Map toJson() => { + "id": id, + "name": name, + "description": description, + }; +} + +class EmergencyContact { + EmergencyContact({ + this.telco, + this.isactive, + this.provider, + this.isprimary, + this.numbermail, + this.serviceType, + this.contactinfoid, + this.commServiceId, + }); + + final String? telco; + final bool? isactive; + final int? provider; + final bool? isprimary; + final String? numbermail; + final int? serviceType; + final int? contactinfoid; + final int? commServiceId; + + factory EmergencyContact.fromJson(Map json) => EmergencyContact( + telco: json["telco"], + isactive: json["isactive"], + provider: json["provider"], + isprimary: json["isprimary"], + numbermail: json["numbermail"], + serviceType: json["service_type"], + contactinfoid: json["contactinfoid"], + commServiceId: json["comm_service_id"], + ); + + Map toJson() => { + "telco": telco, + "isactive": isactive, + "provider": provider, + "isprimary": isprimary, + "numbermail": numbermail, + "service_type": serviceType, + "contactinfoid": contactinfoid, + "comm_service_id": commServiceId, + }; +} + +class Position { + Position({ + this.id, + this.title, + }); + + final int? id; + final String? title; + + factory Position.fromJson(Map json) => Position( + id: json["id"], + title: json["title"], + ); + + Map toJson() => { + "id": id, + "title": title, + }; +} + +class RelatedPerson { + RelatedPerson({ + this.id, + this.sex, + this.gender, + this.deceased, + this.heightM, + this.birthdate, + this.esigPath, + this.lastName, + this.weightKg, + this.bloodType, + this.firstName, + this.photoPath, + this.maidenName, + this.middleName, + this.uuidQrcode, + this.civilStatus, + this.titlePrefix, + this.titleSuffix, + this.showTitleId, + this.nameExtension, + }); + + final int? id; + final String? sex; + final String? gender; + final bool? deceased; + final double? heightM; + final DateTime? birthdate; + final String? esigPath; + final String? lastName; + final double? weightKg; + final String? bloodType; + final String? firstName; + final String? photoPath; + final dynamic maidenName; + final String? middleName; + final String? uuidQrcode; + final String? civilStatus; + final String? titlePrefix; + final String? titleSuffix; + final bool? showTitleId; + final String? nameExtension; + + factory RelatedPerson.fromJson(Map json) => RelatedPerson( + id: json["id"], + sex: json["sex"], + gender: json["gender"], + deceased: json["deceased"], + heightM: json["height_m"] == null?null:double.parse(json["height_m"].toString()), + birthdate: json["birthdate"] == null ? null : DateTime.parse(json["birthdate"]), + esigPath: json["esig_path"], + lastName: json["last_name"], + weightKg: json["weight_kg"] == null? null:double.parse(json["weight_kg"].toString()) , + bloodType: json["blood_type"], + firstName: json["first_name"], + photoPath: json["photo_path"], + maidenName: json["maiden_name"], + middleName: json["middle_name"], + uuidQrcode: json["uuid_qrcode"], + civilStatus: json["civil_status"], + titlePrefix: json["title_prefix"], + titleSuffix: json["title_suffix"], + showTitleId: json["show_title_id"], + nameExtension: json["name_extension"], + ); + + Map toJson() => { + "id": id, + "sex": sex, + "gender": gender, + "deceased": deceased, + "height_m": heightM, + "birthdate": "${birthdate!.year.toString().padLeft(4, '0')}-${birthdate!.month.toString().padLeft(2, '0')}-${birthdate!.day.toString().padLeft(2, '0')}", + "esig_path": esigPath, + "last_name": lastName, + "weight_kg": weightKg, + "blood_type": bloodType, + "first_name": firstName, + "photo_path": photoPath, + "maiden_name": maidenName, + "middle_name": middleName, + "uuid_qrcode": uuidQrcode, + "civil_status": civilStatus, + "title_prefix": titlePrefix, + "title_suffix": titleSuffix, + "show_title_id": showTitleId, + "name_extension": nameExtension, + }; +} + +class Relationship { + Relationship({ + this.id, + this.type, + this.category, + }); + + final int? id; + final String? type; + final String? category; + + factory Relationship.fromJson(Map json) => Relationship( + id: json["id"], + type: json["type"], + category: json["category"], + ); + + Map toJson() => { + "id": id, + "type": type, + "category": category, + }; +} diff --git a/lib/model/profile/profileInfomation.dart b/lib/model/profile/profileInfomation.dart index 63bd116..37987fb 100644 --- a/lib/model/profile/profileInfomation.dart +++ b/lib/model/profile/profileInfomation.dart @@ -2,6 +2,7 @@ import 'package:unit2/model/profile/basic_info.dart'; import 'package:unit2/model/profile/basic_information/primary-information.dart'; import 'package:unit2/model/profile/educational_background.dart'; import 'package:unit2/model/profile/eligibility.dart'; +import 'package:unit2/model/profile/family_backround.dart'; import 'package:unit2/model/profile/learning_development.dart'; import 'package:unit2/model/profile/other_info.dart'; import 'package:unit2/model/profile/references.dart'; @@ -15,7 +16,8 @@ class ProfileInformation{ List references; List learningsAndDevelopment; List educationalBackgrounds; + List families; ListworkExperiences; List voluntaryWorks; - ProfileInformation({required this.otherInformation, required this.voluntaryWorks, required this.workExperiences, required this.basicInfo,required this.eligibilities,required this.references, required this.learningsAndDevelopment,required this.educationalBackgrounds}); + ProfileInformation({required this.families, required this.otherInformation, required this.voluntaryWorks, required this.workExperiences, required this.basicInfo,required this.eligibilities,required this.references, required this.learningsAndDevelopment,required this.educationalBackgrounds}); } \ No newline at end of file diff --git a/lib/screens/profile/components/family_background_screen.dart b/lib/screens/profile/components/family_background_screen.dart new file mode 100644 index 0000000..7668ff7 --- /dev/null +++ b/lib/screens/profile/components/family_background_screen.dart @@ -0,0 +1,189 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:unit2/model/profile/family_backround.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/global.dart'; + +class FamilyBackgroundScreen extends StatefulWidget { + final List familyBackground; + const FamilyBackgroundScreen({super.key, required this.familyBackground}); + + @override + State createState() => _FamilyBackgroundScreenState(); +} + +class _FamilyBackgroundScreenState extends State { + FamilyBackground? father; + FamilyBackground? mother; + FamilyBackground? spouse; + List children = []; + List otherRelated = []; + @override + Widget build(BuildContext context) { + father = widget.familyBackground.firstWhere((element) => element.relationship!.id==1); + mother = widget.familyBackground.firstWhere((element) => element.relationship!.id==2); + spouse = widget.familyBackground.firstWhere((element) => element.relationship!.id==3); + var childs = widget.familyBackground.where((element) => element.relationship!.id==4); + if(childs.isNotEmpty){ + for (var element in childs) { + children.add(element); + } + } + var relateds = widget.familyBackground.where((element) => element.relationship!.id! < 4); + if(relateds.isNotEmpty){ + for (var element in childs) { + otherRelated.add(element); + } + } + return Scaffold( + appBar: AppBar(title: const Text("Addresses"), centerTitle: true, backgroundColor: primary,), + body: Column( + children: [ + //Father + Container( + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: const BorderRadius.all(Radius.circular(12))), + padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), + width: screenWidth, + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Father"), + Text("${father!.relatedPerson!.firstName} ${father!.relatedPerson!.middleName} ${father!.relatedPerson!.lastName} ${father!.relatedPerson!.nameExtension??''}"), + const Text("Full Name"), + Row(children: [ + Checkbox(value: false, onChanged: (value){}), + const Text("Incase of emergency") + ],) + ]), + ), + IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)) + ], + ),), + const SizedBox(height: 5,), + //Mother + Container( + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: const BorderRadius.all(Radius.circular(12))), + padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), + width: screenWidth, + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Mother"), + Text("${mother!.relatedPerson!.firstName} ${mother!.relatedPerson!.middleName} ${mother!.relatedPerson!.lastName} ${mother!.relatedPerson!.nameExtension??''}"), + const Text("Full Name"), + Row(children: [ + Checkbox(value: false, onChanged: (value){}), + const Text("Incase of emergency") + ],) + ]), + ), + IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)) + ], + ),), + const SizedBox(height: 5,), + //Spouse + spouse != null? + Container( + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: const BorderRadius.all(Radius.circular(12))), + padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), + width: screenWidth, + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Spouse"), + Text("${spouse!.relatedPerson!.firstName} ${spouse!.relatedPerson!.middleName} ${spouse!.relatedPerson!.lastName} ${spouse!.relatedPerson!.nameExtension??''}"), + const Text("Full Name"), + Row(children: [ + Checkbox(value: false, onChanged: (value){}), + const Text("Incase of emergency") + ],) + ]), + ), + IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)) + ], + ),):const SizedBox(), + const SizedBox(height: 5,), + +// Childrens + children.isNotEmpty?Expanded( + child: ListView( + children: children.map((child) => Container( + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: const BorderRadius.all(Radius.circular(12))), + padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), + width: screenWidth, + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Children"), + Text("${child.relatedPerson!.firstName} ${child.relatedPerson!.middleName} ${child.relatedPerson!.lastName} ${child.relatedPerson!.nameExtension??''}"), + const Text("Full Name"), + Row(children: [ + Checkbox(value: false, onChanged: (value){}), + const Text("Incase of emergency") + ],) + ]), + ), + IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)) + ], + ),),).toList()), + ):const SizedBox(), + + otherRelated.isNotEmpty?Expanded( + child: ListView( + children: otherRelated.map((relative) => Container( + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: const BorderRadius.all(Radius.circular(12))), + padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), + width: screenWidth, + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("Other related Person(s)"), + Text("${relative.relatedPerson!.firstName} ${relative!.relatedPerson!.middleName} ${relative!.relatedPerson!.lastName} ${relative!.relatedPerson!.nameExtension??''}"), + const Text("Full Name"), + Row(children: [ + Checkbox(value: false, onChanged: (value){}), + const Text("Incase of emergency") + ],) + ]), + ), + IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)) + ], + ),),).toList()), + ):const SizedBox() + ]), + ); + } +} \ No newline at end of file diff --git a/lib/screens/profile/profile.dart b/lib/screens/profile/profile.dart index d9280f6..05ca514 100644 --- a/lib/screens/profile/profile.dart +++ b/lib/screens/profile/profile.dart @@ -15,6 +15,7 @@ import 'package:unit2/screens/profile/components/basic_information/identificatio import 'package:unit2/screens/profile/components/basic_information/primary_information.dart'; import 'package:unit2/screens/profile/components/education_screen.dart'; import 'package:unit2/screens/profile/components/eligibility.dart'; +import 'package:unit2/screens/profile/components/family_background_screen.dart'; import 'package:unit2/screens/profile/components/learning_and_development_screen.dart'; import 'package:unit2/screens/profile/components/loading_screen.dart'; import 'package:unit2/screens/profile/components/other_information/non_academic_recognition.dart'; @@ -122,7 +123,11 @@ class _ProfileInfoState extends State { MainMenu( icon: Elusive.group, title: "Family", - onTap: (){}, + onTap: (){ + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + return FamilyBackgroundScreen(familyBackground: state.profileInformation.families); + })); + }, ), const Divider(), MainMenu( diff --git a/lib/screens/unit2/homepage.dart/components/menu-screen.dart b/lib/screens/unit2/homepage.dart/components/menu-screen.dart index 7a059ba..b06a8de 100644 --- a/lib/screens/unit2/homepage.dart/components/menu-screen.dart +++ b/lib/screens/unit2/homepage.dart/components/menu-screen.dart @@ -21,53 +21,51 @@ class _MenuScreenState extends State { widget.userData!.user!.login!.user!.firstName!.toUpperCase(); final String lastname = widget.userData!.user!.login!.user!.lastName!.toUpperCase(); - return SafeArea( - child: Drawer( - child: SingleChildScrollView( - child: SizedBox( - height: blockSizeVertical * 96, - child: Column( - // ignore: prefer_const_literals_to_create_immutables - children: [ - UserAccountsDrawerHeader( - decoration: const BoxDecoration( - color: primary, - image: DecorationImage( - image: AssetImage('assets/pngs/bg.png'), - fit: BoxFit.cover)), - accountName: Text("$firstName $lastname"), - accountEmail: null, - currentAccountPicture: CircleAvatar( - radius: 40, - backgroundColor: fifth, - child: CircleAvatar( - radius: 33, - backgroundColor: third, - child: //Icon(Icons.person, size: 40, color: fifth), - Text( - firstName[0].toUpperCase(), - style: const TextStyle(fontSize: 45.0, color: fifth), - ), + return Drawer( + child: SingleChildScrollView( + child: SizedBox( + height: blockSizeVertical * 96, + child: Column( + // ignore: prefer_const_literals_to_create_immutables + children: [ + UserAccountsDrawerHeader( + decoration: const BoxDecoration( + color: primary, + image: DecorationImage( + image: AssetImage('assets/pngs/bg.png'), + fit: BoxFit.cover)), + accountName: Text("$firstName $lastname"), + accountEmail: null, + currentAccountPicture: CircleAvatar( + radius: 40, + backgroundColor: fifth, + child: CircleAvatar( + radius: 33, + backgroundColor: third, + child: //Icon(Icons.person, size: 40, color: fifth), + Text( + firstName[0].toUpperCase(), + style: const TextStyle(fontSize: 45.0, color: fifth), ), ), ), - getTile(FontAwesome5.user, "Basic Info", '/basic-info', context, + ), + getTile(FontAwesome5.user, "Basic Info", '/basic-info', context, + widget.userData!), + const Divider(), + getTile(FontAwesome5.user_circle, "Profile", + '/profile', context, widget.userData!), + const Divider(), + getTile(FontAwesome5.life_ring, "Request SOS", '/request-sos', + context, widget.userData!), + const Divider(), + Expanded( + child: Align( + alignment: FractionalOffset.bottomLeft, + child: getTile(WebSymbols.logout, "Logout", 'login', context, widget.userData!), - const Divider(), - getTile(FontAwesome5.user_circle, "Profile", - '/profile', context, widget.userData!), - const Divider(), - getTile(FontAwesome5.life_ring, "Request SOS", '/request-sos', - context, widget.userData!), - const Divider(), - Expanded( - child: Align( - alignment: FractionalOffset.bottomLeft, - child: getTile(WebSymbols.logout, "Logout", 'login', context, - widget.userData!), - )), - ], - ), + )), + ], ), ), ), diff --git a/lib/screens/unit2/homepage.dart/module-screen.dart b/lib/screens/unit2/homepage.dart/module-screen.dart index e8aa371..df1f7d7 100644 --- a/lib/screens/unit2/homepage.dart/module-screen.dart +++ b/lib/screens/unit2/homepage.dart/module-screen.dart @@ -47,34 +47,33 @@ class _MainScreenState extends State { } }); }); - return SafeArea( - child: Scaffold( + return Scaffold( appBar: AppBar( - backgroundColor: primary, - leading: IconButton( - onPressed: () { - ZoomDrawer.of(context)!.toggle(); - }, - icon: const Icon( - Icons.menu, - color: Colors.white, - ), - ), - centerTitle: true, - title: const Text( - unit2ModuleScreen, - style: TextStyle( - fontSize: 18.0, - color: Colors.white, - ), - ), + backgroundColor: primary, + leading: IconButton( + onPressed: () { + ZoomDrawer.of(context)!.toggle(); + }, + icon: const Icon( + Icons.menu, + color: Colors.white, + ), + ), + centerTitle: true, + title: const Text( + unit2ModuleScreen, + style: TextStyle( + fontSize: 18.0, + color: Colors.white, + ), + ), ), body: state.userData!.user!.login!.user!.roles!.isNotEmpty - ? DashBoard( - roles: roles, - ) - : const NoModule(), - )); + ? DashBoard( + roles: roles, + ) + : const NoModule(), + ); } return Container(); }), diff --git a/lib/sevices/profile/profile_service.dart b/lib/sevices/profile/profile_service.dart index d6ffdfe..af76c43 100644 --- a/lib/sevices/profile/profile_service.dart +++ b/lib/sevices/profile/profile_service.dart @@ -8,6 +8,7 @@ import 'package:unit2/model/profile/basic_information/contact_information.dart'; import 'package:unit2/model/profile/basic_information/identification_information.dart'; import 'package:unit2/model/profile/educational_background.dart'; import 'package:unit2/model/profile/eligibility.dart'; +import 'package:unit2/model/profile/family_backround.dart'; import 'package:unit2/model/profile/learning_development.dart'; import 'package:unit2/model/profile/other_info.dart'; import 'package:unit2/model/profile/other_information/non_acedimic_recognition.dart'; @@ -37,6 +38,7 @@ class ProfileService { List identificationInformation = []; List contactInformation = []; List eligibilities = []; + List families = []; List citizenships = []; List learningsDevelopments = []; List educationalBackgrounds = []; @@ -89,6 +91,14 @@ class ProfileService { }); } + // get all family background + if(data['data']['family_background'] != null){ + data['data']['family_background'].forEach((var family){ + FamilyBackground familyBackground = FamilyBackground.fromJson(family); + families.add(familyBackground); + }); + } + //get all eligibilities if (data['data']['eligibilities'] != null) { data['data']['eligibilities']!.forEach((var cert) { @@ -188,6 +198,7 @@ class ProfileService { orgMemberships: orgMemberships, nonAcademicRecognition: nonAcademicRecognitions); ProfileInformation profileInformation = ProfileInformation( + families: families, otherInformation: otherInformation, workExperiences: workExperiences, basicInfo: basicInfo, From 118a4f45872e02f8a6a279e3c11bdddde59f5523 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Thu, 9 Feb 2023 16:48:19 +0800 Subject: [PATCH 29/86] refactor profile screens --- lib/model/profile/eligibility.dart | 2 +- .../basic_information/address_screen.dart | 42 +- .../basic_information/citizenship_screen.dart | 3 +- ...n.dart => contact_information_screen.dart} | 35 +- .../identification_information.dart | 63 --- .../identification_information_screen.dart | 90 ++++ .../primary_information.dart | 163 ------- .../primary_information_screen.dart | 159 +++++++ .../profile/components/education_screen.dart | 110 +++-- .../profile/components/eligibility.dart | 68 --- .../components/eligibility_screen.dart | 70 +++ .../components/family_background_screen.dart | 412 ++++++++++------ .../learning_and_development_screen.dart | 20 +- .../profile/components/loading_screen.dart | 2 +- ...t => non_academic_recognition_screen.dart} | 14 +- ...ership.dart => org_membership_screen.dart} | 25 +- .../skills_and_hobbies_screen.dart | 50 +- .../profile/components/references_screen.dart | 26 +- .../voluntary_works_screen.dart} | 23 +- .../components/work_history_screen.dart | 80 ++-- lib/screens/profile/profile.dart | 442 +++++++++--------- lib/theme-data.dart/box_shadow.dart | 8 + lib/utils/text_container.dart | 46 +- 23 files changed, 1120 insertions(+), 833 deletions(-) rename lib/screens/profile/components/basic_information/{contact_information.dart => contact_information_screen.dart} (66%) delete mode 100644 lib/screens/profile/components/basic_information/identification_information.dart create mode 100644 lib/screens/profile/components/basic_information/identification_information_screen.dart delete mode 100644 lib/screens/profile/components/basic_information/primary_information.dart create mode 100644 lib/screens/profile/components/basic_information/primary_information_screen.dart delete mode 100644 lib/screens/profile/components/eligibility.dart create mode 100644 lib/screens/profile/components/eligibility_screen.dart rename lib/screens/profile/components/other_information/{non_academic_recognition.dart => non_academic_recognition_screen.dart} (77%) rename lib/screens/profile/components/other_information/{org_membership.dart => org_membership_screen.dart} (71%) rename lib/screens/profile/{voluntary_works.dart => components/voluntary_works_screen.dart} (70%) create mode 100644 lib/theme-data.dart/box_shadow.dart diff --git a/lib/model/profile/eligibility.dart b/lib/model/profile/eligibility.dart index 8da69b6..b67a775 100644 --- a/lib/model/profile/eligibility.dart +++ b/lib/model/profile/eligibility.dart @@ -28,7 +28,7 @@ class EligibityCert { final Eligibility? eligibility; final ExamAddress? examAddress; final DateTime? validityDate; - final DateTime? licenseNumber; + final String? licenseNumber; factory EligibityCert.fromJson(Map json) => EligibityCert( id: json["id"], diff --git a/lib/screens/profile/components/basic_information/address_screen.dart b/lib/screens/profile/components/basic_information/address_screen.dart index f5c9bff..fecfa17 100644 --- a/lib/screens/profile/components/basic_information/address_screen.dart +++ b/lib/screens/profile/components/basic_information/address_screen.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; import 'package:unit2/model/profile/basic_information/adress.dart'; +import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; +import 'package:unit2/utils/text_container.dart'; class AddressScreen extends StatefulWidget { final List addresses; @@ -16,33 +18,41 @@ class _AddressScreenState extends State { Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text("Addresses"),centerTitle: true, backgroundColor: primary,), + appBar: AppBar(title: const Text(adressScreenTitle),centerTitle: true, backgroundColor: primary,), body: ListView.builder( + padding: const EdgeInsets.symmetric(vertical: 8,horizontal: 10), itemCount: widget.addresses.length, itemBuilder: ( BuildContext context, int index){ String? subdivision = widget.addresses[index].details??''; String category = widget.addresses[index].address!.category!.name!; - String? barangay = widget.addresses[index].address!.barangay != null?widget.addresses[index].address!.barangay!.description:''; + String? barangay = widget.addresses[index].address!.barangay != null?'${widget.addresses[index].address!.barangay!.description!.toUpperCase()},':''; String cityMunicipality = widget.addresses[index].address!.cityMunicipality!.description!; String province = widget.addresses[index].address!.cityMunicipality!.province!.description!; String region = widget.addresses[index].address!.cityMunicipality!.province!.region!.description!; return Column(children: [ - Container( - width: screenWidth, - decoration: BoxDecoration( - color: Colors.grey[200], - borderRadius: const BorderRadius.all(Radius.circular(12))), - padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), - child: Row(children: [ - Expanded(child: Column(children: [ - Row(children: [Text(subdivision), const SizedBox(width: 5,), Text(category)],), - Text("$barangay $cityMunicipality $province $region"), - ],)), - IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)) - ]), + + Column( + children: [ + Container( + width: screenWidth, + decoration: box1(), + padding: const EdgeInsets.fromLTRB(8,16,0,16), + child: Row(children: [ + Expanded(child: Column(children: [ + Row(children: [Text(subdivision,style: Theme.of(context).textTheme.titleMedium,), const SizedBox(width: 5,), + + Text(category,style: Theme.of(context).textTheme.bodySmall,)],), + const Divider(), + const SizedBox(height: 5,), + Text("$barangay $cityMunicipality, $province, $region",style: Theme.of(context).textTheme.labelLarge,), + ],)), + IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert,color: Colors.grey,)) + ]), + ), + ], ), - const SizedBox(height: 5,) + const SizedBox(height: 5,), ],); }), ); diff --git a/lib/screens/profile/components/basic_information/citizenship_screen.dart b/lib/screens/profile/components/basic_information/citizenship_screen.dart index 3275bd0..f826806 100644 --- a/lib/screens/profile/components/basic_information/citizenship_screen.dart +++ b/lib/screens/profile/components/basic_information/citizenship_screen.dart @@ -4,6 +4,7 @@ import 'package:flutter/src/widgets/placeholder.dart'; import 'package:unit2/model/profile/basic_information/citizenship.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; +import 'package:unit2/utils/text_container.dart'; class CitizenShipScreen extends StatefulWidget { final List citizenships; @@ -18,7 +19,7 @@ class _CitizenShipScreenState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text("Citizenship"),centerTitle: true, + title: const Text(citizenshipScreenTitle),centerTitle: true, backgroundColor: primary, ), body: widget.citizenships.isEmpty? diff --git a/lib/screens/profile/components/basic_information/contact_information.dart b/lib/screens/profile/components/basic_information/contact_information_screen.dart similarity index 66% rename from lib/screens/profile/components/basic_information/contact_information.dart rename to lib/screens/profile/components/basic_information/contact_information_screen.dart index 68962a6..e8055e6 100644 --- a/lib/screens/profile/components/basic_information/contact_information.dart +++ b/lib/screens/profile/components/basic_information/contact_information_screen.dart @@ -1,36 +1,39 @@ import 'package:flutter/material.dart'; import 'package:unit2/model/profile/basic_information/contact_information.dart'; +import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/text_container.dart'; -class ContactInformation extends StatefulWidget { +class ContactInformationScreen extends StatefulWidget { final List contacts; - const ContactInformation({super.key, required this.contacts}); + const ContactInformationScreen({super.key, required this.contacts}); @override - State createState() => _ContactInformationState(); + State createState() => _ContactInformationScreenState(); } -class _ContactInformationState extends State { +class _ContactInformationScreenState extends State { @override Widget build(BuildContext context) { return SafeArea( child: Scaffold( appBar: AppBar( - title: const Text("Contact Information"), + title: const Text(contactScreenTitle), centerTitle: true, backgroundColor: primary, ), body: ListView.builder( + padding: const EdgeInsets.symmetric(vertical: 8,horizontal: 10), itemCount: widget.contacts.length, itemBuilder: (BuildContext context, int index) { + String numberMail = widget.contacts[index].numbermail!; + String commService = widget.contacts[index].commService!.serviceProvider!.alias!; return Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( - decoration: BoxDecoration( - color: Colors.grey[200], - borderRadius: const BorderRadius.all(Radius.circular(12))), + decoration: box1(), padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), child: Row( children: [ @@ -39,16 +42,18 @@ class _ContactInformationState extends State { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(widget.contacts[index].numbermail.toString(),style: Theme.of(context).textTheme.titleLarge,), + Text(numberMail,style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500)), + const Divider(), const SizedBox(height: 5,), Row( children: [ - Text(widget.contacts[index].commService! - .serviceProvider!.alias - .toString()), - const SizedBox(width: 15,), + Expanded( + child: Text(commService + .toString(),style: Theme.of(context).textTheme.titleSmall,), + ), + widget.contacts[index].active==true? const Badge(backgroundColor: Colors.green, label: Text("Active",),):const SizedBox(), - const SizedBox(width: 8), + const SizedBox(width: 5), widget.contacts[index].primary==true? const Badge(backgroundColor: Colors.blue, label: Text("Primary"),):const SizedBox() ], ), @@ -59,7 +64,7 @@ class _ContactInformationState extends State { ]), ), - IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)) + IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert,color: Colors.grey,)) ], ), ), diff --git a/lib/screens/profile/components/basic_information/identification_information.dart b/lib/screens/profile/components/basic_information/identification_information.dart deleted file mode 100644 index 7cf8efc..0000000 --- a/lib/screens/profile/components/basic_information/identification_information.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; -import 'package:unit2/model/profile/basic_information/identification_information.dart'; -import 'package:unit2/theme-data.dart/colors.dart'; -import 'package:unit2/utils/global.dart'; - -class IdentificationInformations extends StatefulWidget { - final List identities; - const IdentificationInformations({super.key, required this.identities}); - - @override - State createState() => _IdentificationInformationsState(); -} - -class _IdentificationInformationsState extends State { - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: const Text("Identifications"),centerTitle: true, backgroundColor: primary,), -body: ListView.builder( - itemCount: widget.identities.length, - itemBuilder: (BuildContext context, int index){ - return Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: screenWidth, - decoration: BoxDecoration( - color: Colors.grey[200], - borderRadius: const BorderRadius.all(Radius.circular(12))), - padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(widget.identities[index].agency!.name!,style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.bold)), - Row( - children: [ - Text(widget.identities[index].identificationNumber!,style: Theme.of(context).textTheme.titleSmall!.copyWith(fontWeight: FontWeight.bold)), - const SizedBox(width: 20,), - Badge(backgroundColor: success2, label:Text(widget.identities[index].agency!.privateEntity==true?"PRIVATE":"GOVERNMENT",)), - ], - ), - Text("${widget.identities[index].issuedAt!.cityMunicipality!.description!} ${widget.identities[index].issuedAt!.cityMunicipality!.province!.description}"), - ]), - - ), - IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)) - ], - ), - ), - const SizedBox(height: 5,), - ], - ); -}), - ); - } -} \ No newline at end of file diff --git a/lib/screens/profile/components/basic_information/identification_information_screen.dart b/lib/screens/profile/components/basic_information/identification_information_screen.dart new file mode 100644 index 0000000..e531d2a --- /dev/null +++ b/lib/screens/profile/components/basic_information/identification_information_screen.dart @@ -0,0 +1,90 @@ +import 'package:flutter/material.dart'; +import 'package:unit2/model/profile/basic_information/identification_information.dart'; +import 'package:unit2/theme-data.dart/box_shadow.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/global.dart'; +import 'package:unit2/utils/text_container.dart'; + +class IdentificationsScreen extends StatefulWidget { + final List identities; + const IdentificationsScreen({super.key, required this.identities}); + + @override + State createState() => + _IdentificationsScreenState(); +} + +class _IdentificationsScreenState + extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text(identificationScreenTitle), + centerTitle: true, + backgroundColor: primary, + ), + body: ListView.builder( + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 10), + itemCount: widget.identities.length, + itemBuilder: (BuildContext context, int index) { + String agency = widget.identities[index].agency!.name!; + String idNumber = widget.identities[index].identificationNumber!; + bool government = widget.identities[index].agency!.privateEntity!; + String issuedAt = "${widget.identities[index].issuedAt!.cityMunicipality!.description!} ${widget.identities[index].issuedAt!.cityMunicipality!.province!.description}"; + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: screenWidth, + decoration: box1(), + padding: + const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(agency,style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500)), + const Divider(), + const SizedBox(height: 5,), + Row( + children: [ + Expanded( + child: Text( + "$idNumberText : $idNumber",style: Theme.of(context).textTheme.titleSmall, + ), + ), + + Badge( + + backgroundColor: success2, + label: Text( + government == + true + ? privateText.toUpperCase() + :governmentText.toUpperCase(), + style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.white),)), + ], + ), + const SizedBox(height: 5,), + Text(issuedAt), + ]), + ), + IconButton( + onPressed: () {}, icon: const Icon(Icons.more_vert,color: Colors.grey,)) + ], + ), + ), + const SizedBox( + height: 5, + ), + ], + ); + }), + ); + } +} diff --git a/lib/screens/profile/components/basic_information/primary_information.dart b/lib/screens/profile/components/basic_information/primary_information.dart deleted file mode 100644 index f8243a0..0000000 --- a/lib/screens/profile/components/basic_information/primary_information.dart +++ /dev/null @@ -1,163 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; -import 'package:flutter_form_builder/flutter_form_builder.dart'; -import 'package:form_builder_validators/form_builder_validators.dart'; -import 'package:intl/intl.dart'; -import 'package:unit2/model/profile/basic_information/primary-information.dart'; -import 'package:unit2/theme-data.dart/colors.dart'; -import 'package:unit2/theme-data.dart/form-style.dart'; -import 'package:unit2/utils/global.dart'; - -class PrimaryInfo extends StatefulWidget { - final PrimaryInformation primaryInformation; - const PrimaryInfo({super.key, required this.primaryInformation}); - - @override - State createState() => _PrimaryInfoState(); -} - -class _PrimaryInfoState extends State { - @override - Widget build(BuildContext context) { - final _formKey = GlobalKey(); - bool enabled = false; - DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); - return SafeArea( - child: Scaffold( - appBar: AppBar( - title: const Text("Primary Information"), - centerTitle: true, - backgroundColor: primary, - ), - body: Container( - padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 24), - child: FormBuilder( - child: Column( - children: [ - FormBuilderTextField( - enabled: enabled, - name: 'lastname', - initialValue: widget.primaryInformation.lastName!, - decoration: normalTextFieldStyle("Last name", ""), - - ), - const SizedBox(height: 15,), - FormBuilderTextField( - enabled: enabled, - name: 'firstname', - initialValue: widget.primaryInformation.firstName!, - decoration: normalTextFieldStyle("First name", ""), - ), - const SizedBox(height: 15,), - SizedBox( - width: screenWidth, - child: Row(children: [ - Flexible( - flex: 2, - child: FormBuilderTextField( - enabled: enabled, - name: 'middlename', - initialValue: widget.primaryInformation.middleName!, - decoration: normalTextFieldStyle("Middle name", ""), - ),), - const SizedBox(width: 10,), - Flexible( - flex: 1, - child: FormBuilderTextField( - enabled: enabled, - name: 'extension-name', - initialValue: widget.primaryInformation.nameExtension??='N/A', - decoration: normalTextFieldStyle("Name extension", ""), - ),) - ]), - - ), - const SizedBox(height: 15,), - SizedBox(width: screenWidth, - child: Row(children: [ - Flexible( - flex: 1, - child: FormBuilderTextField( - enabled: enabled, - name: 'bday', - initialValue: dteFormat2.format(widget.primaryInformation.birthdate!), - decoration: normalTextFieldStyle("Birth date", ""), - ),), - const SizedBox(width: 10,), - Flexible( - flex: 1, - child: FormBuilderTextField( - enabled: enabled, - name: 'sex', - initialValue: widget.primaryInformation.sex!, - decoration: normalTextFieldStyle("Sex", ""), - ),) - ]),), - const SizedBox(height: 15,), - SizedBox(width: screenWidth, - child: Row(children: [ - Flexible( - flex: 1, - child: FormBuilderTextField( - enabled: enabled, - name: 'bloodtype', - initialValue:widget.primaryInformation.bloodType!, - decoration: normalTextFieldStyle("Blood type", ""), - ),), - const SizedBox(width: 10,), - Flexible( - flex: 1, - child: FormBuilderTextField( - enabled: enabled, - name: 'civil-status', - initialValue: widget.primaryInformation.civilStatus!, - decoration: normalTextFieldStyle("Civil Status", ""), - ),), - const SizedBox(width: 10,), - Flexible( - flex: 1, - child: FormBuilderTextField( - enabled: enabled, - name: 'gender', - initialValue:widget.primaryInformation.gender??="N/A", - decoration: normalTextFieldStyle("Gender", ""), - ),), - ]),), - - const SizedBox(height: 15,), - SizedBox(width: screenWidth, - child: Row(children: [ - Flexible( - flex: 1, - child: FormBuilderTextField( - enabled: enabled, - name: 'height', - initialValue:widget.primaryInformation.heightM!.toString(), - decoration: normalTextFieldStyle("Height", ""), - ),), - const SizedBox(width: 10,), - Flexible( - flex: 1, - child: FormBuilderTextField( - enabled: enabled, - name: 'Weight', - initialValue: widget.primaryInformation.weightKg!.toString(), - decoration: normalTextFieldStyle("Weight", ""), - ),), - const SizedBox(width: 10,), - Flexible( - flex: 1, - child: FormBuilderTextField( - enabled: enabled, - name: 'prefix&suffix', - initialValue:"${widget.primaryInformation.titlePrefix??="NA"} | ${widget.primaryInformation.titleSuffix??="N/A"}", - decoration: normalTextFieldStyle("Title Prefix and Suffix", ""), - ),), - ]),), - ], - )), - )), - ); - } -} diff --git a/lib/screens/profile/components/basic_information/primary_information_screen.dart b/lib/screens/profile/components/basic_information/primary_information_screen.dart new file mode 100644 index 0000000..3dddc42 --- /dev/null +++ b/lib/screens/profile/components/basic_information/primary_information_screen.dart @@ -0,0 +1,159 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:intl/intl.dart'; +import 'package:unit2/model/profile/basic_information/primary-information.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/theme-data.dart/form-style.dart'; +import 'package:unit2/utils/global.dart'; +import 'package:unit2/utils/text_container.dart'; + +class PrimaryInfo extends StatefulWidget { + final PrimaryInformation primaryInformation; + const PrimaryInfo({super.key, required this.primaryInformation}); + + @override + State createState() => _PrimaryInfoState(); +} + +class _PrimaryInfoState extends State { + @override + Widget build(BuildContext context) { + final _formKey = GlobalKey(); + bool enabled = false; + DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); + return Scaffold( + appBar: AppBar( + title: const Text(primaryInformationScreenTitle), + centerTitle: true, + backgroundColor: primary, + ), + body: Container( + padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 24), + child: FormBuilder( + child: Column( + children: [ + FormBuilderTextField( + enabled: enabled, + name: lastname, + initialValue: widget.primaryInformation.lastName!, + decoration: normalTextFieldStyle("Last name", ""), + + ), + const SizedBox(height: 15,), + FormBuilderTextField( + enabled: enabled, + name: firstname, + initialValue: widget.primaryInformation.firstName!, + decoration: normalTextFieldStyle("First name", ""), + ), + const SizedBox(height: 15,), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 2, + child: FormBuilderTextField( + enabled: enabled, + name: middlename, + initialValue: widget.primaryInformation.middleName!, + decoration: normalTextFieldStyle("Middle name", ""), + ),), + const SizedBox(width: 10,), + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: extensionName, + initialValue: widget.primaryInformation.nameExtension??='N/A', + decoration: normalTextFieldStyle("Name extension", ""), + ),) + ]), + + ), + const SizedBox(height: 15,), + SizedBox(width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: extensionName, + initialValue: dteFormat2.format(widget.primaryInformation.birthdate!), + decoration: normalTextFieldStyle("Birth date", ""), + ),), + const SizedBox(width: 10,), + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: sex, + initialValue: widget.primaryInformation.sex!, + decoration: normalTextFieldStyle("Sex", ""), + ),) + ]),), + const SizedBox(height: 15,), + SizedBox(width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: bloodType, + initialValue:widget.primaryInformation.bloodType!, + decoration: normalTextFieldStyle("Blood type", ""), + ),), + const SizedBox(width: 10,), + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: civilStatus, + initialValue: widget.primaryInformation.civilStatus!, + decoration: normalTextFieldStyle("Civil Status", ""), + ),), + const SizedBox(width: 10,), + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: gender, + initialValue:widget.primaryInformation.gender??="N/A", + decoration: normalTextFieldStyle("Gender", ""), + ),), + ]),), + + const SizedBox(height: 15,), + SizedBox(width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: height, + initialValue:widget.primaryInformation.heightM!.toString(), + decoration: normalTextFieldStyle("Height", ""), + ),), + const SizedBox(width: 10,), + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: width, + initialValue: widget.primaryInformation.weightKg!.toString(), + decoration: normalTextFieldStyle("Weight", ""), + ),), + const SizedBox(width: 10,), + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: prefixSuffix, + initialValue:"${widget.primaryInformation.titlePrefix??="NA"} | ${widget.primaryInformation.titleSuffix??="N/A"}", + decoration: normalTextFieldStyle("Title Prefix and Suffix", ""), + ),), + ]),), + ], + )), + )); + } +} diff --git a/lib/screens/profile/components/education_screen.dart b/lib/screens/profile/components/education_screen.dart index 0cd6db4..de6f09f 100644 --- a/lib/screens/profile/components/education_screen.dart +++ b/lib/screens/profile/components/education_screen.dart @@ -1,8 +1,9 @@ import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; + import 'package:unit2/model/profile/educational_background.dart'; +import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/text_container.dart'; class EducationScreen extends StatefulWidget { final List educationBackgrounds; @@ -17,52 +18,109 @@ class _EducationScreenState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text("Educational Background"), + title: const Text(educationScreenTitle), centerTitle: true, backgroundColor: primary, ), body: ListView.builder( + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 10), itemCount: widget.educationBackgrounds.length, itemBuilder: (BuildContext context, int index) { String level = widget.educationBackgrounds[index].education!.level!; String periodFrom = widget.educationBackgrounds[index].periodFrom!; String periodTo = widget.educationBackgrounds[index].periodTo!; - String? program = widget.educationBackgrounds[index].education!.course == null? null: widget.educationBackgrounds[index].education!.course!.program!; - List? honors = widget.educationBackgrounds[index].honors!.toList(); - String school = widget.educationBackgrounds[index].education!.school!.name!; + String? program = + widget.educationBackgrounds[index].education!.course == null + ? null + : widget.educationBackgrounds[index].education!.course! + .program!; + List? honors = + widget.educationBackgrounds[index].honors!.toList(); + String school = + widget.educationBackgrounds[index].education!.school!.name!; return Column( children: [ Container( - decoration: BoxDecoration( - color: Colors.grey[200], - borderRadius: - const BorderRadius.all(Radius.circular(12))), + decoration: box1(), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Row( children: [ Expanded( - child: Column(children: [ - Row( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(level), - Text("$periodFrom $periodTo"), - ], - ), - Text(program??=''), - Text(school), - Container( - child: honors.isNotEmpty? Column( - children: honors.map((Honor honor) => Text(honor.name!)).toList(), - ):const SizedBox() - ) - ]), + Row( + children: [ + Expanded( + child: Text( + level, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(fontWeight: FontWeight.w500), + )), + Text( + "$periodFrom - $periodTo", + style: + Theme.of(context).textTheme.bodyMedium, + ), + ], + ), + + const SizedBox(height: 5,), + Text( + school, + style: Theme.of(context).textTheme.titleSmall, + ), + Container( + padding: const EdgeInsets.only(top: 8), + child: honors.isNotEmpty + ? Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text("$honorsText : "), + Column( + children: + + honors + .map((Honor honor) => + Text(" ${honor.name!}")) + .toList(), + ), + ], + ) + : const SizedBox()), + program == null + ? const SizedBox() + : Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + const SizedBox( + height: 5, + ), + Text(program), + ], + ), + ]), ), IconButton( - onPressed: () {}, icon: const Icon(Icons.more_vert)) + onPressed: () {}, + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + )) ], ), - ) + ), + const SizedBox( + height: 5, + ), ], ); }), diff --git a/lib/screens/profile/components/eligibility.dart b/lib/screens/profile/components/eligibility.dart deleted file mode 100644 index be2d7ff..0000000 --- a/lib/screens/profile/components/eligibility.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; -import 'package:unit2/model/profile/eligibility.dart'; -import 'package:unit2/theme-data.dart/colors.dart'; -import 'package:unit2/utils/global.dart'; - -class EligibiltyScreen extends StatefulWidget { - final List eligibilities; - const EligibiltyScreen({super.key, required this.eligibilities}); - - @override - State createState() => _EligibiltyScreenState(); -} - -class _EligibiltyScreenState extends State { - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text("Eligibility"), - centerTitle: true, - backgroundColor: primary, - ), - body: ListView.builder( - itemCount: widget.eligibilities.length, - itemBuilder: (BuildContext context, int index) { - return Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: screenWidth, - padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), - decoration: BoxDecoration( - color: Colors.grey[200], - borderRadius: - const BorderRadius.all(Radius.circular(12))), - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - - children: [ - - Text(widget.eligibilities[index].eligibility!.title!, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith(fontWeight: FontWeight.bold)), - Text( - "License number: ${widget.eligibilities[index].licenseNumber==null?'N/A':widget.eligibilities[index].licenseNumber.toString()}"), - Text("Rating: ${widget.eligibilities[index].rating}.") - ]), - - ), - IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)) - ], - ), - ) - ], - ); - }), - ); - } -} diff --git a/lib/screens/profile/components/eligibility_screen.dart b/lib/screens/profile/components/eligibility_screen.dart new file mode 100644 index 0000000..8eb7b5d --- /dev/null +++ b/lib/screens/profile/components/eligibility_screen.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:unit2/model/profile/eligibility.dart'; +import 'package:unit2/theme-data.dart/box_shadow.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/global.dart'; +import 'package:unit2/utils/text_container.dart'; + +class EligibiltyScreen extends StatefulWidget { + final List eligibilities; + const EligibiltyScreen({super.key, required this.eligibilities}); + + @override + State createState() => _EligibiltyScreenState(); +} + +class _EligibiltyScreenState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text(elibilityScreenTitle), + centerTitle: true, + backgroundColor: primary, + ), + body: ListView.builder( + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 10), + itemCount: widget.eligibilities.length, + itemBuilder: (BuildContext context, int index) { + String title = widget.eligibilities[index].eligibility!.title!; + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: screenWidth, + padding: + const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + decoration: box1(), + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title,style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500), + ), + const Divider(), + const SizedBox(height: 5,), + Text( + "$licenseNumber: ${widget.eligibilities[index].licenseNumber == null ? 'N/A' : widget.eligibilities[index].licenseNumber.toString()}",style: Theme.of(context).textTheme.titleSmall), + const SizedBox(height: 3,), + Text( + "$rating : ${widget.eligibilities[index].rating}.",style: Theme.of(context).textTheme.titleSmall) + ]), + ), + IconButton( + onPressed: () {}, icon: const Icon(Icons.more_vert,color: Colors.grey,)) + ], + ), + ) + ], + ); + }), + ); + } +} diff --git a/lib/screens/profile/components/family_background_screen.dart b/lib/screens/profile/components/family_background_screen.dart index 7668ff7..393b2f9 100644 --- a/lib/screens/profile/components/family_background_screen.dart +++ b/lib/screens/profile/components/family_background_screen.dart @@ -1,11 +1,9 @@ -import 'dart:math'; - import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; import 'package:unit2/model/profile/family_backround.dart'; +import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; +import 'package:unit2/utils/text_container.dart'; class FamilyBackgroundScreen extends StatefulWidget { final List familyBackground; @@ -23,167 +21,275 @@ class _FamilyBackgroundScreenState extends State { List otherRelated = []; @override Widget build(BuildContext context) { - father = widget.familyBackground.firstWhere((element) => element.relationship!.id==1); - mother = widget.familyBackground.firstWhere((element) => element.relationship!.id==2); - spouse = widget.familyBackground.firstWhere((element) => element.relationship!.id==3); - var childs = widget.familyBackground.where((element) => element.relationship!.id==4); - if(childs.isNotEmpty){ + father = widget.familyBackground + .firstWhere((element) => element.relationship!.id == 1); + mother = widget.familyBackground + .firstWhere((element) => element.relationship!.id == 2); + spouse = widget.familyBackground + .firstWhere((element) => element.relationship!.id == 3); + + // get all children + var childs = widget.familyBackground + .where((element) => element.relationship!.id == 4); + if (childs.isNotEmpty) { for (var element in childs) { children.add(element); } - } - var relateds = widget.familyBackground.where((element) => element.relationship!.id! < 4); - if(relateds.isNotEmpty){ - for (var element in childs) { + } + + //get all related persons + var relateds = widget.familyBackground + .where((element) => element.relationship!.id! > 4); + if (relateds.isNotEmpty) { + for (var element in relateds) { otherRelated.add(element); } - } + } return Scaffold( - appBar: AppBar(title: const Text("Addresses"), centerTitle: true, backgroundColor: primary,), - body: Column( - children: [ - //Father - Container( - decoration: BoxDecoration( - color: Colors.grey[200], - borderRadius: const BorderRadius.all(Radius.circular(12))), - padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), + appBar: AppBar( + title: const Text(familyBackgroundScreenTitle), + centerTitle: true, + backgroundColor: primary, + ), + body: ListView(children: [ + //Father---------------------------------------------- + Container( + decoration: box1(), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), width: screenWidth, - child: Row( - children: [ - Expanded( - child: Column( + child: Row( + children: [ + Expanded( + child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text("Father"), - Text("${father!.relatedPerson!.firstName} ${father!.relatedPerson!.middleName} ${father!.relatedPerson!.lastName} ${father!.relatedPerson!.nameExtension??''}"), - const Text("Full Name"), - Row(children: [ - Checkbox(value: false, onChanged: (value){}), - const Text("Incase of emergency") - ],) - ]), - ), - IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)) - ], - ),), - const SizedBox(height: 5,), - //Mother - Container( - decoration: BoxDecoration( - color: Colors.grey[200], - borderRadius: const BorderRadius.all(Radius.circular(12))), - padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), - width: screenWidth, - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text("Mother"), - Text("${mother!.relatedPerson!.firstName} ${mother!.relatedPerson!.middleName} ${mother!.relatedPerson!.lastName} ${mother!.relatedPerson!.nameExtension??''}"), - const Text("Full Name"), - Row(children: [ - Checkbox(value: false, onChanged: (value){}), - const Text("Incase of emergency") - ],) - ]), - ), - IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)) - ], - ),), - const SizedBox(height: 5,), - //Spouse - spouse != null? - Container( - decoration: BoxDecoration( - color: Colors.grey[200], - borderRadius: const BorderRadius.all(Radius.circular(12))), - padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), - width: screenWidth, - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text("Spouse"), - Text("${spouse!.relatedPerson!.firstName} ${spouse!.relatedPerson!.middleName} ${spouse!.relatedPerson!.lastName} ${spouse!.relatedPerson!.nameExtension??''}"), - const Text("Full Name"), - Row(children: [ - Checkbox(value: false, onChanged: (value){}), - const Text("Incase of emergency") - ],) - ]), - ), - IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)) - ], - ),):const SizedBox(), - const SizedBox(height: 5,), + const Text(fatherText), + const SizedBox(height: 5,), + Text( + " ${father!.relatedPerson!.firstName} ${father!.relatedPerson!.middleName} ${father!.relatedPerson!.lastName} ${father!.relatedPerson!.nameExtension ?? ''},",style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500),), + Text(" $fullname",style: Theme.of(context).textTheme.bodySmall,), + Row( + children: [ + Checkbox(value: false, onChanged: (value) {}), + const Text(incaseOfEmergency) + ], + ) + ]), + ), + IconButton( + onPressed: () {}, + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + )) + ], + ), + ), + const SizedBox( + height: 5, + ), -// Childrens - children.isNotEmpty?Expanded( - child: ListView( - children: children.map((child) => Container( - decoration: BoxDecoration( - color: Colors.grey[200], - borderRadius: const BorderRadius.all(Radius.circular(12))), - padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), - width: screenWidth, - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text("Children"), - Text("${child.relatedPerson!.firstName} ${child.relatedPerson!.middleName} ${child.relatedPerson!.lastName} ${child.relatedPerson!.nameExtension??''}"), - const Text("Full Name"), - Row(children: [ - Checkbox(value: false, onChanged: (value){}), - const Text("Incase of emergency") - ],) - ]), - ), - IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)) - ], - ),),).toList()), - ):const SizedBox(), + //Mother----------------------------------------------------- + Container( + decoration: box1(), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + width: screenWidth, + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text(motherText), + const SizedBox(height: 5,), + Text( + " ${mother!.relatedPerson!.firstName} ${mother!.relatedPerson!.middleName} ${mother!.relatedPerson!.lastName} ${mother!.relatedPerson!.nameExtension ?? ''}",style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500),), + Text(" $fullname",style: Theme.of(context).textTheme.bodySmall), + Row( + children: [ + Checkbox(value: false, onChanged: (value) {}), + const Text(incaseOfEmergency) + ], + ) + ]), + ), + IconButton( + onPressed: () {}, + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + )) + ], + ), + ), + const SizedBox( + height: 5, + ), + //Spouse --------------------------------------------------------- + spouse != null + ? Container( + decoration: box1(), + padding: + const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + width: screenWidth, + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text(spouseText), + const SizedBox(height: 5,), + Text( + " ${spouse!.relatedPerson!.firstName} ${spouse!.relatedPerson!.middleName} ${spouse!.relatedPerson!.lastName} ${spouse!.relatedPerson!.nameExtension ?? ''}",style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500)), + Text(" $fullname",style: Theme.of(context).textTheme.bodySmall), + Row( + children: [ + Checkbox(value: false, onChanged: (value) {}), + const Text(incaseOfEmergency) + ], + ) + ]), + ), + IconButton( + onPressed: () {}, + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + )) + ], + ), + ) + : const SizedBox(), + const SizedBox( + height: 5, + ), - otherRelated.isNotEmpty?Expanded( - child: ListView( - children: otherRelated.map((relative) => Container( - decoration: BoxDecoration( - color: Colors.grey[200], - borderRadius: const BorderRadius.all(Radius.circular(12))), - padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), - width: screenWidth, - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text("Other related Person(s)"), - Text("${relative.relatedPerson!.firstName} ${relative!.relatedPerson!.middleName} ${relative!.relatedPerson!.lastName} ${relative!.relatedPerson!.nameExtension??''}"), - const Text("Full Name"), - Row(children: [ - Checkbox(value: false, onChanged: (value){}), - const Text("Incase of emergency") - ],) - ]), - ), - IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)) - ], - ),),).toList()), - ):const SizedBox() +// Childrens ---------------------------------- + children.isNotEmpty + ? Container( + decoration: box1(), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: children + .map( + (child){ + int index = children.indexOf(child); + return Container( + + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + width: screenWidth, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + index == 0? const Text(childrenText):const SizedBox(), + const SizedBox( + height: 5, + ), + Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + " ${child.relatedPerson!.firstName} ${child.relatedPerson!.middleName} ${child.relatedPerson!.lastName} ${child.relatedPerson!.nameExtension ?? ''}",style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500)), + Text(" $fullname",style: Theme.of(context).textTheme.bodySmall), + Row( + children: [ + Checkbox( + value: false, + onChanged: (value) {}), + const Text(incaseOfEmergency) + ], + ) + ]), + ), + IconButton( + onPressed: () {}, + icon: const Icon(Icons.more_vert,color: Colors.grey,)) + ], + ), + ], + ), + ); + } + ) + .toList()), + ) + : const SizedBox(), + const SizedBox( + height: 5, + ), +//Other related person + otherRelated.isNotEmpty + ? Container( + decoration: box1(), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: otherRelated + .map( + (relative){ + int index2 = otherRelated.indexOf(relative); + return Container( + + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + width: screenWidth, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + index2 == 0? const Text(otherRelatedText):const SizedBox(), + const SizedBox( + height: 5, + ), + Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + " ${relative.relatedPerson!.firstName} ${relative.relatedPerson!.middleName} ${relative.relatedPerson!.lastName} ${relative.relatedPerson!.nameExtension ?? ''}",style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500)), + Text(" $fullname",style: Theme.of(context).textTheme.bodySmall!), + Row( + children: [ + Checkbox( + value: false, + onChanged: (value) {}), + const Text(incaseOfEmergency) + ], + ) + ]), + ), + IconButton( + onPressed: () {}, + icon: const Icon(Icons.more_vert,color: Colors.grey,)) + ], + ), + ], + ), + ); + } + ) + .toList()), + ) + : const SizedBox(), ]), ); } -} \ No newline at end of file +} diff --git a/lib/screens/profile/components/learning_and_development_screen.dart b/lib/screens/profile/components/learning_and_development_screen.dart index f2f89ed..78e64c9 100644 --- a/lib/screens/profile/components/learning_and_development_screen.dart +++ b/lib/screens/profile/components/learning_and_development_screen.dart @@ -3,8 +3,10 @@ import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter/src/widgets/placeholder.dart'; import 'package:intl/intl.dart'; import 'package:unit2/model/profile/learning_development.dart'; +import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; +import 'package:unit2/utils/text_container.dart'; class LearningAndDevelopmentScreen extends StatefulWidget { final List learningDevelopments; @@ -19,23 +21,24 @@ class _LearningAndDevelopmentScreenState extends State nonAcademicRecognitions; @@ -17,8 +19,9 @@ class _NonAcademicRecognitionScreenState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text("Organization Memberships"), + title: const Text(orgMembershipTitle), backgroundColor: primary, centerTitle: true, ), body: ListView.builder( itemCount: widget.orgMemberships.length, + padding: const EdgeInsets.symmetric(vertical: 8,horizontal: 10), itemBuilder: (BuildContext context, int index) { String entity = widget.orgMemberships[index].agency!.privateEntity == false - ? "Government" - : "Private"; + ? governmentText.toUpperCase() + : privateText.toUpperCase(); String agencyName = widget.orgMemberships[index].agency!.name!; return Column( children: [ Container( width: screenWidth, - decoration: BoxDecoration( - color: Colors.grey[200], - borderRadius: - const BorderRadius.all(Radius.circular(12))), + decoration: box1(), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Row(children: [ @@ -47,14 +47,17 @@ class _OrgMembershipsScreenState extends State { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(entity), - Text(agencyName), + + Text(agencyName,style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500),), + const Divider(), + Text(entity,style: Theme.of(context).textTheme.labelLarge,), ], )), IconButton( - onPressed: () {}, icon: const Icon(Icons.more_vert)) + onPressed: () {}, icon: const Icon(Icons.more_vert,color: Colors.grey,)) ]), - ) + ), + const SizedBox(height: 5,), ], ); }), diff --git a/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart b/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart index 332a02f..b00f398 100644 --- a/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart +++ b/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart @@ -3,6 +3,8 @@ import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter/src/widgets/placeholder.dart'; import 'package:unit2/model/profile/other_information/skills_and_hobbies.dart'; import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/global.dart'; +import 'package:unit2/utils/text_container.dart'; class SkillHobbiesScreen extends StatefulWidget { final ListskillsHobbies; @@ -16,33 +18,35 @@ class _SkillHobbiesScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text("Skills and Hobbies"), + appBar: AppBar(title: const Text(skillAndHobbiesTitle), backgroundColor: primary, centerTitle: true, ), - body: Wrap( - spacing: 5, - runSpacing: 5, - clipBehavior: Clip.none, - verticalDirection: VerticalDirection.down, - children:widget.skillsHobbies.map((SkillsHobbies sh){ - return Badge( - padding: const EdgeInsets.all(8), - alignment: AlignmentDirectional.topStart, - backgroundColor: Colors.grey.shade300, - child: Row( - children: [ - Text(sh.name!), - IconButton( - onPressed: () {}, - icon: const Icon(Icons.close)), - ], - ), - - ); - }).toList() - + body: Padding( + padding: const EdgeInsets.all(16), + child: Wrap( + spacing: 8, + runSpacing: 8, + alignment: WrapAlignment.start, + clipBehavior: Clip.none, + verticalDirection: VerticalDirection.up, + crossAxisAlignment: WrapCrossAlignment.start, + direction: Axis.horizontal, + children:widget.skillsHobbies.map((SkillsHobbies sh){ + return FittedBox( + child: Row( + children: [ + Text(sh.name!), + IconButton( + onPressed: () {}, + icon: const Icon(Icons.close)), + ], + ), + ); + }).toList() + + ), ), ); } diff --git a/lib/screens/profile/components/references_screen.dart b/lib/screens/profile/components/references_screen.dart index 5fb200b..1a4e221 100644 --- a/lib/screens/profile/components/references_screen.dart +++ b/lib/screens/profile/components/references_screen.dart @@ -2,7 +2,9 @@ import 'package:flutter/material.dart'; import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter/src/widgets/placeholder.dart'; import 'package:unit2/model/profile/references.dart'; +import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/text_container.dart'; class ReferencesScreen extends StatefulWidget { final List references; @@ -17,19 +19,18 @@ class _ReferencesScreenState extends State { Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text("Personal References"),centerTitle: true,backgroundColor: primary,), + appBar: AppBar(title: const Text(referencesScreenTitle),centerTitle: true,backgroundColor: primary,), body: ListView.builder( + padding: const EdgeInsets.symmetric(vertical: 8,horizontal: 10), itemCount: widget.references.length, itemBuilder: (BuildContext context, int index){ - String fullname = "${widget.references[0].firstName} ${widget.references[0].middleName} ${widget.references[0].lastName}"; - String addres = "${widget.references[0].address!.cityMunicipality!.description}, ${widget.references[0].address!.cityMunicipality!.province!.description}, ${widget.references[0].address!.cityMunicipality!.province!.region!.description}"; - String mobile = widget.references[0].contactNo.toString(); + String fullname = "${widget.references[index].firstName} ${widget.references[index].middleName} ${widget.references[index].lastName}"; + String addres = "${widget.references[index].address!.cityMunicipality!.description}, ${widget.references[index].address!.cityMunicipality!.province!.description}, ${widget.references[0].address!.cityMunicipality!.province!.region!.description}"; + String mobile = widget.references[index].contactNo.toString(); return Column(children: [ Container( padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), - decoration: BoxDecoration( - color: Colors.grey[200], - borderRadius: const BorderRadius.all(Radius.circular(12))), + decoration:box1(), child: Row( children: [ Expanded( @@ -38,18 +39,19 @@ class _ReferencesScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(fullname,style: Theme.of(context).textTheme.titleLarge!.copyWith(fontWeight: FontWeight.bold)), + Text(fullname,style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500)), + const Divider(), const SizedBox(height: 5,), - Text(addres,style: Theme.of(context).textTheme.labelLarge!.copyWith(fontWeight: FontWeight.bold)), + Text(addres,style: Theme.of(context).textTheme.titleSmall!.copyWith(fontWeight: FontWeight.w500)), const SizedBox(height: 8,), - Text("PHONE / MOBILE NUMBER $mobile",style: Theme.of(context).textTheme.labelSmall!.copyWith(fontWeight: FontWeight.bold)), + Text("${mobileOrPhone.toUpperCase()} : $mobile",style: Theme.of(context).textTheme.labelMedium!), ],), ), - IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)) + IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert,color: Colors.grey,)) ], ), ), - const SizedBox(height: 8,), + const SizedBox(height: 5,), ],); }) , ); diff --git a/lib/screens/profile/voluntary_works.dart b/lib/screens/profile/components/voluntary_works_screen.dart similarity index 70% rename from lib/screens/profile/voluntary_works.dart rename to lib/screens/profile/components/voluntary_works_screen.dart index eb06273..b6e24b7 100644 --- a/lib/screens/profile/voluntary_works.dart +++ b/lib/screens/profile/components/voluntary_works_screen.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; import 'package:intl/intl.dart'; import 'package:unit2/model/profile/voluntary_works.dart'; +import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/text_container.dart'; class VolunataryWorkScreen extends StatefulWidget { final List voluntaryWorks; @@ -18,9 +18,10 @@ class _VolunataryWorkScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text("Volunatary Work & Civic Services"),backgroundColor: primary,), + appBar: AppBar(title: const Text(voluntaryScreenTitle),backgroundColor: primary,), body: ListView.builder( itemCount:widget.voluntaryWorks.length , + padding: const EdgeInsets.symmetric(vertical: 8,horizontal: 10), itemBuilder: (BuildContext context, int index){ String position = widget.voluntaryWorks[index].position!.title!; String agency = widget.voluntaryWorks[index].agency!.name!; @@ -30,9 +31,7 @@ class _VolunataryWorkScreenState extends State { return Column( children: [ Container( - decoration: BoxDecoration( - color: Colors.grey[200], - borderRadius: const BorderRadius.all(Radius.circular(12))), + decoration:box1(), padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), child: Row( children: [ @@ -41,10 +40,14 @@ class _VolunataryWorkScreenState extends State { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(position), - Text(agency), - Text("$from to $to"), - Text("Worked/Involved for: $hours hours"), + Text(position,style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500),), + const SizedBox(height: 5,), + Text(agency,style: Theme.of(context).textTheme.titleSmall,), + const Divider(), + const SizedBox(height: 3,), + Text("$duration : $from to $to"), + const SizedBox(height: 5,), + Text("$numberOfHours : $hours hours"), ]), ), IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)) diff --git a/lib/screens/profile/components/work_history_screen.dart b/lib/screens/profile/components/work_history_screen.dart index 13442bb..47d5c90 100644 --- a/lib/screens/profile/components/work_history_screen.dart +++ b/lib/screens/profile/components/work_history_screen.dart @@ -1,59 +1,71 @@ import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; import 'package:intl/intl.dart'; import 'package:unit2/model/profile/work_history.dart'; +import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/text_container.dart'; import '../../../utils/global.dart'; class WorkHistoryScreen extends StatefulWidget { - final ListworkExperiences; - const WorkHistoryScreen({super.key,required this.workExperiences}); + final List workExperiences; + const WorkHistoryScreen({super.key, required this.workExperiences}); @override State createState() => _WorkHistoryScreenState(); } class _WorkHistoryScreenState extends State { - DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); + DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text("Work History"),backgroundColor: primary,centerTitle: true,), + appBar: AppBar( + title: const Text(workHistoryScreenTitle), + backgroundColor: primary, + centerTitle: true, + ), body: ListView.builder( - itemCount: widget.workExperiences.length, - itemBuilder: (BuildContext context, int index){ - String position = widget.workExperiences[index].position!.title!; - String agency = widget.workExperiences[index].agency!.name!; - String from = dteFormat2.format(widget.workExperiences[index].fromDate!); - String? to = widget.workExperiences[index].toDate == null? "Present" : dteFormat2.format(widget.workExperiences[index].toDate!); - return Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: screenWidth, - decoration: BoxDecoration( - color: Colors.grey[200], - borderRadius: const BorderRadius.all(Radius.circular(12))), - padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 10), + itemCount: widget.workExperiences.length, + itemBuilder: (BuildContext context, int index) { + String position = widget.workExperiences[index].position!.title!; + String agency = widget.workExperiences[index].agency!.name!; + String from = + dteFormat2.format(widget.workExperiences[index].fromDate!); + String? to = widget.workExperiences[index].toDate == null + ? present.toUpperCase() + : dteFormat2.format(widget.workExperiences[index].toDate!); + return Column( + + children: [ + Container( + width: screenWidth, + decoration: box1(), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Row(children: [ - Expanded(child: Column( + Expanded( + child: Column( mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(position), - Text(agency), - Text("$from to $to"), - ],)), - IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)) + Text(position,style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w600),), + const Divider(), + const SizedBox(height: 8,), + Text(agency,style: Theme.of(context).textTheme.titleSmall!.copyWith(fontWeight: FontWeight.w500),), + const SizedBox(height: 5,), + Text("$from to $to",style: Theme.of(context).textTheme.bodyMedium,), + ], + )), + IconButton(onPressed: () {}, icon: const Icon(Icons.more_vert,color: Colors.grey,)) ]), - ), - const SizedBox(height: 5,), - ], - ); + ), + const SizedBox( + height: 5, + ), + ], + ); }), ); } -} \ No newline at end of file +} diff --git a/lib/screens/profile/profile.dart b/lib/screens/profile/profile.dart index 05ca514..ce97d91 100644 --- a/lib/screens/profile/profile.dart +++ b/lib/screens/profile/profile.dart @@ -10,20 +10,20 @@ import 'package:fluttericon/modern_pictograms_icons.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/screens/profile/components/basic_information/address_screen.dart'; import 'package:unit2/screens/profile/components/basic_information/citizenship_screen.dart'; -import 'package:unit2/screens/profile/components/basic_information/contact_information.dart'; -import 'package:unit2/screens/profile/components/basic_information/identification_information.dart'; -import 'package:unit2/screens/profile/components/basic_information/primary_information.dart'; +import 'package:unit2/screens/profile/components/basic_information/contact_information_screen.dart'; +import 'package:unit2/screens/profile/components/basic_information/identification_information_screen.dart'; +import 'package:unit2/screens/profile/components/basic_information/primary_information_screen.dart'; import 'package:unit2/screens/profile/components/education_screen.dart'; -import 'package:unit2/screens/profile/components/eligibility.dart'; +import 'package:unit2/screens/profile/components/eligibility_screen.dart'; import 'package:unit2/screens/profile/components/family_background_screen.dart'; import 'package:unit2/screens/profile/components/learning_and_development_screen.dart'; import 'package:unit2/screens/profile/components/loading_screen.dart'; -import 'package:unit2/screens/profile/components/other_information/non_academic_recognition.dart'; -import 'package:unit2/screens/profile/components/other_information/org_membership.dart'; +import 'package:unit2/screens/profile/components/other_information/non_academic_recognition_screen.dart'; +import 'package:unit2/screens/profile/components/other_information/org_membership_screen.dart'; import 'package:unit2/screens/profile/components/other_information/skills_and_hobbies_screen.dart'; import 'package:unit2/screens/profile/components/references_screen.dart'; import 'package:unit2/screens/profile/components/work_history_screen.dart'; -import 'package:unit2/screens/profile/voluntary_works.dart'; +import 'package:unit2/screens/profile/components/voluntary_works_screen.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import '../../bloc/user/user_bloc.dart'; import 'components/main_menu.dart'; @@ -39,227 +39,227 @@ class ProfileInfo extends StatefulWidget { class _ProfileInfoState extends State { @override Widget build(BuildContext context) { - return SafeArea( - child: Scaffold( - appBar: AppBar( - backgroundColor: primary, - centerTitle: true, - title: const Text('Profile'), - ), - body: ProgressHUD( - child: - BlocBuilder(builder: (context, state) { - if (state is UserLoggedIn) { - return BlocConsumer( - listener: (context, state) { - if (state is ProfileLoading) { - final progress = ProgressHUD.of(context); - progress?.showWithText( - 'Loading Profile', - ); - } - if (state is ProfileLoaded) { - final progress = ProgressHUD.of(context); - progress?.dismiss(); - } - }, - builder: (context, state) { - if (state is ProfileLoaded) { + return Scaffold( + appBar: AppBar( + backgroundColor: primary, + centerTitle: true, + title: const Text('Profile'), + ), + body: ProgressHUD( + child: + BlocBuilder(builder: (context, state) { + if (state is UserLoggedIn) { + return BlocConsumer( + listener: (context, state) { + if (state is ProfileLoading) { + final progress = ProgressHUD.of(context); + progress?.showWithText( + 'Loading Profile', + ); + } + if (state is ProfileLoaded) { + final progress = ProgressHUD.of(context); + progress?.dismiss(); + } + }, + builder: (context, state) { + if (state is ProfileLoaded) { - return Container( - padding: const EdgeInsets.symmetric( - vertical: 12, horizontal: 12), - child: ListView( - children: [ - const Text( - "View and Update your Profile Information"), - ExpandableGroup( - collapsedIcon: - const Icon(Icons.keyboard_arrow_down), - expandedIcon: - const Icon(Icons.keyboard_arrow_up), - header: const ListTile( - leading: Icon( - Elusive.address_book, - color: primary, - ), - title: Text( - "Basic Information", - style: TextStyle( - fontWeight: FontWeight.bold), - ), - ), - items: [ - subMenu(Icons.person, "Primary",(){ - Navigator.push(context,MaterialPageRoute(builder: (BuildContext context){ - return PrimaryInfo(primaryInformation: state.profileInformation.basicInfo.primaryInformation!); - }) ); - }), - subMenu(Icons.home, "Home Addresses",(){ - Navigator.push(context,MaterialPageRoute(builder: (BuildContext context){ - return AddressScreen(addresses: state.profileInformation.basicInfo.addresses); - }) ); - - }), - subMenu( - Icons.contact_mail, "Identifications",(){ - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ - return IdentificationInformations(identities: state.profileInformation.basicInfo.identifications); - })); - }), - subMenu( - Icons.contact_phone, "Contact Info",(){ - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ - return ContactInformation(contacts: state.profileInformation.basicInfo.contactInformation,); - })); - }), - subMenu(Icons.flag, "Citizenships",(){ + return Container( + padding: const EdgeInsets.symmetric( + vertical: 12, horizontal: 12), + child: ListView( + children: [ + Text( + "View and Update your Profile Information",textAlign: TextAlign.center + ,style: Theme.of(context).textTheme.bodyLarge,), + ExpandableGroup( + collapsedIcon: + const Icon(Icons.keyboard_arrow_down), + expandedIcon: + const Icon(Icons.keyboard_arrow_up), + header: const ListTile( + leading: Icon( + Elusive.address_book, + color: primary, + ), + title: Text( + "Basic Information", + style: TextStyle( + fontWeight: FontWeight.bold), + ), + ), + items: [ + subMenu(Icons.person, "Primary",(){ + Navigator.push(context,MaterialPageRoute(builder: (BuildContext context){ + return PrimaryInfo(primaryInformation: state.profileInformation.basicInfo.primaryInformation!); + }) ); + }), + subMenu(Icons.home, "Home Addresses",(){ + Navigator.push(context,MaterialPageRoute(builder: (BuildContext context){ + return AddressScreen(addresses: state.profileInformation.basicInfo.addresses); + }) ); + + }), + subMenu( + Icons.contact_mail, "Identifications",(){ Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ - return CitizenShipScreen(citizenships: state.profileInformation.basicInfo.citizenships,); + return IdentificationsScreen(identities: state.profileInformation.basicInfo.identifications); })); }), - ]), - const Divider(), - MainMenu( - icon: Elusive.group, - title: "Family", - onTap: (){ - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ - return FamilyBackgroundScreen(familyBackground: state.profileInformation.families); - })); - }, - ), - const Divider(), - MainMenu( - icon: FontAwesome5.graduation_cap, - title: "Education", - onTap: (){ - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ - return EducationScreen(educationBackgrounds: state.profileInformation.educationalBackgrounds); - })); - }, - ), - const Divider(), - MainMenu( - icon: Icons.stars, - title: "Eligibility", - onTap: (){ + subMenu( + Icons.contact_phone, "Contact Info",(){ + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + return ContactInformationScreen(contacts: state.profileInformation.basicInfo.contactInformation,); + })); + }), + subMenu(Icons.flag, "Citizenships",(){ Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ - return EligibiltyScreen(eligibilities: state.profileInformation.eligibilities); + return CitizenShipScreen(citizenships: state.profileInformation.basicInfo.citizenships,); })); - }, - ), - const Divider(), - MainMenu( - icon: FontAwesome5.shopping_bag, - title: "Work History", - onTap: (){ - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ - return WorkHistoryScreen(workExperiences: state.profileInformation.workExperiences); - })); - }, - ), - const Divider(), - MainMenu( - icon: FontAwesome5.walking, - title: "Voluntary Work & Civic Services", - onTap: (){ - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ - return VolunataryWorkScreen(voluntaryWorks: state.profileInformation.voluntaryWorks); - })); - }, - ), - const Divider(), - MainMenu( - icon: Elusive.lightbulb, - title: "Learning & Development", - onTap: (){ - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ - return LearningAndDevelopmentScreen(learningDevelopments: state.profileInformation.learningsAndDevelopment); - })); - }, - ), - const Divider(), - MainMenu( - icon: Brandico.codepen, - title: "Personal References", - onTap: (){ - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ - return ReferencesScreen(references: state.profileInformation.references); - })); - }, - ), - ExpandableGroup( - collapsedIcon: - const Icon(Icons.keyboard_arrow_down), - expandedIcon: - const Icon(Icons.keyboard_arrow_up), - header: const ListTile( - leading: Icon( - Icons.info, - color: primary, - ), - title: Text( - "Other Information", - style: TextStyle( - fontWeight: FontWeight.bold), - ), - ), - items: [ - subMenu(Icons.fitness_center, - "Skills & Hobbies",(){ - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ - return SkillHobbiesScreen(skillsHobbies: state.profileInformation.otherInformation.skillsAndHobbies); - })); - }), - subMenu(FontAwesome5.certificate, - "Organization Memberships",(){ - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ - return OrgMembershipsScreen(orgMemberships: state.profileInformation.otherInformation.orgMemberships); - })); - }), - subMenu(Entypo.doc_text, - "Non-Academic Recognitions",(){ - - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ - return NonAcademicRecognitionScreen(nonAcademicRecognitions: state.profileInformation.otherInformation.nonAcademicRecognition); - })); - }), - ]), - ExpandableGroup( - collapsedIcon: - const Icon(Icons.keyboard_arrow_down), - expandedIcon: - const Icon(Icons.keyboard_arrow_up), - header: const ListTile( - leading: Icon( - FontAwesome5.laptop_house, - color: primary, - ), - title: Text( - "Assets", - style: TextStyle( - fontWeight: FontWeight.bold), - ), - ), - items: [ - subMenu(ModernPictograms.home, - "Real Property Tax",(){}), - ]), - ], + }), + ]), + const Divider(), + MainMenu( + icon: Elusive.group, + title: "Family", + onTap: (){ + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + return FamilyBackgroundScreen(familyBackground: state.profileInformation.families); + })); + }, ), - ); - } - if (state is ProfileLoading) { - return const LoadingScreen(); - } + const Divider(), + MainMenu( + icon: FontAwesome5.graduation_cap, + title: "Education", + onTap: (){ + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + return EducationScreen(educationBackgrounds: state.profileInformation.educationalBackgrounds); + })); + }, + ), + const Divider(), + MainMenu( + icon: Icons.stars, + title: "Eligibility", + onTap: (){ + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + return EligibiltyScreen(eligibilities: state.profileInformation.eligibilities); + })); + }, + ), + const Divider(), + MainMenu( + icon: FontAwesome5.shopping_bag, + title: "Work History", + onTap: (){ + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + return WorkHistoryScreen(workExperiences: state.profileInformation.workExperiences); + })); + }, + ), + const Divider(), + MainMenu( + icon: FontAwesome5.walking, + title: "Voluntary Work & Civic Services", + onTap: (){ + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + return VolunataryWorkScreen(voluntaryWorks: state.profileInformation.voluntaryWorks); + })); + }, + ), + const Divider(), + MainMenu( + icon: Elusive.lightbulb, + title: "Learning & Development", + onTap: (){ + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + return LearningAndDevelopmentScreen(learningDevelopments: state.profileInformation.learningsAndDevelopment); + })); + }, + ), + const Divider(), + MainMenu( + icon: Brandico.codepen, + title: "Personal References", + onTap: (){ + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + return ReferencesScreen(references: state.profileInformation.references); + })); + }, + ), + ExpandableGroup( + collapsedIcon: + const Icon(Icons.keyboard_arrow_down), + expandedIcon: + const Icon(Icons.keyboard_arrow_up), + header: const ListTile( + leading: Icon( + Icons.info, + color: primary, + ), + title: Text( + "Other Information", + style: TextStyle( + fontWeight: FontWeight.bold), + ), + ), + items: [ + subMenu(Icons.fitness_center, + "Skills & Hobbies",(){ + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + return SkillHobbiesScreen(skillsHobbies: state.profileInformation.otherInformation.skillsAndHobbies); + })); + }), + subMenu(FontAwesome5.certificate, + "Organization Memberships",(){ + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + return OrgMembershipsScreen(orgMemberships: state.profileInformation.otherInformation.orgMemberships); + })); + }), + subMenu(Entypo.doc_text, + "Non-Academic Recognitions",(){ - return Container(); - }, - ); - } - return Container(); - }), - ))); + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + return NonAcademicRecognitionScreen(nonAcademicRecognitions: state.profileInformation.otherInformation.nonAcademicRecognition); + })); + }), + ]), + ExpandableGroup( + collapsedIcon: + const Icon(Icons.keyboard_arrow_down), + expandedIcon: + const Icon(Icons.keyboard_arrow_up), + header: const ListTile( + leading: Icon( + FontAwesome5.laptop_house, + color: primary, + ), + title: Text( + "Assets", + style: TextStyle( + fontWeight: FontWeight.bold), + ), + ), + items: [ + subMenu(ModernPictograms.home, + "Real Property Tax",(){}), + ]), + ], + ), + ); + } + if (state is ProfileLoading) { + return const LoadingScreen(); + } + + return Container(); + }, + ); + } + return Container(); + }), + )); } } diff --git a/lib/theme-data.dart/box_shadow.dart b/lib/theme-data.dart/box_shadow.dart new file mode 100644 index 0000000..1df168d --- /dev/null +++ b/lib/theme-data.dart/box_shadow.dart @@ -0,0 +1,8 @@ + import 'package:flutter/material.dart'; + +BoxDecoration box1(){ + return const BoxDecoration( + boxShadow: [BoxShadow(color: Colors.black12,spreadRadius: 5,blurRadius: 5)] , + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(3))); +} \ No newline at end of file diff --git a/lib/utils/text_container.dart b/lib/utils/text_container.dart index 8363228..2e72c43 100644 --- a/lib/utils/text_container.dart +++ b/lib/utils/text_container.dart @@ -69,6 +69,50 @@ const String timeoutError = "Internet timeout! Please Check your connection"; const String formatError = "Invalid Error"; const String httpError = "Error getting requested data"; const String onError = "Something went wrong! Please try again."; -// +const String adressScreenTitle = "Addresses"; +const String citizenshipScreenTitle = "Citizenship"; +const String contactScreenTitle = "Contact Information"; +const String identificationScreenTitle = "Identifications"; +const String idNumberText = "ID number"; +const String privateText = 'private'; +const String governmentText = "government"; +const String primaryInformationScreenTitle = "Primary Information"; +const String lastname = "lastname"; +const String firstname = "firstname"; +const String middlename = "middlename"; +const String extensionName = 'extension-name'; +const String birthDat = 'bday'; +const String sex = 'sex'; +const String bloodType = 'bloodtype'; +const String civilStatus = 'civil-status'; +const String gender = 'gender'; +const String height = 'height'; +const String width = 'width'; +const String prefixSuffix = 'prefix&suffix'; +const String nonAcademicRecTitle = "Non Academic Recognition"; +const String orgMembershipTitle = "Organization Membership"; +const String skillAndHobbiesTitle = "Skills and Hobbies"; +const String educationScreenTitle = "Educational Background"; +const String honorsText = 'Honors'; +const String elibilityScreenTitle = "Eligibility"; +const String licenseNumber = "LicenseNumber"; +const String rating = "Rating"; +const String familyBackgroundScreenTitle = "Family Background"; +const String fatherText = "Father"; +const String fullname = "Full name"; +const String incaseOfEmergency = "Incase of emergency"; +const String motherText = 'Mother'; +const String spouseText = "Spouse"; +const String childrenText = "Children"; +const String otherRelatedText = "Other related person"; +const String learningAndDevelopmentScreenTitle = "Learning and Development"; +const String duration = "Duration"; +const String type = "Type"; +const String referencesScreenTitle = "Personal References"; +const String mobileOrPhone = "phone / mobile number"; +const String voluntaryScreenTitle = "Voluntary Work & Civic Services"; +const String numberOfHours = "Worked/Involved for"; +const String workHistoryScreenTitle = "Work History"; +const String present = "present"; // From 98991c0ef6a0fb9e7c541117fd12fe373afe7f5d Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Fri, 10 Feb 2023 10:02:35 +0800 Subject: [PATCH 30/86] add login invalid credentials error and logout funcationality --- lib/bloc/user/user_bloc.dart | 26 ++++++++++++++----- lib/bloc/user/user_state.dart | 12 +++++++++ .../homepage.dart/components/menu-screen.dart | 2 +- lib/screens/unit2/login/login.dart | 23 ++++++++++++++++ lib/utils/urls.dart | 4 +-- 5 files changed, 58 insertions(+), 9 deletions(-) diff --git a/lib/bloc/user/user_bloc.dart b/lib/bloc/user/user_bloc.dart index 71dd9eb..104b7dd 100644 --- a/lib/bloc/user/user_bloc.dart +++ b/lib/bloc/user/user_bloc.dart @@ -1,3 +1,6 @@ +import 'dart:async'; +import 'dart:io'; + import 'package:barcode_scan2/barcode_scan2.dart'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; @@ -7,6 +10,7 @@ import 'package:unit2/screens/unit2/login/functions/get_app_version.dart'; import 'package:unit2/sevices/login_service/auth_service.dart'; import '../../utils/scanner.dart'; +import '../../utils/text_container.dart'; part 'user_event.dart'; part 'user_state.dart'; @@ -14,6 +18,7 @@ part 'user_state.dart'; class UserBloc extends Bloc { UserData? _userData; VersionInfo? _versionInfo; + String? _apkVersion; UserBloc() : super(UserInitial()) { // this event is called when opening the app to check if // there is new app version @@ -23,7 +28,8 @@ class UserBloc extends Bloc { VersionInfo versionInfo = await AuthService.instance.getVersionInfo(); _versionInfo = versionInfo; String apkVersion = await getAppVersion(); - emit(VersionLoaded(versionInfo: _versionInfo,apkVersion: apkVersion)); + _apkVersion = apkVersion; + emit(VersionLoaded(versionInfo: _versionInfo,apkVersion: _apkVersion)); } catch (e) { emit(UserError( message: e.toString(), @@ -31,7 +37,7 @@ class UserBloc extends Bloc { } }); on((event, emit) { - emit(VersionLoaded(versionInfo: _versionInfo)); + emit(VersionLoaded(versionInfo: _versionInfo,apkVersion: _apkVersion)); }); on((event, emit) async { try { @@ -39,8 +45,12 @@ class UserBloc extends Bloc { .webLogin(username: event.username, password: event.password); _userData = userData; emit(UserLoggedIn(userData: _userData)); - } catch (e) { - emit(UserError(message: e.toString())); + } on TimeoutException catch (_) { + emit(InternetTimeout(message: timeoutError)); + }on SocketException catch (_){ + emit(InternetTimeout(message:timeoutError)); + }on Error catch(_){ + emit(InvalidCredentials(message: "Invalid username or password")); } }); on((event, emit) async { @@ -49,8 +59,12 @@ class UserBloc extends Bloc { .qrLogin(uuid: event.uuid, password: event.password); _userData = userData; emit(UserLoggedIn(userData: _userData)); - } catch (e) { - emit(UserError(message: e.toString())); + } on TimeoutException catch (_) { + emit(InternetTimeout(message: timeoutError)); + }on SocketException catch (_){ + emit(InternetTimeout(message:timeoutError)); + }on Error catch(_){ + emit(InvalidCredentials(message: "Invalid username or password")); } }); on((event, emit) { diff --git a/lib/bloc/user/user_state.dart b/lib/bloc/user/user_state.dart index 551c9e2..372f052 100644 --- a/lib/bloc/user/user_state.dart +++ b/lib/bloc/user/user_state.dart @@ -48,3 +48,15 @@ class UuidLoaded extends UserState{ @override List get props => [uuid]; } + +class InternetTimeout extends UserState{ + final String message; + InternetTimeout({required this.message}); + @override + List get props => [message]; +} + +class InvalidCredentials extends UserState{ + final String message ; + InvalidCredentials ({required this.message}); +} diff --git a/lib/screens/unit2/homepage.dart/components/menu-screen.dart b/lib/screens/unit2/homepage.dart/components/menu-screen.dart index b06a8de..312d2d0 100644 --- a/lib/screens/unit2/homepage.dart/components/menu-screen.dart +++ b/lib/screens/unit2/homepage.dart/components/menu-screen.dart @@ -62,7 +62,7 @@ class _MenuScreenState extends State { Expanded( child: Align( alignment: FractionalOffset.bottomLeft, - child: getTile(WebSymbols.logout, "Logout", 'login', context, + child: getTile(WebSymbols.logout, "Logout", '/', context, widget.userData!), )), ], diff --git a/lib/screens/unit2/login/login.dart b/lib/screens/unit2/login/login.dart index ec4cb21..7b6fbf5 100644 --- a/lib/screens/unit2/login/login.dart +++ b/lib/screens/unit2/login/login.dart @@ -7,6 +7,7 @@ import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:unit2/screens/unit2/login/components/update_required.dart'; +import 'package:unit2/utils/alerts.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/error_state.dart'; import '../../../bloc/user/user_bloc.dart'; @@ -43,8 +44,24 @@ class _UniT2LoginState extends State { Navigator.pushReplacementNamed(context, '/module-screen'); } if (state is UuidLoaded) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); Navigator.pushNamed(context, '/qr-login'); } + if (state is UserError) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + if (state is InternetTimeout) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + if (state is InvalidCredentials) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + errorAlert(context, "Error Login", state.message); + context.read().add(LoadVersion()); + } }, builder: (context, state) { if (state is VersionLoaded) { return Builder(builder: (context) { @@ -293,6 +310,7 @@ class _UniT2LoginState extends State { ), ); } else { + //New update available return Update( apkVersion: state.apkVersion!, currenVersion: state.versionInfo!.version!, @@ -305,6 +323,11 @@ class _UniT2LoginState extends State { message: state.message, ); } + if (state is InternetTimeout) { + return ErrorState( + message: state.message, + ); + } if (state is SplashScreen) { return const UniTSplashScreen(); } diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index 525cc13..cee712a 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -4,8 +4,8 @@ class Url { String host() { // return '192.168.10.221:3003'; - return 'agusandelnorte.gov.ph'; - // return 'devweb.agusandelnorte.gov.ph'; + // return 'agusandelnorte.gov.ph'; + return 'devweb.agusandelnorte.gov.ph'; } String authentication() { From 0fb2ca49fa5cb1530800b67c697431928555353e Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Fri, 10 Feb 2023 15:39:00 +0800 Subject: [PATCH 31/86] refactor models and add empty response to profile screens --- lib/bloc/profile/profile_bloc.dart | 3 - .../basic_information/citizenship.dart | 6 - .../contact_information.dart | 10 +- .../identification_information.dart | 18 +-- lib/model/profile/eligibility.dart | 16 +-- .../non_acedimic_recognition.dart | 2 +- .../organization_memberships.dart | 2 +- lib/model/profile/references.dart | 4 +- lib/model/profile/voluntary_works.dart | 4 +- lib/model/profile/work_history.dart | 2 +- .../basic_information/address_screen.dart | 29 ++--- .../contact_information_screen.dart | 26 ++-- .../identification_information_screen.dart | 26 ++-- .../profile/components/education_screen.dart | 34 +++-- .../components/eligibility_screen.dart | 25 ++-- .../components/family_background_screen.dart | 6 +- .../learning_and_development_screen.dart | 29 +++-- .../non_academic_recognition_screen.dart | 21 ++-- .../org_membership_screen.dart | 22 ++-- .../skills_and_hobbies_screen.dart | 18 ++- .../profile/components/references_screen.dart | 113 ++++++++++------- .../components/voluntary_works_screen.dart | 116 ++++++++++-------- .../components/work_history_screen.dart | 28 ++--- lib/screens/unit2/login/login.dart | 10 +- lib/screens/unit2/login/qr_login.dart | 7 +- lib/utils/internet_time_out.dart | 54 ++++++++ lib/widgets/add_leading.dart | 13 ++ lib/widgets/empty_data.dart | 39 ++++++ lib/widgets/error_state.dart | 57 ++++++++- 29 files changed, 452 insertions(+), 288 deletions(-) create mode 100644 lib/utils/internet_time_out.dart create mode 100644 lib/widgets/add_leading.dart create mode 100644 lib/widgets/empty_data.dart diff --git a/lib/bloc/profile/profile_bloc.dart b/lib/bloc/profile/profile_bloc.dart index c778ab6..8368e9b 100644 --- a/lib/bloc/profile/profile_bloc.dart +++ b/lib/bloc/profile/profile_bloc.dart @@ -5,9 +5,6 @@ import 'package:unit2/model/profile/basic_information/primary-information.dart'; import 'package:unit2/model/profile/profileInfomation.dart'; import 'package:unit2/sevices/profile/profile_service.dart'; -import '../../model/profile/basic_info.dart'; -import '../../model/profile/basic_info.dart'; - part 'profile_event.dart'; part 'profile_state.dart'; diff --git a/lib/model/profile/basic_information/citizenship.dart b/lib/model/profile/basic_information/citizenship.dart index cddc21e..397e12d 100644 --- a/lib/model/profile/basic_information/citizenship.dart +++ b/lib/model/profile/basic_information/citizenship.dart @@ -1,9 +1,3 @@ -// To parse this JSON data, do -// -// final citizenship = citizenshipFromJson(jsonString); - -import 'package:meta/meta.dart'; -import 'dart:convert'; class Citizenship { Citizenship({ diff --git a/lib/model/profile/basic_information/contact_information.dart b/lib/model/profile/basic_information/contact_information.dart index e7f9d7a..729e7f5 100644 --- a/lib/model/profile/basic_information/contact_information.dart +++ b/lib/model/profile/basic_information/contact_information.dart @@ -48,8 +48,8 @@ class CommService { factory CommService.fromJson(Map json) => CommService( id: json["id"], - serviceType: ServiceType.fromJson(json["service_type"]), - serviceProvider: ServiceProvider.fromJson(json["service_provider"]), + serviceType: json["service_type"]==null?null: ServiceType.fromJson(json["service_type"]), + serviceProvider: json["service_provider"] == null?null: ServiceProvider.fromJson(json["service_provider"]), ); Map toJson() => { @@ -73,7 +73,7 @@ class ServiceProvider { factory ServiceProvider.fromJson(Map json) => ServiceProvider( id: json["id"], alias: json["alias"], - agency: Agency.fromJson(json["agency"]), + agency: json["agency"] == null? null: Agency.fromJson(json["agency"]), ); Map toJson() => { @@ -99,7 +99,7 @@ class Agency { factory Agency.fromJson(Map json) => Agency( id: json["id"], name: json["name"], - category: Category.fromJson(json["category"]), + category: json["category"] == null? null : Category.fromJson(json["category"]), privateEntity: json["private_entity"], ); @@ -125,7 +125,7 @@ class Category { factory Category.fromJson(Map json) => Category( id: json["id"], name: json["name"], - industryClass: IndustryClass.fromJson(json["industry_class"]), + industryClass: json["industry_class"] == null? null: IndustryClass.fromJson(json["industry_class"]), ); Map toJson() => { diff --git a/lib/model/profile/basic_information/identification_information.dart b/lib/model/profile/basic_information/identification_information.dart index 09e20fe..f0ee066 100644 --- a/lib/model/profile/basic_information/identification_information.dart +++ b/lib/model/profile/basic_information/identification_information.dart @@ -25,8 +25,8 @@ class Identification { factory Identification.fromJson(Map json) => Identification( id: json["id"], - agency: Agency.fromJson(json["agency"]), - issuedAt: IssuedAt.fromJson(json["issued_at"]), + agency:json["agency"] == null? null: Agency.fromJson(json["agency"]), + issuedAt: json["issued_at"] == null? null:IssuedAt.fromJson(json["issued_at"]), dateIssued:json["date_issued"]==null?null:DateTime.parse(json["date_issued"]), expirationDate:json["expiration_date"]==null?null:DateTime.parse(json["expiration_date"]), asPdfReference: json["as_pdf_reference"], @@ -60,7 +60,7 @@ class Agency { factory Agency.fromJson(Map json) => Agency( id: json["id"], name: json["name"], - category: Category.fromJson(json["category"]), + category: json["category"] == null? null: Category.fromJson(json["category"]), privateEntity: json["private_entity"], ); @@ -86,7 +86,7 @@ class Category { factory Category.fromJson(Map json) => Category( id: json["id"], name: json["name"], - industryClass: IndustryClass.fromJson(json["industry_class"]), + industryClass:json['industry_class'] == null? null: IndustryClass.fromJson(json["industry_class"]), ); Map toJson() => { @@ -140,10 +140,10 @@ class IssuedAt { factory IssuedAt.fromJson(Map json) => IssuedAt( id: json["id"], issuedAtClass: json["class"], - country: Country.fromJson(json["country"]), + country: json['country'] == null? null: Country.fromJson(json["country"]), barangay: json["barangay"], - addressCategory: AddressCategory.fromJson(json["address_category"]), - cityMunicipality: CityMunicipality.fromJson(json["city_municipality"]), + addressCategory: json["address_category"] == null? null: AddressCategory.fromJson(json["address_category"]), + cityMunicipality:json["city_municipality"] == null? null: CityMunicipality.fromJson(json["city_municipality"]), ); Map toJson() => { @@ -198,7 +198,7 @@ class CityMunicipality { factory CityMunicipality.fromJson(Map json) => CityMunicipality( code: json["code"], zipcode: json["zipcode"], - province: Province.fromJson(json["province"]), + province:json["province"] == null? null : Province.fromJson(json["province"]), psgcCode: json["psgc_code"], description: json["description"], ); @@ -229,7 +229,7 @@ class Province { factory Province.fromJson(Map json) => Province( code: json["code"], - region: Region.fromJson(json["region"]), + region: json["region"] == null? null:Region.fromJson(json["region"]), psgcCode: json["psgc_code"], shortname: json["shortname"], description: json["description"], diff --git a/lib/model/profile/eligibility.dart b/lib/model/profile/eligibility.dart index b67a775..5999cf4 100644 --- a/lib/model/profile/eligibility.dart +++ b/lib/model/profile/eligibility.dart @@ -33,10 +33,10 @@ class EligibityCert { factory EligibityCert.fromJson(Map json) => EligibityCert( id: json["id"], rating: json["rating"]?.toDouble(), - examDate: DateTime.parse(json["exam_date"]), + examDate: json['exam_date'] == null? null: DateTime.parse(json["exam_date"]), attachments: null, - eligibility: Eligibility.fromJson(json["eligibility"]), - examAddress: ExamAddress.fromJson(json["exam_address"]), + eligibility: json['eligibility'] == null?null: Eligibility.fromJson(json["eligibility"]), + examAddress: json['eligibilty'] == null? null: ExamAddress.fromJson(json["exam_address"]), validityDate: json["validity_date"], licenseNumber: json["license_number"], ); @@ -97,10 +97,10 @@ class ExamAddress { factory ExamAddress.fromJson(Map json) => ExamAddress( id: json["id"], examAddressClass: json["class"], - country: Country.fromJson(json["country"]), + country:json["country"] == null? null: Country.fromJson(json["country"]), barangay: json["barangay"], - addressCategory: AddressCategory.fromJson(json["address_category"]), - cityMunicipality: CityMunicipality.fromJson(json["city_municipality"]), + addressCategory: json["address_category"] == null?null: AddressCategory.fromJson(json["address_category"]), + cityMunicipality: json["city_municipality"]==null? null: CityMunicipality.fromJson(json["city_municipality"]), ); Map toJson() => { @@ -155,7 +155,7 @@ class CityMunicipality { factory CityMunicipality.fromJson(Map json) => CityMunicipality( code: json["code"], zipcode: json["zipcode"], - province: Province.fromJson(json["province"]), + province: json["province"]== null? null: Province.fromJson(json["province"]), psgcCode: json["psgc_code"], description: json["description"], ); @@ -186,7 +186,7 @@ class Province { factory Province.fromJson(Map json) => Province( code: json["code"], - region: Region.fromJson(json["region"]), + region:json["region"] == null? null: Region.fromJson(json["region"]), psgcCode: json["psgc_code"], shortname: json["shortname"], description: json["description"], diff --git a/lib/model/profile/other_information/non_acedimic_recognition.dart b/lib/model/profile/other_information/non_acedimic_recognition.dart index 119b762..0697a66 100644 --- a/lib/model/profile/other_information/non_acedimic_recognition.dart +++ b/lib/model/profile/other_information/non_acedimic_recognition.dart @@ -93,7 +93,7 @@ class IndustryClass { final int? id; final String? name; - final dynamic description; + final String? description; factory IndustryClass.fromJson(Map json) => IndustryClass( id: json["id"], diff --git a/lib/model/profile/other_information/organization_memberships.dart b/lib/model/profile/other_information/organization_memberships.dart index 7776380..150f54f 100644 --- a/lib/model/profile/other_information/organization_memberships.dart +++ b/lib/model/profile/other_information/organization_memberships.dart @@ -85,7 +85,7 @@ class IndustryClass { final int? id; final String? name; - final dynamic description; + final String? description; factory IndustryClass.fromJson(Map json) => IndustryClass( id: json["id"], diff --git a/lib/model/profile/references.dart b/lib/model/profile/references.dart index b353cec..9fda1eb 100644 --- a/lib/model/profile/references.dart +++ b/lib/model/profile/references.dart @@ -139,7 +139,7 @@ class CityMunicipality { factory CityMunicipality.fromJson(Map json) => CityMunicipality( code: json["code"], zipcode: json["zipcode"], - province: Province.fromJson(json["province"]), + province: json["province"] == null? null : Province.fromJson(json["province"]), psgcCode: json["psgc_code"], description: json["description"], ); @@ -170,7 +170,7 @@ class Province { factory Province.fromJson(Map json) => Province( code: json["code"], - region: Region.fromJson(json["region"]), + region: json["region"] == null? null : Region.fromJson(json["region"]), psgcCode: json["psgc_code"], shortname: json["shortname"], description: json["description"], diff --git a/lib/model/profile/voluntary_works.dart b/lib/model/profile/voluntary_works.dart index 4938116..267fb8d 100644 --- a/lib/model/profile/voluntary_works.dart +++ b/lib/model/profile/voluntary_works.dart @@ -28,7 +28,7 @@ class VoluntaryWork { factory VoluntaryWork.fromJson(Map json) => VoluntaryWork( agency: json["agency"] == null ? null : Agency.fromJson(json["agency"]), address: json["address"] == null ? null : Address.fromJson(json["address"]), - toDate: json["to_date"], + toDate: json["to_date"] == null? null : DateTime.parse(json['to_data']), position: json["position"] == null ? null : Position.fromJson(json["position"]), fromDate: json["from_date"] == null ? null : DateTime.parse(json["from_date"]), totalHours: json["total_hours"], @@ -277,7 +277,7 @@ class IndustryClass { final int? id; final String? name; - final dynamic description; + final String? description; factory IndustryClass.fromJson(Map json) => IndustryClass( id: json["id"], diff --git a/lib/model/profile/work_history.dart b/lib/model/profile/work_history.dart index 3fbd78d..7a1ff98 100644 --- a/lib/model/profile/work_history.dart +++ b/lib/model/profile/work_history.dart @@ -121,7 +121,7 @@ class IndustryClass { final int? id; final String? name; - final dynamic description; + final String? description; factory IndustryClass.fromJson(Map json) => IndustryClass( id: json["id"], diff --git a/lib/screens/profile/components/basic_information/address_screen.dart b/lib/screens/profile/components/basic_information/address_screen.dart index fecfa17..ffb8e61 100644 --- a/lib/screens/profile/components/basic_information/address_screen.dart +++ b/lib/screens/profile/components/basic_information/address_screen.dart @@ -4,32 +4,29 @@ import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; +import 'package:unit2/widgets/add_leading.dart'; +import 'package:unit2/widgets/empty_data.dart'; -class AddressScreen extends StatefulWidget { +class AddressScreen extends StatelessWidget { final List addresses; const AddressScreen({super.key, required this.addresses}); - @override - State createState() => _AddressScreenState(); -} - -class _AddressScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text(adressScreenTitle),centerTitle: true, backgroundColor: primary,), - body: ListView.builder( + appBar: AppBar(title: const Text(adressScreenTitle),centerTitle: true, backgroundColor: primary, actions: [AddLeading(onPressed: (){})],), + body: addresses.isNotEmpty ? ListView.builder( padding: const EdgeInsets.symmetric(vertical: 8,horizontal: 10), - itemCount: widget.addresses.length, + itemCount: addresses.length, itemBuilder: ( BuildContext context, int index){ - String? subdivision = widget.addresses[index].details??''; - String category = widget.addresses[index].address!.category!.name!; - String? barangay = widget.addresses[index].address!.barangay != null?'${widget.addresses[index].address!.barangay!.description!.toUpperCase()},':''; - String cityMunicipality = widget.addresses[index].address!.cityMunicipality!.description!; - String province = widget.addresses[index].address!.cityMunicipality!.province!.description!; - String region = widget.addresses[index].address!.cityMunicipality!.province!.region!.description!; + String? subdivision = addresses[index].details??''; + String category = addresses[index].address!.category!.name!; + String? barangay = addresses[index].address!.barangay != null?'${addresses[index].address!.barangay!.description!.toUpperCase()},':''; + String cityMunicipality = addresses[index].address!.cityMunicipality!.description!; + String province = addresses[index].address!.cityMunicipality!.province!.description!; + String region = addresses[index].address!.cityMunicipality!.province!.region!.description!; return Column(children: [ Column( @@ -54,7 +51,7 @@ class _AddressScreenState extends State { ), const SizedBox(height: 5,), ],); - }), + }):const EmptyData(message: "You don't have address added. Please click + to add."), ); } } \ No newline at end of file diff --git a/lib/screens/profile/components/basic_information/contact_information_screen.dart b/lib/screens/profile/components/basic_information/contact_information_screen.dart index e8055e6..f89c626 100644 --- a/lib/screens/profile/components/basic_information/contact_information_screen.dart +++ b/lib/screens/profile/components/basic_information/contact_information_screen.dart @@ -3,16 +3,13 @@ import 'package:unit2/model/profile/basic_information/contact_information.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; +import 'package:unit2/widgets/add_leading.dart'; +import 'package:unit2/widgets/empty_data.dart'; -class ContactInformationScreen extends StatefulWidget { +class ContactInformationScreen extends StatelessWidget { final List contacts; const ContactInformationScreen({super.key, required this.contacts}); - @override - State createState() => _ContactInformationScreenState(); -} - -class _ContactInformationScreenState extends State { @override Widget build(BuildContext context) { return SafeArea( @@ -21,13 +18,14 @@ class _ContactInformationScreenState extends State { title: const Text(contactScreenTitle), centerTitle: true, backgroundColor: primary, + actions: [AddLeading(onPressed: (){})], ), - body: ListView.builder( + body: contacts.isEmpty? ListView.builder( padding: const EdgeInsets.symmetric(vertical: 8,horizontal: 10), - itemCount: widget.contacts.length, + itemCount: contacts.length, itemBuilder: (BuildContext context, int index) { - String numberMail = widget.contacts[index].numbermail!; - String commService = widget.contacts[index].commService!.serviceProvider!.alias!; + String numberMail = contacts[index].numbermail!; + String commService = contacts[index].commService!.serviceProvider!.alias!; return Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, @@ -52,13 +50,13 @@ class _ContactInformationScreenState extends State { .toString(),style: Theme.of(context).textTheme.titleSmall,), ), - widget.contacts[index].active==true? const Badge(backgroundColor: Colors.green, label: Text("Active",),):const SizedBox(), + contacts[index].active==true? const Badge(backgroundColor: Colors.green, label: Text("Active",),):const SizedBox(), const SizedBox(width: 5), - widget.contacts[index].primary==true? const Badge(backgroundColor: Colors.blue, label: Text("Primary"),):const SizedBox() + contacts[index].primary==true? const Badge(backgroundColor: Colors.blue, label: Text("Primary"),):const SizedBox() ], ), const SizedBox(height: 5,), - Text(widget.contacts[index].commService! + Text(contacts[index].commService! .serviceProvider!.agency!.name .toString()), @@ -74,7 +72,7 @@ class _ContactInformationScreenState extends State { ], ); - }), + }):const EmptyData(message: "You don't have contact information added. Please click + to add"), ), ); } diff --git a/lib/screens/profile/components/basic_information/identification_information_screen.dart b/lib/screens/profile/components/basic_information/identification_information_screen.dart index e531d2a..a3584da 100644 --- a/lib/screens/profile/components/basic_information/identification_information_screen.dart +++ b/lib/screens/profile/components/basic_information/identification_information_screen.dart @@ -4,18 +4,13 @@ import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; +import 'package:unit2/widgets/add_leading.dart'; +import 'package:unit2/widgets/empty_data.dart'; -class IdentificationsScreen extends StatefulWidget { +class IdentificationsScreen extends StatelessWidget { final List identities; const IdentificationsScreen({super.key, required this.identities}); - @override - State createState() => - _IdentificationsScreenState(); -} - -class _IdentificationsScreenState - extends State { @override Widget build(BuildContext context) { return Scaffold( @@ -23,15 +18,16 @@ class _IdentificationsScreenState title: const Text(identificationScreenTitle), centerTitle: true, backgroundColor: primary, + actions: [AddLeading(onPressed: (){})], ), - body: ListView.builder( + body:identities.isNotEmpty? ListView.builder( padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 10), - itemCount: widget.identities.length, + itemCount: identities.length, itemBuilder: (BuildContext context, int index) { - String agency = widget.identities[index].agency!.name!; - String idNumber = widget.identities[index].identificationNumber!; - bool government = widget.identities[index].agency!.privateEntity!; - String issuedAt = "${widget.identities[index].issuedAt!.cityMunicipality!.description!} ${widget.identities[index].issuedAt!.cityMunicipality!.province!.description}"; + String agency = identities[index].agency!.name!; + String idNumber = identities[index].identificationNumber!; + bool government = identities[index].agency!.privateEntity!; + String issuedAt = "${identities[index].issuedAt!.cityMunicipality!.description!} ${identities[index].issuedAt!.cityMunicipality!.province!.description}"; return Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, @@ -84,7 +80,7 @@ class _IdentificationsScreenState ), ], ); - }), + }):const EmptyData(message: "You don't have identifications added. Please click + to add."), ); } } diff --git a/lib/screens/profile/components/education_screen.dart b/lib/screens/profile/components/education_screen.dart index de6f09f..99af383 100644 --- a/lib/screens/profile/components/education_screen.dart +++ b/lib/screens/profile/components/education_screen.dart @@ -4,16 +4,13 @@ import 'package:unit2/model/profile/educational_background.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; +import 'package:unit2/widgets/add_leading.dart'; +import 'package:unit2/widgets/empty_data.dart'; -class EducationScreen extends StatefulWidget { +class EducationScreen extends StatelessWidget { final List educationBackgrounds; const EducationScreen({super.key, required this.educationBackgrounds}); - @override - State createState() => _EducationScreenState(); -} - -class _EducationScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( @@ -21,23 +18,24 @@ class _EducationScreenState extends State { title: const Text(educationScreenTitle), centerTitle: true, backgroundColor: primary, + actions: [AddLeading(onPressed: (){})], ), - body: ListView.builder( + body: educationBackgrounds.isNotEmpty ? ListView.builder( padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 10), - itemCount: widget.educationBackgrounds.length, + itemCount: educationBackgrounds.length, itemBuilder: (BuildContext context, int index) { - String level = widget.educationBackgrounds[index].education!.level!; - String periodFrom = widget.educationBackgrounds[index].periodFrom!; - String periodTo = widget.educationBackgrounds[index].periodTo!; + String level = educationBackgrounds[index].education!.level!; + String periodFrom = educationBackgrounds[index].periodFrom!; + String periodTo = educationBackgrounds[index].periodTo!; String? program = - widget.educationBackgrounds[index].education!.course == null + educationBackgrounds[index].education!.course == null ? null - : widget.educationBackgrounds[index].education!.course! + : educationBackgrounds[index].education!.course! .program!; List? honors = - widget.educationBackgrounds[index].honors!.toList(); + educationBackgrounds[index].honors!.toList(); String school = - widget.educationBackgrounds[index].education!.school!.name!; + educationBackgrounds[index].education!.school!.name!; return Column( children: [ Container( @@ -81,13 +79,13 @@ class _EducationScreenState extends State { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text("$honorsText : "), + const Text("$honorsText : ",style: TextStyle(fontWeight: FontWeight.w600),), Column( children: honors .map((Honor honor) => - Text(" ${honor.name!}")) + Text(" - ${honor.name!}")) .toList(), ), ], @@ -123,7 +121,7 @@ class _EducationScreenState extends State { ), ], ); - }), + }):const EmptyData(message: "You don't have any Educational Background added. Please click + to add."), ); } } diff --git a/lib/screens/profile/components/eligibility_screen.dart b/lib/screens/profile/components/eligibility_screen.dart index 8eb7b5d..6f5148f 100644 --- a/lib/screens/profile/components/eligibility_screen.dart +++ b/lib/screens/profile/components/eligibility_screen.dart @@ -6,16 +6,13 @@ import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; +import 'package:unit2/widgets/add_leading.dart'; +import 'package:unit2/widgets/empty_data.dart'; -class EligibiltyScreen extends StatefulWidget { +class EligibiltyScreen extends StatelessWidget { final List eligibilities; const EligibiltyScreen({super.key, required this.eligibilities}); - @override - State createState() => _EligibiltyScreenState(); -} - -class _EligibiltyScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( @@ -23,12 +20,13 @@ class _EligibiltyScreenState extends State { title: const Text(elibilityScreenTitle), centerTitle: true, backgroundColor: primary, + actions: [AddLeading(onPressed: (){})], ), - body: ListView.builder( + body: eligibilities.isNotEmpty? ListView.builder( padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 10), - itemCount: widget.eligibilities.length, + itemCount: eligibilities.length, itemBuilder: (BuildContext context, int index) { - String title = widget.eligibilities[index].eligibility!.title!; + String title = eligibilities[index].eligibility!.title!; return Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, @@ -51,20 +49,21 @@ class _EligibiltyScreenState extends State { const Divider(), const SizedBox(height: 5,), Text( - "$licenseNumber: ${widget.eligibilities[index].licenseNumber == null ? 'N/A' : widget.eligibilities[index].licenseNumber.toString()}",style: Theme.of(context).textTheme.titleSmall), + "$licenseNumber: ${eligibilities[index].licenseNumber == null ? 'N/A' : eligibilities[index].licenseNumber.toString()}",style: Theme.of(context).textTheme.titleSmall), const SizedBox(height: 3,), Text( - "$rating : ${widget.eligibilities[index].rating}.",style: Theme.of(context).textTheme.titleSmall) + "$rating : ${eligibilities[index].rating}.",style: Theme.of(context).textTheme.titleSmall) ]), ), IconButton( onPressed: () {}, icon: const Icon(Icons.more_vert,color: Colors.grey,)) ], ), - ) + ), + const SizedBox(height: 5,) ], ); - }), + }):const EmptyData(message: "You don't have any Eligibility added. Please click + to add."), ); } } diff --git a/lib/screens/profile/components/family_background_screen.dart b/lib/screens/profile/components/family_background_screen.dart index 393b2f9..2592010 100644 --- a/lib/screens/profile/components/family_background_screen.dart +++ b/lib/screens/profile/components/family_background_screen.dart @@ -71,7 +71,11 @@ class _FamilyBackgroundScreenState extends State { Text(" $fullname",style: Theme.of(context).textTheme.bodySmall,), Row( children: [ - Checkbox(value: false, onChanged: (value) {}), + Checkbox(value: false, onChanged: (value) { + setState(() { + value = !value!; + }); + }), const Text(incaseOfEmergency) ], ) diff --git a/lib/screens/profile/components/learning_and_development_screen.dart b/lib/screens/profile/components/learning_and_development_screen.dart index 78e64c9..5b53706 100644 --- a/lib/screens/profile/components/learning_and_development_screen.dart +++ b/lib/screens/profile/components/learning_and_development_screen.dart @@ -7,34 +7,33 @@ import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; +import 'package:unit2/widgets/add_leading.dart'; +import 'package:unit2/widgets/empty_data.dart'; -class LearningAndDevelopmentScreen extends StatefulWidget { +class LearningAndDevelopmentScreen extends StatelessWidget { final List learningDevelopments; const LearningAndDevelopmentScreen({super.key, required this.learningDevelopments}); - @override - State createState() => _LearningAndDevelopmentScreenState(); -} + -class _LearningAndDevelopmentScreenState extends State { - DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); @override Widget build(BuildContext context) { + DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); return Scaffold( appBar: AppBar(title: const Text(learningAndDevelopmentScreenTitle), centerTitle: true, backgroundColor: primary, - + actions: [AddLeading(onPressed: (){})], ), - body: ListView.builder( + body: learningDevelopments.isNotEmpty? ListView.builder( padding: const EdgeInsets.symmetric(vertical: 8,horizontal: 10), - itemCount: widget.learningDevelopments.length, + itemCount: learningDevelopments.length, itemBuilder: (BuildContext context, int index){ - String training = widget.learningDevelopments[index].conductedTraining!.title!.title!; - String provider = widget.learningDevelopments[index].conductedTraining!.conductedBy!.name!; - String start = dteFormat2.format(widget.learningDevelopments[index].conductedTraining!.fromDate!); - String end = dteFormat2.format(widget.learningDevelopments[index].conductedTraining!.toDate!); - String type = widget.learningDevelopments[index].conductedTraining!.learningDevelopmentType!.title!; + String training = learningDevelopments[index].conductedTraining!.title!.title!; + String provider = learningDevelopments[index].conductedTraining!.conductedBy!.name!; + String start = dteFormat2.format(learningDevelopments[index].conductedTraining!.fromDate!); + String end = dteFormat2.format(learningDevelopments[index].conductedTraining!.toDate!); + String type = learningDevelopments[index].conductedTraining!.learningDevelopmentType!.title!; return Column( children: [ Container( @@ -65,7 +64,7 @@ class _LearningAndDevelopmentScreenState extends State nonAcademicRecognitions; const NonAcademicRecognitionScreen({super.key, required this.nonAcademicRecognitions}); - @override - State createState() => _NonAcademicRecognitionScreenState(); -} - -class _NonAcademicRecognitionScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text(nonAcademicRecTitle), centerTitle: true, backgroundColor: primary,), - body: ListView.builder( + appBar: AppBar(title: const Text(nonAcademicRecTitle), centerTitle: true, backgroundColor: primary,actions: [AddLeading(onPressed: (){})],), + body: nonAcademicRecognitions.isNotEmpty?ListView.builder( padding: const EdgeInsets.symmetric(vertical: 8,horizontal: 10), - itemCount: widget.nonAcademicRecognitions.length, + itemCount: nonAcademicRecognitions.length, itemBuilder: (BuildContext context, int index){ - String award = widget.nonAcademicRecognitions[index].title!; - String presenter = widget.nonAcademicRecognitions[index].presenter!.name!; + String award = nonAcademicRecognitions[index].title!; + String presenter = nonAcademicRecognitions[index].presenter!.name!; return Column( children: [ Container( @@ -50,7 +47,7 @@ class _NonAcademicRecognitionScreenState extends State orgMemberships; const OrgMembershipsScreen({super.key, required this.orgMemberships}); - @override - State createState() => _OrgMembershipsScreenState(); -} - -class _OrgMembershipsScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( @@ -24,16 +19,17 @@ class _OrgMembershipsScreenState extends State { title: const Text(orgMembershipTitle), backgroundColor: primary, centerTitle: true, + actions: [AddLeading(onPressed: (){})], ), - body: ListView.builder( - itemCount: widget.orgMemberships.length, + body: orgMemberships.isNotEmpty? ListView.builder( + itemCount: orgMemberships.length, padding: const EdgeInsets.symmetric(vertical: 8,horizontal: 10), itemBuilder: (BuildContext context, int index) { String entity = - widget.orgMemberships[index].agency!.privateEntity == false + orgMemberships[index].agency!.privateEntity == false ? governmentText.toUpperCase() : privateText.toUpperCase(); - String agencyName = widget.orgMemberships[index].agency!.name!; + String agencyName = orgMemberships[index].agency!.name!; return Column( children: [ Container( @@ -60,7 +56,7 @@ class _OrgMembershipsScreenState extends State { const SizedBox(height: 5,), ], ); - }), + }):const EmptyData(message: "You don't have any Organization Membership added. Please click + to add."), ); } } diff --git a/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart b/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart index b00f398..cbc57e2 100644 --- a/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart +++ b/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart @@ -5,26 +5,24 @@ import 'package:unit2/model/profile/other_information/skills_and_hobbies.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; +import 'package:unit2/widgets/add_leading.dart'; +import 'package:unit2/widgets/empty_data.dart'; -class SkillHobbiesScreen extends StatefulWidget { +class SkillHobbiesScreen extends StatelessWidget { final ListskillsHobbies; const SkillHobbiesScreen({super.key,required this.skillsHobbies}); - @override - State createState() => _SkillHobbiesScreenState(); -} - -class _SkillHobbiesScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text(skillAndHobbiesTitle), backgroundColor: primary, centerTitle: true, + actions: [AddLeading(onPressed: (){})], ), - body: Padding( - padding: const EdgeInsets.all(16), + body: skillsHobbies.isNotEmpty? Padding( + padding: const EdgeInsets.all(24), child: Wrap( spacing: 8, runSpacing: 8, @@ -33,7 +31,7 @@ class _SkillHobbiesScreenState extends State { verticalDirection: VerticalDirection.up, crossAxisAlignment: WrapCrossAlignment.start, direction: Axis.horizontal, - children:widget.skillsHobbies.map((SkillsHobbies sh){ + children:skillsHobbies.map((SkillsHobbies sh){ return FittedBox( child: Row( children: [ @@ -47,7 +45,7 @@ class _SkillHobbiesScreenState extends State { }).toList() ), - ), + ):const EmptyData(message: "You don't have any Skills and Hobbies added. Please click + to add"), ); } } \ No newline at end of file diff --git a/lib/screens/profile/components/references_screen.dart b/lib/screens/profile/components/references_screen.dart index 1a4e221..319fd18 100644 --- a/lib/screens/profile/components/references_screen.dart +++ b/lib/screens/profile/components/references_screen.dart @@ -5,55 +5,82 @@ import 'package:unit2/model/profile/references.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; +import 'package:unit2/widgets/add_leading.dart'; +import 'package:unit2/widgets/empty_data.dart'; -class ReferencesScreen extends StatefulWidget { +class ReferencesScreen extends StatelessWidget { final List references; const ReferencesScreen({super.key, required this.references}); - @override - State createState() => _ReferencesScreenState(); -} - -class _ReferencesScreenState extends State { @override Widget build(BuildContext context) { - - return Scaffold( - appBar: AppBar(title: const Text(referencesScreenTitle),centerTitle: true,backgroundColor: primary,), - body: ListView.builder( - padding: const EdgeInsets.symmetric(vertical: 8,horizontal: 10), - itemCount: widget.references.length, - itemBuilder: (BuildContext context, int index){ - String fullname = "${widget.references[index].firstName} ${widget.references[index].middleName} ${widget.references[index].lastName}"; - String addres = "${widget.references[index].address!.cityMunicipality!.description}, ${widget.references[index].address!.cityMunicipality!.province!.description}, ${widget.references[0].address!.cityMunicipality!.province!.region!.description}"; - String mobile = widget.references[index].contactNo.toString(); - return Column(children: [ - Container( - padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), - decoration:box1(), - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - - Text(fullname,style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500)), - const Divider(), - const SizedBox(height: 5,), - Text(addres,style: Theme.of(context).textTheme.titleSmall!.copyWith(fontWeight: FontWeight.w500)), - const SizedBox(height: 8,), - Text("${mobileOrPhone.toUpperCase()} : $mobile",style: Theme.of(context).textTheme.labelMedium!), - ],), + return Scaffold( + appBar: AppBar( + title: const Text(referencesScreenTitle), + centerTitle: true, + backgroundColor: primary, + actions: [AddLeading(onPressed: (){})], + ), + body: references.isNotEmpty? ListView.builder( + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 10), + itemCount: references.length, + itemBuilder: (BuildContext context, int index) { + String fullname = + "${references[index].firstName} ${references[index].middleName} ${references[index].lastName}"; + String addres = + "${references[index].address!.cityMunicipality!.description}, ${references[index].address!.cityMunicipality!.province!.description}, ${references[0].address!.cityMunicipality!.province!.region!.description}"; + String mobile = references[index].contactNo.toString(); + return Column( + children: [ + Container( + padding: + const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + decoration: box1(), + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(fullname, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(fontWeight: FontWeight.w500)), + const Divider(), + const SizedBox( + height: 5, + ), + Text(addres, + style: Theme.of(context) + .textTheme + .titleSmall! + .copyWith(fontWeight: FontWeight.w500)), + const SizedBox( + height: 8, + ), + Text("${mobileOrPhone.toUpperCase()} : $mobile", + style: + Theme.of(context).textTheme.labelMedium!), + ], + ), + ), + IconButton( + onPressed: () {}, + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + )) + ], ), - IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert,color: Colors.grey,)) - ], - ), - ), - const SizedBox(height: 5,), - ],); - }) , + ), + const SizedBox( + height: 5, + ), + ], + ); + }):const EmptyData(message: "You don't have any References added. Please click + to add."), ); } -} \ No newline at end of file +} diff --git a/lib/screens/profile/components/voluntary_works_screen.dart b/lib/screens/profile/components/voluntary_works_screen.dart index b6e24b7..eebdaed 100644 --- a/lib/screens/profile/components/voluntary_works_screen.dart +++ b/lib/screens/profile/components/voluntary_works_screen.dart @@ -4,64 +4,82 @@ import 'package:unit2/model/profile/voluntary_works.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; +import 'package:unit2/widgets/add_leading.dart'; +import 'package:unit2/widgets/empty_data.dart'; -class VolunataryWorkScreen extends StatefulWidget { +class VolunataryWorkScreen extends StatelessWidget { final List voluntaryWorks; const VolunataryWorkScreen({super.key, required this.voluntaryWorks}); - @override - State createState() => _VolunataryWorkScreenState(); -} - -class _VolunataryWorkScreenState extends State { - DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: const Text(voluntaryScreenTitle),backgroundColor: primary,), - body: ListView.builder( - itemCount:widget.voluntaryWorks.length , - padding: const EdgeInsets.symmetric(vertical: 8,horizontal: 10), - itemBuilder: (BuildContext context, int index){ - String position = widget.voluntaryWorks[index].position!.title!; - String agency = widget.voluntaryWorks[index].agency!.name!; - String from = dteFormat2.format(widget.voluntaryWorks[index].fromDate!); - String hours = widget.voluntaryWorks[index].totalHours.toString(); - String? to = widget.voluntaryWorks[index].toDate == null? "Present" : dteFormat2.format(widget.voluntaryWorks[index].toDate!); - return Column( - children: [ - Container( - decoration:box1(), - padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), - child: Row( + DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); + return Scaffold( + appBar: AppBar( + title: const Text(voluntaryScreenTitle), + backgroundColor: primary, + actions: [AddLeading(onPressed: (){})], + ), + body: voluntaryWorks.isNotEmpty? ListView.builder( + itemCount: voluntaryWorks.length, + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 10), + itemBuilder: (BuildContext context, int index) { + String position = voluntaryWorks[index].position!.title!; + String agency = voluntaryWorks[index].agency!.name!; + String from = dteFormat2.format(voluntaryWorks[index].fromDate!); + String hours = voluntaryWorks[index].totalHours.toString(); + String? to = voluntaryWorks[index].toDate == null + ? "Present" + : dteFormat2.format(voluntaryWorks[index].toDate!); + return Column( children: [ - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, + Container( + decoration: box1(), + padding: + const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: Row( children: [ - Text(position,style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500),), - const SizedBox(height: 5,), - Text(agency,style: Theme.of(context).textTheme.titleSmall,), - const Divider(), - const SizedBox(height: 3,), - Text("$duration : $from to $to"), - const SizedBox(height: 5,), - Text("$numberOfHours : $hours hours"), - ]), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + position, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(fontWeight: FontWeight.w500), + ), + const SizedBox( + height: 5, + ), + Text( + agency, + style: Theme.of(context).textTheme.titleSmall, + ), + const Divider(), + const SizedBox( + height: 3, + ), + Text("$duration : $from to $to"), + const SizedBox( + height: 5, + ), + Text("$numberOfHours : $hours hours"), + ]), + ), + IconButton( + onPressed: () {}, icon: const Icon(Icons.more_vert)) + ], + ), + ), + const SizedBox( + height: 5, ), - IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)) ], - ), - - - ), - const SizedBox(height: 5,), - - ], - ); - - }), + ); + }):const EmptyData(message: "You don't have any Voluntary Works added. Please click + to add."), ); } -} \ No newline at end of file +} diff --git a/lib/screens/profile/components/work_history_screen.dart b/lib/screens/profile/components/work_history_screen.dart index 47d5c90..1d896dd 100644 --- a/lib/screens/profile/components/work_history_screen.dart +++ b/lib/screens/profile/components/work_history_screen.dart @@ -4,38 +4,38 @@ import 'package:unit2/model/profile/work_history.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; +import 'package:unit2/widgets/add_leading.dart'; +import 'package:unit2/widgets/empty_data.dart'; import '../../../utils/global.dart'; -class WorkHistoryScreen extends StatefulWidget { +class WorkHistoryScreen extends StatelessWidget { final List workExperiences; const WorkHistoryScreen({super.key, required this.workExperiences}); - @override - State createState() => _WorkHistoryScreenState(); -} + -class _WorkHistoryScreenState extends State { - DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); @override Widget build(BuildContext context) { + DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); return Scaffold( appBar: AppBar( title: const Text(workHistoryScreenTitle), backgroundColor: primary, centerTitle: true, + actions: [AddLeading(onPressed: (){})], ), - body: ListView.builder( + body: workExperiences.isNotEmpty? ListView.builder( padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 10), - itemCount: widget.workExperiences.length, + itemCount: workExperiences.length, itemBuilder: (BuildContext context, int index) { - String position = widget.workExperiences[index].position!.title!; - String agency = widget.workExperiences[index].agency!.name!; + String position = workExperiences[index].position!.title!; + String agency = workExperiences[index].agency!.name!; String from = - dteFormat2.format(widget.workExperiences[index].fromDate!); - String? to = widget.workExperiences[index].toDate == null + dteFormat2.format(workExperiences[index].fromDate!); + String? to = workExperiences[index].toDate == null ? present.toUpperCase() - : dteFormat2.format(widget.workExperiences[index].toDate!); + : dteFormat2.format(workExperiences[index].toDate!); return Column( children: [ @@ -65,7 +65,7 @@ class _WorkHistoryScreenState extends State { ), ], ); - }), + }):const EmptyData(message: "You don't have any Work History added. Please click + to add ."), ); } } diff --git a/lib/screens/unit2/login/login.dart b/lib/screens/unit2/login/login.dart index 7b6fbf5..045bcc6 100644 --- a/lib/screens/unit2/login/login.dart +++ b/lib/screens/unit2/login/login.dart @@ -8,6 +8,7 @@ import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:unit2/screens/unit2/login/components/update_required.dart'; import 'package:unit2/utils/alerts.dart'; +import 'package:unit2/utils/internet_time_out.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/error_state.dart'; import '../../../bloc/user/user_bloc.dart'; @@ -319,14 +320,13 @@ class _UniT2LoginState extends State { }); } if (state is UserError) { - return ErrorState( - message: state.message, + return SomethingWentWrong( + message:onError , + onpressed:(){} , ); } if (state is InternetTimeout) { - return ErrorState( - message: state.message, - ); + return const TimeOutError(); } if (state is SplashScreen) { return const UniTSplashScreen(); diff --git a/lib/screens/unit2/login/qr_login.dart b/lib/screens/unit2/login/qr_login.dart index 0a52f5c..6be5696 100644 --- a/lib/screens/unit2/login/qr_login.dart +++ b/lib/screens/unit2/login/qr_login.dart @@ -1,14 +1,11 @@ -import 'package:flutter/cupertino.dart'; + import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/container.dart'; -import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_svg/svg.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:unit2/theme-data.dart/btn-style.dart'; -import 'package:unit2/widgets/error_state.dart'; import 'package:unit2/widgets/wave.dart'; import '../../../bloc/user/user_bloc.dart'; import '../../../theme-data.dart/colors.dart'; @@ -186,8 +183,6 @@ class _QRLoginState extends State { ], ), ); - }if(state is UserError){ - return ErrorState(message: state.message,); } return Container(); }, diff --git a/lib/utils/internet_time_out.dart b/lib/utils/internet_time_out.dart new file mode 100644 index 0000000..b4a0359 --- /dev/null +++ b/lib/utils/internet_time_out.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:unit2/theme-data.dart/btn-style.dart'; + +import '../theme-data.dart/colors.dart'; + +class TimeOutError extends StatelessWidget { + const TimeOutError({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SvgPicture.asset( + 'assets/svgs/timeout.svg', + height: 200.0, + width: 200.0, + allowDrawingOutsideViewBox: true, + ), + const SizedBox( + height: 25, + ), + const Text( + 'Connection Timeout! Pls check your internet connectivity.', + textAlign: TextAlign.center,), + + const SizedBox( + height: 25, + ), + SizedBox( + height: 50, + child: ElevatedButton.icon( + style: mainBtnStyle( + primary, Colors.transparent, primary.withOpacity(.5)), + onPressed:(){ + }, + icon: const Icon( + Icons.refresh, + color: Colors.white, + ), + label: const Text( + "try again", + style: TextStyle(color: Colors.white), + )), + ) + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/widgets/add_leading.dart b/lib/widgets/add_leading.dart new file mode 100644 index 0000000..b053d1a --- /dev/null +++ b/lib/widgets/add_leading.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; + +class AddLeading extends StatelessWidget { + final Function() onPressed; + const AddLeading({super.key, required this.onPressed}); + + @override + Widget build(BuildContext context) { + return IconButton(onPressed: onPressed, icon: const Icon(Icons.add)); + } +} \ No newline at end of file diff --git a/lib/widgets/empty_data.dart b/lib/widgets/empty_data.dart new file mode 100644 index 0000000..78a4515 --- /dev/null +++ b/lib/widgets/empty_data.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:unit2/utils/global.dart'; + + +class EmptyData extends StatelessWidget { + final String message; + const EmptyData({Key? key, required this.message,}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SvgPicture.asset( + 'assets/svgs/empty.svg', + height: 200.0, + width: 200.0, + allowDrawingOutsideViewBox: true, + ), + const SizedBox( + height: 20, + ), + Text( + message,style: Theme.of(context).textTheme.displaySmall!.copyWith(fontSize: blockSizeVertical * 2), + textAlign: TextAlign.center, + ), + + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/widgets/error_state.dart b/lib/widgets/error_state.dart index ede9ef3..e66a523 100644 --- a/lib/widgets/error_state.dart +++ b/lib/widgets/error_state.dart @@ -1,13 +1,58 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/src/widgets/container.dart'; -import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:unit2/theme-data.dart/btn-style.dart'; -class ErrorState extends StatelessWidget { +import '../theme-data.dart/colors.dart'; + +class SomethingWentWrong extends StatelessWidget { final String? message; - const ErrorState({super.key,this.message}); + final Function()? onpressed; + const SomethingWentWrong({Key? key, required this.message, required this.onpressed}) + : super(key: key); @override Widget build(BuildContext context) { - return Center(child: Text(message!)); + return Container( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SvgPicture.asset( + 'assets/svgs/error.svg', + height: 200.0, + width: 200.0, + allowDrawingOutsideViewBox: true, + ), + const SizedBox( + height: 10, + ), + Text( + message??'', + textAlign: TextAlign.center, + ), + const SizedBox( + height: 20, + ), + SizedBox( + height: 50, + child: ElevatedButton.icon( + style: mainBtnStyle( + primary, Colors.transparent, primary.withOpacity(.5)), + onPressed: onpressed, + icon: const Icon( + Icons.refresh, + color: Colors.white, + ), + label: const Text( + "try again", + style: TextStyle(color: Colors.white), + )), + ) + ], + ), + ), + ); } } \ No newline at end of file From b9615e36666eef70e3632453c9893396411a1a42 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Wed, 15 Feb 2023 11:40:12 +0800 Subject: [PATCH 32/86] load eligibilities with profile bloc --- lib/bloc/profile/profile_bloc.dart | 6 + lib/bloc/profile/profile_event.dart | 7 + lib/bloc/profile/profile_state.dart | 7 + lib/model/location/barangay.dart | 122 ++++++++++ lib/model/location/city.dart | 98 ++++++++ lib/model/location/country.dart | 34 +++ lib/model/location/provinces.dart | 66 +++++ lib/model/location/purok.dart | 0 lib/model/location/region.dart | 34 +++ lib/model/utils/eligibilities_choices.dart | 34 +++ .../components/eligibility/edit_modal.dart | 225 +++++++++++++++++ .../eligibility/eligibility_screen.dart | 182 ++++++++++++++ lib/screens/profile/profile.dart | 228 +++++++++++------- lib/widgets/custom_switch.dart | 44 ++++ pubspec.lock | 8 + pubspec.yaml | 1 + 16 files changed, 1007 insertions(+), 89 deletions(-) create mode 100644 lib/model/location/barangay.dart create mode 100644 lib/model/location/city.dart create mode 100644 lib/model/location/country.dart create mode 100644 lib/model/location/provinces.dart create mode 100644 lib/model/location/purok.dart create mode 100644 lib/model/location/region.dart create mode 100644 lib/model/utils/eligibilities_choices.dart create mode 100644 lib/screens/profile/components/eligibility/edit_modal.dart create mode 100644 lib/screens/profile/components/eligibility/eligibility_screen.dart create mode 100644 lib/widgets/custom_switch.dart diff --git a/lib/bloc/profile/profile_bloc.dart b/lib/bloc/profile/profile_bloc.dart index 8368e9b..bd28a2e 100644 --- a/lib/bloc/profile/profile_bloc.dart +++ b/lib/bloc/profile/profile_bloc.dart @@ -2,6 +2,7 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:unit2/model/profile/basic_info.dart'; import 'package:unit2/model/profile/basic_information/primary-information.dart'; +import 'package:unit2/model/profile/eligibility.dart'; import 'package:unit2/model/profile/profileInfomation.dart'; import 'package:unit2/sevices/profile/profile_service.dart'; @@ -23,5 +24,10 @@ class ProfileBloc extends Bloc { // emit(ProfileErrorState(mesage: e.toString())); // } }); + + on((event,emit){ + emit(ProfileLoading()); + emit(EligibilityLoaded(eligibilities: event.eligibilities)); + }); } } diff --git a/lib/bloc/profile/profile_event.dart b/lib/bloc/profile/profile_event.dart index e978beb..83d1f28 100644 --- a/lib/bloc/profile/profile_event.dart +++ b/lib/bloc/profile/profile_event.dart @@ -21,3 +21,10 @@ class LoadProfileInformation extends ProfileEvent{ List get props => []; } +class LoadEligibility extends ProfileEvent{ + final List eligibilities; + const LoadEligibility({required this.eligibilities}); + @override + List get props => []; +} + diff --git a/lib/bloc/profile/profile_state.dart b/lib/bloc/profile/profile_state.dart index ae753d5..fd1f36a 100644 --- a/lib/bloc/profile/profile_state.dart +++ b/lib/bloc/profile/profile_state.dart @@ -27,3 +27,10 @@ class ProfileLoading extends ProfileState{ } +class EligibilityLoaded extends ProfileState{ + final List eligibilities; + const EligibilityLoaded({required this.eligibilities}); + @override + List get props => [eligibilities]; +} + diff --git a/lib/model/location/barangay.dart b/lib/model/location/barangay.dart new file mode 100644 index 0000000..a73735d --- /dev/null +++ b/lib/model/location/barangay.dart @@ -0,0 +1,122 @@ +// To parse this JSON data, do +// +// final barangay = barangayFromJson(jsonString); + +import 'package:meta/meta.dart'; +import 'dart:convert'; + +Barangay barangayFromJson(String str) => Barangay.fromJson(json.decode(str)); + +String barangayToJson(Barangay data) => json.encode(data.toJson()); + +class Barangay { + Barangay({ + required this.code, + required this.description, + required this.cityMunicipality, + }); + + final String code; + final String description; + final CityMunicipality cityMunicipality; + + factory Barangay.fromJson(Map json) => Barangay( + code: json["code"], + description: json["description"], + cityMunicipality: CityMunicipality.fromJson(json["city_municipality"]), + ); + + Map toJson() => { + "code": code, + "description": description, + "city_municipality": cityMunicipality.toJson(), + }; +} + +class CityMunicipality { + CityMunicipality({ + required this.code, + required this.description, + required this.province, + required this.psgcCode, + required this.zipcode, + }); + + final String code; + final String description; + final Province province; + final String psgcCode; + final String zipcode; + + factory CityMunicipality.fromJson(Map json) => CityMunicipality( + code: json["code"], + description: json["description"], + province: Province.fromJson(json["province"]), + psgcCode: json["psgc_code"], + zipcode: json["zipcode"], + ); + + Map toJson() => { + "code": code, + "description": description, + "province": province.toJson(), + "psgc_code": psgcCode, + "zipcode": zipcode, + }; +} + +class Province { + Province({ + required this.code, + required this.description, + required this.region, + required this.psgcCode, + required this.shortname, + }); + + final String code; + final String description; + final Region region; + final String psgcCode; + final String shortname; + + factory Province.fromJson(Map json) => Province( + code: json["code"], + description: json["description"], + region: Region.fromJson(json["region"]), + psgcCode: json["psgc_code"], + shortname: json["shortname"], + ); + + Map toJson() => { + "code": code, + "description": description, + "region": region.toJson(), + "psgc_code": psgcCode, + "shortname": shortname, + }; +} + +class Region { + Region({ + required this.code, + required this.description, + required this.psgcCode, + }); + + final int code; + final String description; + final String psgcCode; + + factory Region.fromJson(Map json) => Region( + code: json["code"], + description: json["description"], + psgcCode: json["psgc_code"], + ); + + Map toJson() => { + "code": code, + "description": description, + "psgc_code": psgcCode, + }; +} diff --git a/lib/model/location/city.dart b/lib/model/location/city.dart new file mode 100644 index 0000000..8d9014c --- /dev/null +++ b/lib/model/location/city.dart @@ -0,0 +1,98 @@ +// To parse this JSON data, do +// +// final city = cityFromJson(jsonString); + +import 'package:meta/meta.dart'; +import 'dart:convert'; + +City cityFromJson(String str) => City.fromJson(json.decode(str)); + +String cityToJson(City data) => json.encode(data.toJson()); + +class City { + City({ + required this.code, + required this.description, + required this.province, + required this.psgcCode, + required this.zipcode, + }); + + final String code; + final String description; + final Province province; + final String psgcCode; + final String zipcode; + + factory City.fromJson(Map json) => City( + code: json["code"], + description: json["description"], + province: Province.fromJson(json["province"]), + psgcCode: json["psgc_code"], + zipcode: json["zipcode"], + ); + + Map toJson() => { + "code": code, + "description": description, + "province": province.toJson(), + "psgc_code": psgcCode, + "zipcode": zipcode, + }; +} + +class Province { + Province({ + required this.code, + required this.description, + required this.region, + required this.psgcCode, + required this.shortname, + }); + + final String code; + final String description; + final Region region; + final String psgcCode; + final String shortname; + + factory Province.fromJson(Map json) => Province( + code: json["code"], + description: json["description"], + region: Region.fromJson(json["region"]), + psgcCode: json["psgc_code"], + shortname: json["shortname"], + ); + + Map toJson() => { + "code": code, + "description": description, + "region": region.toJson(), + "psgc_code": psgcCode, + "shortname": shortname, + }; +} + +class Region { + Region({ + required this.code, + required this.description, + required this.psgcCode, + }); + + final int code; + final String description; + final String psgcCode; + + factory Region.fromJson(Map json) => Region( + code: json["code"], + description: json["description"], + psgcCode: json["psgc_code"], + ); + + Map toJson() => { + "code": code, + "description": description, + "psgc_code": psgcCode, + }; +} diff --git a/lib/model/location/country.dart b/lib/model/location/country.dart new file mode 100644 index 0000000..c3a98ca --- /dev/null +++ b/lib/model/location/country.dart @@ -0,0 +1,34 @@ +// To parse this JSON data, do +// +// final country = countryFromJson(jsonString); + +import 'package:meta/meta.dart'; +import 'dart:convert'; + +Country countryFromJson(String str) => Country.fromJson(json.decode(str)); + +String countryToJson(Country data) => json.encode(data.toJson()); + +class Country { + Country({ + required this.id, + required this.name, + required this.code, + }); + + final int id; + final String name; + final String code; + + factory Country.fromJson(Map json) => Country( + id: json["id"], + name: json["name"], + code: json["code"], + ); + + Map toJson() => { + "id": id, + "name": name, + "code": code, + }; +} diff --git a/lib/model/location/provinces.dart b/lib/model/location/provinces.dart new file mode 100644 index 0000000..8d7ee19 --- /dev/null +++ b/lib/model/location/provinces.dart @@ -0,0 +1,66 @@ +// To parse this JSON data, do +// +// final province = provinceFromJson(jsonString); + +import 'package:meta/meta.dart'; +import 'dart:convert'; + +Province provinceFromJson(String str) => Province.fromJson(json.decode(str)); + +String provinceToJson(Province data) => json.encode(data.toJson()); + +class Province { + Province({ + required this.code, + required this.description, + required this.region, + required this.psgcCode, + required this.shortname, + }); + + final String code; + final String description; + final Region region; + final String psgcCode; + final String shortname; + + factory Province.fromJson(Map json) => Province( + code: json["code"], + description: json["description"], + region: Region.fromJson(json["region"]), + psgcCode: json["psgc_code"], + shortname: json["shortname"], + ); + + Map toJson() => { + "code": code, + "description": description, + "region": region.toJson(), + "psgc_code": psgcCode, + "shortname": shortname, + }; +} + +class Region { + Region({ + required this.code, + required this.description, + required this.psgcCode, + }); + + final int code; + final String description; + final String psgcCode; + + factory Region.fromJson(Map json) => Region( + code: json["code"], + description: json["description"], + psgcCode: json["psgc_code"], + ); + + Map toJson() => { + "code": code, + "description": description, + "psgc_code": psgcCode, + }; +} diff --git a/lib/model/location/purok.dart b/lib/model/location/purok.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/model/location/region.dart b/lib/model/location/region.dart new file mode 100644 index 0000000..718df02 --- /dev/null +++ b/lib/model/location/region.dart @@ -0,0 +1,34 @@ +// To parse this JSON data, do +// +// final region = regionFromJson(jsonString); + +import 'package:meta/meta.dart'; +import 'dart:convert'; + +Region regionFromJson(String str) => Region.fromJson(json.decode(str)); + +String regionToJson(Region data) => json.encode(data.toJson()); + +class Region { + Region({ + required this.code, + required this.description, + required this.psgcCode, + }); + + final int code; + final String description; + final String psgcCode; + + factory Region.fromJson(Map json) => Region( + code: json["code"], + description: json["description"], + psgcCode: json["psgc_code"], + ); + + Map toJson() => { + "code": code, + "description": description, + "psgc_code": psgcCode, + }; +} diff --git a/lib/model/utils/eligibilities_choices.dart b/lib/model/utils/eligibilities_choices.dart new file mode 100644 index 0000000..24b963d --- /dev/null +++ b/lib/model/utils/eligibilities_choices.dart @@ -0,0 +1,34 @@ +// To parse this JSON data, do +// +// final eligibilities = eligibilitiesFromJson(jsonString); + +import 'package:meta/meta.dart'; +import 'dart:convert'; + +Eligibilities eligibilitiesFromJson(String str) => Eligibilities.fromJson(json.decode(str)); + +String eligibilitiesToJson(Eligibilities data) => json.encode(data.toJson()); + +class Eligibilities { + Eligibilities({ + required this.id, + required this.title, + required this.type, + }); + + final int id; + final String title; + final String type; + + factory Eligibilities.fromJson(Map json) => Eligibilities( + id: json["id"], + title: json["title"], + type: json["type"], + ); + + Map toJson() => { + "id": id, + "title": title, + "type": type, + }; +} diff --git a/lib/screens/profile/components/eligibility/edit_modal.dart b/lib/screens/profile/components/eligibility/edit_modal.dart new file mode 100644 index 0000000..0a60f62 --- /dev/null +++ b/lib/screens/profile/components/eligibility/edit_modal.dart @@ -0,0 +1,225 @@ +import 'package:date_time_picker/date_time_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:intl/intl.dart'; +import 'package:unit2/model/profile/eligibility.dart'; + +import '../../../../theme-data.dart/btn-style.dart'; +import '../../../../theme-data.dart/colors.dart'; +import '../../../../theme-data.dart/form-style.dart'; +import '../../../../utils/global.dart'; +import '../../../../utils/text_container.dart'; + +class EditEligibilityScreen extends StatefulWidget { + final EligibityCert eligibityCert; + const EditEligibilityScreen({super.key,required this.eligibityCert}); + + @override + State createState() => _EditEligibilityScreenState(); +} + +class _EditEligibilityScreenState extends State { + final formKey = GlobalKey(); + bool overseas =false; + DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); + @override + Widget build(BuildContext context) { + return Center( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 18), + child: FormBuilder( + key: formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + FormBuilderTextField( + name: "eligibility", + initialValue: widget.eligibityCert.eligibility!.title!, + decoration: + normalTextFieldStyle("Eligibility", "Eligibility"), + ), + const SizedBox( + height: 20, + ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + name: 'license number', + initialValue: + widget.eligibityCert.licenseNumber, + decoration: normalTextFieldStyle( + "license number", "license number"), + ), + ), + const SizedBox( + width: 12, + ), + Flexible( + flex: 1, + child: FormBuilderTextField( + name: 'rating', + initialValue: + widget.eligibityCert.rating.toString(), + decoration: + normalTextFieldStyle('rating', 'rating'), + ), + ), + ], + ), + ), + const SizedBox( + height: 20, + ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + Flexible( + flex: 1, + child: DateTimePicker( + firstDate: DateTime(2000), + lastDate: DateTime(2100), + decoration: normalTextFieldStyle( + "Exam date", "Exam date"), + initialValue: + widget.eligibityCert.examDate == null + ? '' + : dteFormat2.format( + widget.eligibityCert.examDate!), + )), + const SizedBox( + width: 12, + ), + Flexible( + flex: 1, + child: DateTimePicker( + firstDate: DateTime(2000), + lastDate: DateTime(2100), + decoration: normalTextFieldStyle( + "Validity date", "Validity date"), + initialValue: + widget.eligibityCert.validityDate == null + ? '' + : dteFormat2.format( + widget.eligibityCert.validityDate!), + ), + ), + ], + ), + ), + const SizedBox( + height: 20, + ), + Text( + "Placement of Examination/Confinement", + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith(fontSize: blockSizeVertical * 2), + ), + const SizedBox( + height: 12, + ), + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Overseas Address?"), + ), + const SizedBox( + height: 20, + ), + SizedBox( + child: overseas == true + ? FormBuilderTextField( + name: 'country', + decoration: normalTextFieldStyle( + "Country", "Country"), + ) + : Column( + children: [ + FormBuilderDropdown( + decoration: normalTextFieldStyle( + "Region", "Region"), + name: 'region', + items: [], + initialValue: widget + .eligibityCert + .examAddress + ?.cityMunicipality + ?.province + ?.region + ?.description == + null + ? 'region' + : 'region', + ), + const SizedBox( + height: 20, + ), + FormBuilderDropdown( + decoration: normalTextFieldStyle( + 'Province', "Province"), + name: 'province', + items: [], + initialValue: widget + .eligibityCert + .examAddress + ?.cityMunicipality + ?.province + ?.description == + null + ? 'region' + : 'pprovince'), + const SizedBox( + height: 20, + ), + FormBuilderDropdown( + decoration: normalTextFieldStyle( + "Municipality", "Municipality"), + name: 'municipality', + items: [], + initialValue: widget + .eligibityCert + .examAddress + ?.cityMunicipality + ?.description == + null + ? 'region' + : 'municipality', + ), + ], + )), + const SizedBox( + height: 20, + ), + SizedBox( + width: screenWidth, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () {}, + child: const Text(submit)), + ), + const SizedBox(height: 20,), + + ]), + ), + ), + );; + } +} \ No newline at end of file diff --git a/lib/screens/profile/components/eligibility/eligibility_screen.dart b/lib/screens/profile/components/eligibility/eligibility_screen.dart new file mode 100644 index 0000000..19f1d39 --- /dev/null +++ b/lib/screens/profile/components/eligibility/eligibility_screen.dart @@ -0,0 +1,182 @@ +import 'package:app_popup_menu/app_popup_menu.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/user/user_bloc.dart'; +import 'package:unit2/model/profile/eligibility.dart'; +import 'package:unit2/screens/profile/components/eligibility/edit_modal.dart'; +import 'package:unit2/theme-data.dart/box_shadow.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/alerts.dart'; +import 'package:unit2/utils/global.dart'; +import 'package:unit2/utils/text_container.dart'; +import 'package:unit2/widgets/add_leading.dart'; +import 'package:unit2/widgets/empty_data.dart'; + +class EligibiltyScreen extends StatefulWidget { + const EligibiltyScreen({super.key,}); + + @override + State createState() => _EligibiltyScreenState(); +} + +class _EligibiltyScreenState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text(elibilityScreenTitle), + centerTitle: true, + backgroundColor: primary, + actions: context.read()[AddLeading( + onPressed: () => () {}, + )], + ), + body: BlocBuilder( + builder: (context, state) { + return BlocBuilder( + builder: (context, state) { + if(state is EligibilityLoaded){ +return ListView.builder( + padding: + const EdgeInsets.symmetric(vertical: 8, horizontal: 10), + itemCount: state.eligibilities.length, + itemBuilder: (BuildContext context, int index) { + String title = + state.eligibilities[index].eligibility!.title!; + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: screenWidth, + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + decoration: box1(), + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + title, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: FontWeight.w500), + ), + const Divider(), + const SizedBox( + height: 5, + ), + Text( + "$licenseNumber: ${state.eligibilities[index].licenseNumber == null ? 'N/A' : state.eligibilities[index].licenseNumber.toString()}", + style: Theme.of(context) + .textTheme + .titleSmall), + const SizedBox( + height: 3, + ), + Text( + " : ${state.eligibilities[index].rating ?? 'N/A'}.", + style: Theme.of(context) + .textTheme + .titleSmall) + ]), + ), + AppPopupMenu( + offset: const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + // if (value == 1) { + // confirmAlert(context, () => null, + // "Delete?", "Confirm Delete?"); + // } + // if (value == 2) { + // context.read().add( + // EditEligibility( + // eligibityCert: widget + // .eligibilities[index])); + // } + }, + menuItems: [ + PopupMenuItem( + value: 1, + child: Row( + children: const [ + Icon( + Icons.delete, + ), + SizedBox( + width: 10, + ), + Text( + 'Delete', + ), + ], + ), + ), + PopupMenuItem( + value: 2, + child: Row( + children: const [ + Icon( + Icons.edit, + ), + SizedBox( + width: 10, + ), + Text( + 'Edit', + ), + ], + ), + ), + PopupMenuItem( + value: 2, + child: Row( + children: const [ + Icon( + FontAwesome.attach, + ), + SizedBox( + width: 10, + ), + Text( + 'Attachment', + ), + ], + ), + ), + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ) + ], + ), + ), + const SizedBox( + height: 5, + ) + ], + ); + }); + } + return Container(); + }, + ); + }, + )); + } +} diff --git a/lib/screens/profile/profile.dart b/lib/screens/profile/profile.dart index ce97d91..db097ad 100644 --- a/lib/screens/profile/profile.dart +++ b/lib/screens/profile/profile.dart @@ -8,6 +8,7 @@ import 'package:fluttericon/entypo_icons.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:fluttericon/modern_pictograms_icons.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/model/login_data/employee_info/employee_info.dart'; import 'package:unit2/screens/profile/components/basic_information/address_screen.dart'; import 'package:unit2/screens/profile/components/basic_information/citizenship_screen.dart'; import 'package:unit2/screens/profile/components/basic_information/contact_information_screen.dart'; @@ -46,8 +47,7 @@ class _ProfileInfoState extends State { title: const Text('Profile'), ), body: ProgressHUD( - child: - BlocBuilder(builder: (context, state) { + child: BlocBuilder(builder: (context, state) { if (state is UserLoggedIn) { return BlocConsumer( listener: (context, state) { @@ -64,20 +64,20 @@ class _ProfileInfoState extends State { }, builder: (context, state) { if (state is ProfileLoaded) { - return Container( padding: const EdgeInsets.symmetric( vertical: 12, horizontal: 12), child: ListView( children: [ - Text( - "View and Update your Profile Information",textAlign: TextAlign.center - ,style: Theme.of(context).textTheme.bodyLarge,), + Text( + "View and Update your Profile Information", + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.bodyLarge, + ), ExpandableGroup( collapsedIcon: const Icon(Icons.keyboard_arrow_down), - expandedIcon: - const Icon(Icons.keyboard_arrow_up), + expandedIcon: const Icon(Icons.keyboard_arrow_up), header: const ListTile( leading: Icon( Elusive.address_book, @@ -85,115 +85,157 @@ class _ProfileInfoState extends State { ), title: Text( "Basic Information", - style: TextStyle( - fontWeight: FontWeight.bold), + style: TextStyle(fontWeight: FontWeight.bold), ), ), items: [ - subMenu(Icons.person, "Primary",(){ - Navigator.push(context,MaterialPageRoute(builder: (BuildContext context){ - return PrimaryInfo(primaryInformation: state.profileInformation.basicInfo.primaryInformation!); - }) ); + subMenu(Icons.person, "Primary", () { + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return PrimaryInfo( + primaryInformation: state + .profileInformation + .basicInfo + .primaryInformation!); + })); }), - subMenu(Icons.home, "Home Addresses",(){ - Navigator.push(context,MaterialPageRoute(builder: (BuildContext context){ - return AddressScreen(addresses: state.profileInformation.basicInfo.addresses); - }) ); - + subMenu(Icons.home, "Home Addresses", () { + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return AddressScreen( + addresses: state.profileInformation + .basicInfo.addresses); + })); }), - subMenu( - Icons.contact_mail, "Identifications",(){ - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ - return IdentificationsScreen(identities: state.profileInformation.basicInfo.identifications); - })); - }), - subMenu( - Icons.contact_phone, "Contact Info",(){ - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ - return ContactInformationScreen(contacts: state.profileInformation.basicInfo.contactInformation,); - })); - }), - subMenu(Icons.flag, "Citizenships",(){ - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ - return CitizenShipScreen(citizenships: state.profileInformation.basicInfo.citizenships,); + subMenu(Icons.contact_mail, "Identifications", + () { + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return IdentificationsScreen( + identities: state.profileInformation + .basicInfo.identifications); + })); + }), + subMenu(Icons.contact_phone, "Contact Info", + () { + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return ContactInformationScreen( + contacts: state.profileInformation + .basicInfo.contactInformation, + ); + })); + }), + subMenu(Icons.flag, "Citizenships", () { + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return CitizenShipScreen( + citizenships: state.profileInformation + .basicInfo.citizenships, + ); })); }), ]), const Divider(), - MainMenu( + MainMenu( icon: Elusive.group, title: "Family", - onTap: (){ - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ - return FamilyBackgroundScreen(familyBackground: state.profileInformation.families); + onTap: () { + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return FamilyBackgroundScreen( + familyBackground: + state.profileInformation.families); })); }, ), const Divider(), - MainMenu( + MainMenu( icon: FontAwesome5.graduation_cap, title: "Education", - onTap: (){ - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ - return EducationScreen(educationBackgrounds: state.profileInformation.educationalBackgrounds); + onTap: () { + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return EducationScreen( + educationBackgrounds: state + .profileInformation + .educationalBackgrounds); })); }, ), const Divider(), - MainMenu( + MainMenu( icon: Icons.stars, title: "Eligibility", - onTap: (){ - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ - return EligibiltyScreen(eligibilities: state.profileInformation.eligibilities); + onTap: () { + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return BlocProvider.value(value: ProfileBloc()..add(LoadEligibility(eligibilities: state.profileInformation.eligibilities)), + + child: EligibiltyScreen( + eligibilities: state + .profileInformation.eligibilities), + ); })); }, ), const Divider(), - MainMenu( + MainMenu( icon: FontAwesome5.shopping_bag, title: "Work History", - onTap: (){ - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ - return WorkHistoryScreen(workExperiences: state.profileInformation.workExperiences); + onTap: () { + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return WorkHistoryScreen( + workExperiences: state + .profileInformation.workExperiences); })); }, ), const Divider(), - MainMenu( + MainMenu( icon: FontAwesome5.walking, title: "Voluntary Work & Civic Services", - onTap: (){ - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ - return VolunataryWorkScreen(voluntaryWorks: state.profileInformation.voluntaryWorks); + onTap: () { + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return VolunataryWorkScreen( + voluntaryWorks: state + .profileInformation.voluntaryWorks); })); }, ), const Divider(), - MainMenu( + MainMenu( icon: Elusive.lightbulb, title: "Learning & Development", - onTap: (){ - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ - return LearningAndDevelopmentScreen(learningDevelopments: state.profileInformation.learningsAndDevelopment); - })); + onTap: () { + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return LearningAndDevelopmentScreen( + learningDevelopments: state + .profileInformation + .learningsAndDevelopment); + })); }, ), const Divider(), - MainMenu( + MainMenu( icon: Brandico.codepen, title: "Personal References", - onTap: (){ - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ - return ReferencesScreen(references: state.profileInformation.references); + onTap: () { + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return ReferencesScreen( + references: + state.profileInformation.references); })); }, ), ExpandableGroup( collapsedIcon: const Icon(Icons.keyboard_arrow_down), - expandedIcon: - const Icon(Icons.keyboard_arrow_up), + expandedIcon: const Icon(Icons.keyboard_arrow_up), header: const ListTile( leading: Icon( Icons.info, @@ -201,36 +243,45 @@ class _ProfileInfoState extends State { ), title: Text( "Other Information", - style: TextStyle( - fontWeight: FontWeight.bold), + style: TextStyle(fontWeight: FontWeight.bold), ), ), items: [ - subMenu(Icons.fitness_center, - "Skills & Hobbies",(){ - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ - return SkillHobbiesScreen(skillsHobbies: state.profileInformation.otherInformation.skillsAndHobbies); - })); - }), + subMenu( + Icons.fitness_center, "Skills & Hobbies", + () { + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return SkillHobbiesScreen( + skillsHobbies: state.profileInformation + .otherInformation.skillsAndHobbies); + })); + }), subMenu(FontAwesome5.certificate, - "Organization Memberships",(){ - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ - return OrgMembershipsScreen(orgMemberships: state.profileInformation.otherInformation.orgMemberships); - })); - }), + "Organization Memberships", () { + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return OrgMembershipsScreen( + orgMemberships: state.profileInformation + .otherInformation.orgMemberships); + })); + }), subMenu(Entypo.doc_text, - "Non-Academic Recognitions",(){ - - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ - return NonAcademicRecognitionScreen(nonAcademicRecognitions: state.profileInformation.otherInformation.nonAcademicRecognition); - })); - }), + "Non-Academic Recognitions", () { + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return NonAcademicRecognitionScreen( + nonAcademicRecognitions: state + .profileInformation + .otherInformation + .nonAcademicRecognition); + })); + }), ]), ExpandableGroup( collapsedIcon: const Icon(Icons.keyboard_arrow_down), - expandedIcon: - const Icon(Icons.keyboard_arrow_up), + expandedIcon: const Icon(Icons.keyboard_arrow_up), header: const ListTile( leading: Icon( FontAwesome5.laptop_house, @@ -238,13 +289,12 @@ class _ProfileInfoState extends State { ), title: Text( "Assets", - style: TextStyle( - fontWeight: FontWeight.bold), + style: TextStyle(fontWeight: FontWeight.bold), ), ), items: [ subMenu(ModernPictograms.home, - "Real Property Tax",(){}), + "Real Property Tax", () {}), ]), ], ), diff --git a/lib/widgets/custom_switch.dart b/lib/widgets/custom_switch.dart new file mode 100644 index 0000000..907993f --- /dev/null +++ b/lib/widgets/custom_switch.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; +import 'package:toggle_switch/toggle_switch.dart'; + +class CostumToggleSwitch extends StatelessWidget { + final List activeBGColors; + final List icons; +final int initialLabelIndex; + final void Function(int?)? onToggle; + final List labels; + const CostumToggleSwitch( + {Key? key, + required this.activeBGColors, + required this.icons, + required this.onToggle, + required this.labels, + required this.initialLabelIndex + + }) + : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(15), + height: 80, + child: ToggleSwitch( + minWidth: 150.0, + cornerRadius: 25.0, + activeBgColors: [ + [Colors.green[800]!], + [Colors.red[800]!] + ], + activeFgColor: Colors.white, + inactiveBgColor: Colors.grey, + inactiveFgColor: Colors.white, + initialLabelIndex: initialLabelIndex, + totalSwitches: 2, + labels: labels, + icons: icons, + radiusStyle: false, + onToggle: onToggle), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 388b681..3f8450d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -9,6 +9,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.2" + app_popup_menu: + dependency: "direct main" + description: + name: app_popup_menu + sha256: e05b262b65289431603a84e04e53cb2f3aca6013d3ea61e3f24ddd48d49ef848 + url: "https://pub.dev" + source: hosted + version: "1.0.0" archive: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 698736c..85eecab 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -68,6 +68,7 @@ dependencies: permission_handler: ^10.2.0 expandable_group: ^0.0.8 badges: ^3.0.2 + app_popup_menu: ^1.0.0 dev_dependencies: flutter_test: From 4418107f2141660d21a5d9776eed4d88ff60e139 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Wed, 15 Feb 2023 13:23:06 +0800 Subject: [PATCH 33/86] Edit eligibility with profile bloc --- lib/bloc/profile/profile_bloc.dart | 20 +- lib/bloc/profile/profile_event.dart | 8 + lib/bloc/profile/profile_state.dart | 6 + .../eligibility/eligibility_screen.dart | 290 +++++++----------- .../components/eligibility_screen.dart | 192 +++++++++--- lib/screens/profile/profile.dart | 3 +- .../unit2/homepage.dart/components/menu.dart | 3 +- lib/utils/alerts.dart | 6 +- 8 files changed, 286 insertions(+), 242 deletions(-) diff --git a/lib/bloc/profile/profile_bloc.dart b/lib/bloc/profile/profile_bloc.dart index bd28a2e..d0cc5bf 100644 --- a/lib/bloc/profile/profile_bloc.dart +++ b/lib/bloc/profile/profile_bloc.dart @@ -14,20 +14,22 @@ class ProfileBloc extends Bloc { ProfileInformation? _profileInformation; on((event, emit) async { // try { - emit(ProfileLoading()); - ProfileInformation? profileInformation = - await ProfileService.instance.getProfile(event.token, event.userID); - _profileInformation = profileInformation; - emit(ProfileLoaded( - profileInformation: _profileInformation!)); + emit(ProfileLoading()); + ProfileInformation? profileInformation = + await ProfileService.instance.getProfile(event.token, event.userID); + _profileInformation = profileInformation; + emit(ProfileLoaded(profileInformation: _profileInformation!)); // } catch (e) { // emit(ProfileErrorState(mesage: e.toString())); // } }); - on((event,emit){ - emit(ProfileLoading()); - emit(EligibilityLoaded(eligibilities: event.eligibilities)); + on((event, emit) { + emit(ProfileLoading()); + emit(EligibilityLoaded(eligibilities: event.eligibilities)); + }); + on((event, emit) { + emit(EditEligibilityState(eligibityCert: event.eligibityCert)); }); } } diff --git a/lib/bloc/profile/profile_event.dart b/lib/bloc/profile/profile_event.dart index 83d1f28..0548743 100644 --- a/lib/bloc/profile/profile_event.dart +++ b/lib/bloc/profile/profile_event.dart @@ -28,3 +28,11 @@ class LoadEligibility extends ProfileEvent{ List get props => []; } +class EditEligibility extends ProfileEvent{ + final EligibityCert eligibityCert; + const EditEligibility({required this.eligibityCert}); + @override + List get props => []; +} + + diff --git a/lib/bloc/profile/profile_state.dart b/lib/bloc/profile/profile_state.dart index fd1f36a..f81cf60 100644 --- a/lib/bloc/profile/profile_state.dart +++ b/lib/bloc/profile/profile_state.dart @@ -34,3 +34,9 @@ class EligibilityLoaded extends ProfileState{ List get props => [eligibilities]; } +class EditEligibilityState extends ProfileState{ + final EligibityCert eligibityCert; + const EditEligibilityState({required this.eligibityCert}); + @override + List get props => [eligibityCert]; +} diff --git a/lib/screens/profile/components/eligibility/eligibility_screen.dart b/lib/screens/profile/components/eligibility/eligibility_screen.dart index 19f1d39..703629d 100644 --- a/lib/screens/profile/components/eligibility/eligibility_screen.dart +++ b/lib/screens/profile/components/eligibility/eligibility_screen.dart @@ -1,182 +1,114 @@ -import 'package:app_popup_menu/app_popup_menu.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:fluttericon/font_awesome_icons.dart'; -import 'package:unit2/bloc/profile/profile_bloc.dart'; -import 'package:unit2/bloc/user/user_bloc.dart'; -import 'package:unit2/model/profile/eligibility.dart'; -import 'package:unit2/screens/profile/components/eligibility/edit_modal.dart'; -import 'package:unit2/theme-data.dart/box_shadow.dart'; -import 'package:unit2/theme-data.dart/colors.dart'; -import 'package:unit2/utils/alerts.dart'; -import 'package:unit2/utils/global.dart'; -import 'package:unit2/utils/text_container.dart'; -import 'package:unit2/widgets/add_leading.dart'; -import 'package:unit2/widgets/empty_data.dart'; +// import 'package:app_popup_menu/app_popup_menu.dart'; +// import 'package:flutter/material.dart'; +// import 'package:flutter/src/widgets/framework.dart'; +// import 'package:flutter/src/widgets/placeholder.dart'; +// import 'package:flutter_bloc/flutter_bloc.dart'; +// import 'package:fluttericon/font_awesome_icons.dart'; +// import 'package:unit2/bloc/profile/profile_bloc.dart'; +// import 'package:unit2/bloc/user/user_bloc.dart'; +// import 'package:unit2/model/profile/eligibility.dart'; +// import 'package:unit2/screens/profile/components/eligibility/edit_modal.dart'; +// import 'package:unit2/theme-data.dart/box_shadow.dart'; +// import 'package:unit2/theme-data.dart/colors.dart'; +// import 'package:unit2/utils/alerts.dart'; +// import 'package:unit2/utils/global.dart'; +// import 'package:unit2/utils/text_container.dart'; +// import 'package:unit2/widgets/add_leading.dart'; +// import 'package:unit2/widgets/empty_data.dart'; -class EligibiltyScreen extends StatefulWidget { - const EligibiltyScreen({super.key,}); +// class EligibiltyScreen extends StatefulWidget { +// const EligibiltyScreen({ +// super.key, +// }); - @override - State createState() => _EligibiltyScreenState(); -} +// @override +// State createState() => _EligibiltyScreenState(); +// } -class _EligibiltyScreenState extends State { - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text(elibilityScreenTitle), - centerTitle: true, - backgroundColor: primary, - actions: context.read()[AddLeading( - onPressed: () => () {}, - )], - ), - body: BlocBuilder( - builder: (context, state) { - return BlocBuilder( - builder: (context, state) { - if(state is EligibilityLoaded){ -return ListView.builder( - padding: - const EdgeInsets.symmetric(vertical: 8, horizontal: 10), - itemCount: state.eligibilities.length, - itemBuilder: (BuildContext context, int index) { - String title = - state.eligibilities[index].eligibility!.title!; - return Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: screenWidth, - padding: const EdgeInsets.symmetric( - horizontal: 12, vertical: 8), - decoration: box1(), - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - title, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith( - fontWeight: FontWeight.w500), - ), - const Divider(), - const SizedBox( - height: 5, - ), - Text( - "$licenseNumber: ${state.eligibilities[index].licenseNumber == null ? 'N/A' : state.eligibilities[index].licenseNumber.toString()}", - style: Theme.of(context) - .textTheme - .titleSmall), - const SizedBox( - height: 3, - ), - Text( - " : ${state.eligibilities[index].rating ?? 'N/A'}.", - style: Theme.of(context) - .textTheme - .titleSmall) - ]), - ), - AppPopupMenu( - offset: const Offset(-10, -10), - elevation: 3, - onSelected: (value) { - // if (value == 1) { - // confirmAlert(context, () => null, - // "Delete?", "Confirm Delete?"); - // } - // if (value == 2) { - // context.read().add( - // EditEligibility( - // eligibityCert: widget - // .eligibilities[index])); - // } - }, - menuItems: [ - PopupMenuItem( - value: 1, - child: Row( - children: const [ - Icon( - Icons.delete, - ), - SizedBox( - width: 10, - ), - Text( - 'Delete', - ), - ], - ), - ), - PopupMenuItem( - value: 2, - child: Row( - children: const [ - Icon( - Icons.edit, - ), - SizedBox( - width: 10, - ), - Text( - 'Edit', - ), - ], - ), - ), - PopupMenuItem( - value: 2, - child: Row( - children: const [ - Icon( - FontAwesome.attach, - ), - SizedBox( - width: 10, - ), - Text( - 'Attachment', - ), - ], - ), - ), - ], - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - ), - tooltip: "Options", - ) - ], - ), - ), - const SizedBox( - height: 5, - ) - ], - ); - }); - } - return Container(); - }, - ); - }, - )); - } -} +// class _EligibiltyScreenState extends State { +// @override +// Widget build(BuildContext context) { +// return Scaffold( +// appBar: AppBar( +// title: const Text(elibilityScreenTitle), +// centerTitle: true, +// backgroundColor: primary, +// actions: context.read()[AddLeading( +// onPressed: () => () {}, +// )], +// ), +// body: BlocBuilder( +// builder: (context, state) { +// return BlocBuilder( +// builder: (context, state) { +// if (state is EligibilityLoaded) { +// return ListView.builder( +// padding: const EdgeInsets.symmetric( +// vertical: 8, horizontal: 10), +// itemCount: state.eligibilities.length, +// itemBuilder: (BuildContext context, int index) { +// String title = +// state.eligibilities[index].eligibility!.title!; +// return Column( +// mainAxisAlignment: MainAxisAlignment.start, +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Container( +// width: screenWidth, +// padding: const EdgeInsets.symmetric( +// horizontal: 12, vertical: 8), +// decoration: box1(), +// child: Row( +// children: [ +// Expanded( +// child: Column( +// mainAxisAlignment: +// MainAxisAlignment.start, +// crossAxisAlignment: +// CrossAxisAlignment.start, +// children: [ +// Text( +// title, +// style: Theme.of(context) +// .textTheme +// .titleMedium! +// .copyWith( +// fontWeight: +// FontWeight.w500), +// ), +// const Divider(), +// const SizedBox( +// height: 5, +// ), +// Text( +// "$licenseNumber: ${state.eligibilities[index].licenseNumber == null ? 'N/A' : state.eligibilities[index].licenseNumber.toString()}", +// style: Theme.of(context) +// .textTheme +// .titleSmall), +// const SizedBox( +// height: 3, +// ), +// Text( +// " : ${state.eligibilities[index].rating ?? 'N/A'}.", +// style: Theme.of(context) +// .textTheme +// .titleSmall) +// ]), +// ), +// ] +// ), +// ), +// const SizedBox( +// height: 5, +// ) +// ], +// ); +// }); +// } +// return Container(); +// }, +// ); +// }, +// )); +// } +// } diff --git a/lib/screens/profile/components/eligibility_screen.dart b/lib/screens/profile/components/eligibility_screen.dart index 6f5148f..74af261 100644 --- a/lib/screens/profile/components/eligibility_screen.dart +++ b/lib/screens/profile/components/eligibility_screen.dart @@ -1,7 +1,13 @@ +import 'package:app_popup_menu/app_popup_menu.dart'; import 'package:flutter/material.dart'; import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/eligibility.dart'; +import 'package:unit2/screens/profile/components/eligibility/edit_modal.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; @@ -9,61 +15,151 @@ import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; -class EligibiltyScreen extends StatelessWidget { - final List eligibilities; - const EligibiltyScreen({super.key, required this.eligibilities}); +import '../../../utils/alerts.dart'; +class EligibiltyScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: const Text(elibilityScreenTitle), - centerTitle: true, - backgroundColor: primary, - actions: [AddLeading(onPressed: (){})], - ), - body: eligibilities.isNotEmpty? ListView.builder( - padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 10), - itemCount: eligibilities.length, - itemBuilder: (BuildContext context, int index) { - String title = eligibilities[index].eligibility!.title!; - return Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: screenWidth, - padding: - const EdgeInsets.symmetric(horizontal: 12, vertical: 8), - decoration: box1(), - child: Row( - children: [ - Expanded( - child: Column( + appBar: AppBar( + title: const Text(elibilityScreenTitle), + centerTitle: true, + backgroundColor: primary, + actions: [AddLeading(onPressed: () {})], + ), + body: BlocBuilder( + builder: (context, state) { + if (state is UserLoggedIn) { + return BlocBuilder( + builder: (context, state) { + if (state is EligibilityLoaded) { + return ListView.builder( + padding: const EdgeInsets.symmetric( + vertical: 8, horizontal: 10), + itemCount: state.eligibilities.length, + itemBuilder: (BuildContext context, int index) { + String title = + state.eligibilities[index].eligibility!.title!; + return Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - title,style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500), - ), - const Divider(), - const SizedBox(height: 5,), - Text( - "$licenseNumber: ${eligibilities[index].licenseNumber == null ? 'N/A' : eligibilities[index].licenseNumber.toString()}",style: Theme.of(context).textTheme.titleSmall), - const SizedBox(height: 3,), - Text( - "$rating : ${eligibilities[index].rating}.",style: Theme.of(context).textTheme.titleSmall) - ]), - ), - IconButton( - onPressed: () {}, icon: const Icon(Icons.more_vert,color: Colors.grey,)) - ], - ), - ), - const SizedBox(height: 5,) - ], - ); - }):const EmptyData(message: "You don't have any Eligibility added. Please click + to add."), + Container( + width: screenWidth, + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + decoration: box1(), + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + title, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight.w500), + ), + const Divider(), + const SizedBox( + height: 5, + ), + Text( + "$licenseNumber: ${state.eligibilities[index].licenseNumber == null ? 'N/A' : state.eligibilities[index].licenseNumber.toString()}", + style: Theme.of(context) + .textTheme + .titleSmall), + const SizedBox( + height: 3, + ), + Text( + " : ${state.eligibilities[index].rating}.", + style: Theme.of(context) + .textTheme + .titleSmall) + ]), + ), + AppPopupMenu( + offset: const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + if (value == 2) { + confirmAlert(context, () => null, + "Delete?", "Confirm Delete?"); + } + if (value == 1) { + context.read().add( + EditEligibility( + eligibityCert: state + .eligibilities[index])); + } + }, + menuItems: [ + popMenuItem( + text: "Edit", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Delete", + value: 2, + icon: Icons.delete), + popMenuItem( + text: "Attachment", + value: 3, + icon: FontAwesome.attach) + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ) + ], + ), + ), + const SizedBox( + height: 5, + ) + ], + ); + }); + } + if (state is EditEligibilityState) { + return EditEligibilityScreen( + eligibityCert: state.eligibityCert); + } + return Container(); + }, + ); + } + return Container(); + }, + )); + } + + PopupMenuItem popMenuItem({String? text, int? value, IconData? icon}) { + return PopupMenuItem( + value: value, + child: Row( + children: [ + Icon( + icon, + ), + const SizedBox( + width: 10, + ), + Text( + text!, + ), + ], + ), ); } } diff --git a/lib/screens/profile/profile.dart b/lib/screens/profile/profile.dart index db097ad..1049507 100644 --- a/lib/screens/profile/profile.dart +++ b/lib/screens/profile/profile.dart @@ -173,8 +173,7 @@ class _ProfileInfoState extends State { return BlocProvider.value(value: ProfileBloc()..add(LoadEligibility(eligibilities: state.profileInformation.eligibilities)), child: EligibiltyScreen( - eligibilities: state - .profileInformation.eligibilities), + ), ); })); }, diff --git a/lib/screens/unit2/homepage.dart/components/menu.dart b/lib/screens/unit2/homepage.dart/components/menu.dart index b26cafe..17922a4 100644 --- a/lib/screens/unit2/homepage.dart/components/menu.dart +++ b/lib/screens/unit2/homepage.dart/components/menu.dart @@ -20,7 +20,8 @@ Widget getTile( if (title.toLowerCase() == "logout") { confirmAlert(context, () { Navigator.pushReplacementNamed (context,"/"); - }); + + },"Logout","Are You sure you want to logout?"); }if(title.toLowerCase() == 'profile'){ ProfileArguments profileArguments = ProfileArguments(token: userData.user!.login!.token!, userID:userData.user!.login!.user!.profileId!); Navigator.pushNamed(context, route,arguments: profileArguments); diff --git a/lib/utils/alerts.dart b/lib/utils/alerts.dart index a9d488d..2a39072 100644 --- a/lib/utils/alerts.dart +++ b/lib/utils/alerts.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; -confirmAlert(context, Function() yes) { +confirmAlert(context, Function() yes,String title, String subtitle) { AwesomeDialog( context: context, dialogType: DialogType.question, @@ -26,8 +26,8 @@ confirmAlert(context, Function() yes) { // }, headerAnimationLoop: false, animType: AnimType.bottomSlide, - title: 'LOGOUT!', - desc: 'Are you sure you want to logout?', + title: title, + desc: subtitle, btnOkText: "Yes", btnCancelText: "No", showCloseIcon: false, From e7d5e933dd38cd25fe6985f0ce967dcf1ab2c221 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Wed, 15 Feb 2023 16:48:34 +0800 Subject: [PATCH 34/86] add location utilities and profile utilities services --- lib/bloc/profile/profile_bloc.dart | 17 +- lib/bloc/profile/profile_state.dart | 5 +- lib/model/location/barangay.dart | 44 +- lib/model/location/city.dart | 34 +- lib/model/location/country.dart | 8 +- lib/model/location/provinces.dart | 20 +- lib/model/location/region.dart | 6 +- lib/model/utils/eligibilities_choices.dart | 10 +- .../components/eligibility/edit_modal.dart | 415 ++++++++++-------- .../components/eligibility_screen.dart | 2 + lib/utils/location_utilities.dart | 61 +++ lib/utils/profile_utilities.dart | 38 ++ lib/utils/urls.dart | 11 + 13 files changed, 418 insertions(+), 253 deletions(-) create mode 100644 lib/utils/location_utilities.dart create mode 100644 lib/utils/profile_utilities.dart diff --git a/lib/bloc/profile/profile_bloc.dart b/lib/bloc/profile/profile_bloc.dart index d0cc5bf..2a174ee 100644 --- a/lib/bloc/profile/profile_bloc.dart +++ b/lib/bloc/profile/profile_bloc.dart @@ -4,7 +4,12 @@ import 'package:unit2/model/profile/basic_info.dart'; import 'package:unit2/model/profile/basic_information/primary-information.dart'; import 'package:unit2/model/profile/eligibility.dart'; import 'package:unit2/model/profile/profileInfomation.dart'; +import 'package:unit2/model/utils/eligibilities_choices.dart'; import 'package:unit2/sevices/profile/profile_service.dart'; +import 'package:unit2/utils/location_utilities.dart'; +import 'package:unit2/utils/profile_utilities.dart'; +import '../../model/location/country.dart' as country; +import '../../model/location/region.dart' as region; part 'profile_event.dart'; part 'profile_state.dart'; @@ -28,8 +33,16 @@ class ProfileBloc extends Bloc { emit(ProfileLoading()); emit(EligibilityLoaded(eligibilities: event.eligibilities)); }); - on((event, emit) { - emit(EditEligibilityState(eligibityCert: event.eligibityCert)); + on((event, emit) async{ + // try{ + List countries = await LocationUtils.instance.getCountries(); + List regions = await LocationUtils.instance.getRegions(); + List eligibilities = await ProfileUtilities.instance.getEligibilities(); + emit(EditEligibilityState(eligibityCert: event.eligibityCert,countries: countries,regions: regions,eligibilities: eligibilities)); + // }catch(e){ + // emit(ProfileErrorState(mesage: e.toString())); + // } + }); } } diff --git a/lib/bloc/profile/profile_state.dart b/lib/bloc/profile/profile_state.dart index f81cf60..a992a2e 100644 --- a/lib/bloc/profile/profile_state.dart +++ b/lib/bloc/profile/profile_state.dart @@ -36,7 +36,10 @@ class EligibilityLoaded extends ProfileState{ class EditEligibilityState extends ProfileState{ final EligibityCert eligibityCert; - const EditEligibilityState({required this.eligibityCert}); + final List eligibilities; + final List countries; + final List regions; + const EditEligibilityState({required this.eligibityCert, required this.eligibilities, required this.countries, required this.regions}); @override List get props => [eligibityCert]; } diff --git a/lib/model/location/barangay.dart b/lib/model/location/barangay.dart index a73735d..a56f2f2 100644 --- a/lib/model/location/barangay.dart +++ b/lib/model/location/barangay.dart @@ -16,20 +16,20 @@ class Barangay { required this.cityMunicipality, }); - final String code; - final String description; - final CityMunicipality cityMunicipality; + final String? code; + final String? description; + final CityMunicipality? cityMunicipality; factory Barangay.fromJson(Map json) => Barangay( code: json["code"], description: json["description"], - cityMunicipality: CityMunicipality.fromJson(json["city_municipality"]), + cityMunicipality: json['city_municipality'] == null? null: CityMunicipality.fromJson(json["city_municipality"]), ); Map toJson() => { "code": code, "description": description, - "city_municipality": cityMunicipality.toJson(), + "city_municipality": cityMunicipality!.toJson(), }; } @@ -42,16 +42,16 @@ class CityMunicipality { required this.zipcode, }); - final String code; - final String description; - final Province province; - final String psgcCode; - final String zipcode; + final String? code; + final String? description; + final Province? province; + final String? psgcCode; + final String? zipcode; factory CityMunicipality.fromJson(Map json) => CityMunicipality( code: json["code"], description: json["description"], - province: Province.fromJson(json["province"]), + province: json['province'] == null? null:Province.fromJson(json["province"]), psgcCode: json["psgc_code"], zipcode: json["zipcode"], ); @@ -59,7 +59,7 @@ class CityMunicipality { Map toJson() => { "code": code, "description": description, - "province": province.toJson(), + "province": province!.toJson(), "psgc_code": psgcCode, "zipcode": zipcode, }; @@ -74,16 +74,16 @@ class Province { required this.shortname, }); - final String code; - final String description; - final Region region; - final String psgcCode; - final String shortname; + final String? code; + final String? description; + final Region? region; + final String? psgcCode; + final String? shortname; factory Province.fromJson(Map json) => Province( code: json["code"], description: json["description"], - region: Region.fromJson(json["region"]), + region: json['region'] == null? null: Region.fromJson(json["region"]), psgcCode: json["psgc_code"], shortname: json["shortname"], ); @@ -91,7 +91,7 @@ class Province { Map toJson() => { "code": code, "description": description, - "region": region.toJson(), + "region": region!.toJson(), "psgc_code": psgcCode, "shortname": shortname, }; @@ -104,9 +104,9 @@ class Region { required this.psgcCode, }); - final int code; - final String description; - final String psgcCode; + final int? code; + final String? description; + final String? psgcCode; factory Region.fromJson(Map json) => Region( code: json["code"], diff --git a/lib/model/location/city.dart b/lib/model/location/city.dart index 8d9014c..e946098 100644 --- a/lib/model/location/city.dart +++ b/lib/model/location/city.dart @@ -18,16 +18,16 @@ class City { required this.zipcode, }); - final String code; - final String description; - final Province province; - final String psgcCode; - final String zipcode; + final String? code; + final String? description; + final Province? province; + final String? psgcCode; + final String? zipcode; factory City.fromJson(Map json) => City( code: json["code"], description: json["description"], - province: Province.fromJson(json["province"]), + province: json['province'] == null? null : Province.fromJson(json["province"]), psgcCode: json["psgc_code"], zipcode: json["zipcode"], ); @@ -35,7 +35,7 @@ class City { Map toJson() => { "code": code, "description": description, - "province": province.toJson(), + "province": province!.toJson(), "psgc_code": psgcCode, "zipcode": zipcode, }; @@ -50,16 +50,16 @@ class Province { required this.shortname, }); - final String code; - final String description; - final Region region; - final String psgcCode; - final String shortname; + final String? code; + final String? description; + final Region? region; + final String? psgcCode; + final String? shortname; factory Province.fromJson(Map json) => Province( code: json["code"], description: json["description"], - region: Region.fromJson(json["region"]), + region: json['region'] == null ? null : Region.fromJson(json["region"]), psgcCode: json["psgc_code"], shortname: json["shortname"], ); @@ -67,7 +67,7 @@ class Province { Map toJson() => { "code": code, "description": description, - "region": region.toJson(), + "region": region!.toJson(), "psgc_code": psgcCode, "shortname": shortname, }; @@ -80,9 +80,9 @@ class Region { required this.psgcCode, }); - final int code; - final String description; - final String psgcCode; + final int? code; + final String? description; + final String? psgcCode; factory Region.fromJson(Map json) => Region( code: json["code"], diff --git a/lib/model/location/country.dart b/lib/model/location/country.dart index c3a98ca..e1136b1 100644 --- a/lib/model/location/country.dart +++ b/lib/model/location/country.dart @@ -16,14 +16,14 @@ class Country { required this.code, }); - final int id; - final String name; - final String code; + final int? id; + final String? name; + final String? code; factory Country.fromJson(Map json) => Country( id: json["id"], name: json["name"], - code: json["code"], + code: json["code"].toString(), ); Map toJson() => { diff --git a/lib/model/location/provinces.dart b/lib/model/location/provinces.dart index 8d7ee19..d13da9a 100644 --- a/lib/model/location/provinces.dart +++ b/lib/model/location/provinces.dart @@ -18,16 +18,16 @@ class Province { required this.shortname, }); - final String code; - final String description; - final Region region; - final String psgcCode; - final String shortname; + final String? code; + final String? description; + final Region? region; + final String? psgcCode; + final String? shortname; factory Province.fromJson(Map json) => Province( code: json["code"], description: json["description"], - region: Region.fromJson(json["region"]), + region: json['region'] == null? null: Region.fromJson(json["region"]), psgcCode: json["psgc_code"], shortname: json["shortname"], ); @@ -35,7 +35,7 @@ class Province { Map toJson() => { "code": code, "description": description, - "region": region.toJson(), + "region": region!.toJson(), "psgc_code": psgcCode, "shortname": shortname, }; @@ -48,9 +48,9 @@ class Region { required this.psgcCode, }); - final int code; - final String description; - final String psgcCode; + final int? code; + final String? description; + final String? psgcCode; factory Region.fromJson(Map json) => Region( code: json["code"], diff --git a/lib/model/location/region.dart b/lib/model/location/region.dart index 718df02..99a7c8d 100644 --- a/lib/model/location/region.dart +++ b/lib/model/location/region.dart @@ -16,9 +16,9 @@ class Region { required this.psgcCode, }); - final int code; - final String description; - final String psgcCode; + final int? code; + final String? description; + final String? psgcCode; factory Region.fromJson(Map json) => Region( code: json["code"], diff --git a/lib/model/utils/eligibilities_choices.dart b/lib/model/utils/eligibilities_choices.dart index 24b963d..eae00f0 100644 --- a/lib/model/utils/eligibilities_choices.dart +++ b/lib/model/utils/eligibilities_choices.dart @@ -5,12 +5,12 @@ import 'package:meta/meta.dart'; import 'dart:convert'; -Eligibilities eligibilitiesFromJson(String str) => Eligibilities.fromJson(json.decode(str)); +EligibilityList eligibilitiesFromJson(String str) => EligibilityList.fromJson(json.decode(str)); -String eligibilitiesToJson(Eligibilities data) => json.encode(data.toJson()); +String eligibilitiesToJson(EligibilityList data) => json.encode(data.toJson()); -class Eligibilities { - Eligibilities({ +class EligibilityList { + EligibilityList({ required this.id, required this.title, required this.type, @@ -20,7 +20,7 @@ class Eligibilities { final String title; final String type; - factory Eligibilities.fromJson(Map json) => Eligibilities( + factory EligibilityList.fromJson(Map json) => EligibilityList( id: json["id"], title: json["title"], type: json["type"], diff --git a/lib/screens/profile/components/eligibility/edit_modal.dart b/lib/screens/profile/components/eligibility/edit_modal.dart index 0a60f62..c41c4aa 100644 --- a/lib/screens/profile/components/eligibility/edit_modal.dart +++ b/lib/screens/profile/components/eligibility/edit_modal.dart @@ -2,10 +2,13 @@ import 'package:date_time_picker/date_time_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:intl/intl.dart'; +import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/eligibility.dart'; - +import '../../../../model/location/country.dart' as c; import '../../../../theme-data.dart/btn-style.dart'; import '../../../../theme-data.dart/colors.dart'; import '../../../../theme-data.dart/form-style.dart'; @@ -14,212 +17,246 @@ import '../../../../utils/text_container.dart'; class EditEligibilityScreen extends StatefulWidget { final EligibityCert eligibityCert; - const EditEligibilityScreen({super.key,required this.eligibityCert}); + const EditEligibilityScreen({super.key, required this.eligibityCert}); @override State createState() => _EditEligibilityScreenState(); } class _EditEligibilityScreenState extends State { - final formKey = GlobalKey(); - bool overseas =false; - DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); + final formKey = GlobalKey(); + bool overseas = false; + DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); + c.Country? selectedCountry; @override Widget build(BuildContext context) { - return Center( - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 18), - child: FormBuilder( - key: formKey, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - FormBuilderTextField( - name: "eligibility", - initialValue: widget.eligibityCert.eligibility!.title!, - decoration: - normalTextFieldStyle("Eligibility", "Eligibility"), - ), - const SizedBox( - height: 20, - ), - SizedBox( - width: screenWidth, - child: Row( + return BlocBuilder( + builder: (context, state) { + if (state is UserLoggedIn) { + return BlocBuilder( + builder: (context, state) { + if (state is EditEligibilityState) { + return Center( + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, horizontal: 18), + child: FormBuilder( + key: formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, children: [ - Flexible( - flex: 1, - child: FormBuilderTextField( - name: 'license number', - initialValue: - widget.eligibityCert.licenseNumber, - decoration: normalTextFieldStyle( - "license number", "license number"), - ), + FormBuilderTextField( + name: "eligibility", + initialValue: + widget.eligibityCert.eligibility!.title!, + decoration: normalTextFieldStyle( + "Eligibility", "Eligibility"), ), const SizedBox( - width: 12, + height: 20, ), - Flexible( - flex: 1, - child: FormBuilderTextField( - name: 'rating', - initialValue: - widget.eligibityCert.rating.toString(), - decoration: - normalTextFieldStyle('rating', 'rating'), - ), - ), - ], - ), - ), - const SizedBox( - height: 20, - ), - SizedBox( - width: screenWidth, - child: Row( - children: [ - Flexible( - flex: 1, - child: DateTimePicker( - firstDate: DateTime(2000), - lastDate: DateTime(2100), - decoration: normalTextFieldStyle( - "Exam date", "Exam date"), - initialValue: - widget.eligibityCert.examDate == null - ? '' - : dteFormat2.format( - widget.eligibityCert.examDate!), - )), - const SizedBox( - width: 12, - ), - Flexible( - flex: 1, - child: DateTimePicker( - firstDate: DateTime(2000), - lastDate: DateTime(2100), - decoration: normalTextFieldStyle( - "Validity date", "Validity date"), - initialValue: - widget.eligibityCert.validityDate == null - ? '' - : dteFormat2.format( - widget.eligibityCert.validityDate!), - ), - ), - ], - ), - ), - const SizedBox( - height: 20, - ), - Text( - "Placement of Examination/Confinement", - style: Theme.of(context) - .textTheme - .displaySmall! - .copyWith(fontSize: blockSizeVertical * 2), - ), - const SizedBox( - height: 12, - ), - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - setState(() { - overseas = value!; - }); - }, - decoration: normalTextFieldStyle("", ''), - name: 'overseas', - title: const Text("Overseas Address?"), - ), - const SizedBox( - height: 20, - ), - SizedBox( - child: overseas == true - ? FormBuilderTextField( - name: 'country', - decoration: normalTextFieldStyle( - "Country", "Country"), - ) - : Column( - children: [ - FormBuilderDropdown( + SizedBox( + width: screenWidth, + child: Row( + children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + name: 'license number', + initialValue: + widget.eligibityCert.licenseNumber, decoration: normalTextFieldStyle( - "Region", "Region"), - name: 'region', - items: [], - initialValue: widget - .eligibityCert - .examAddress - ?.cityMunicipality - ?.province - ?.region - ?.description == - null - ? 'region' - : 'region', + "license number", "license number"), ), - const SizedBox( - height: 20, + ), + const SizedBox( + width: 12, + ), + Flexible( + flex: 1, + child: FormBuilderTextField( + name: 'rating', + initialValue: widget.eligibityCert.rating + .toString(), + decoration: normalTextFieldStyle( + 'rating', 'rating'), ), - FormBuilderDropdown( + ), + ], + ), + ), + const SizedBox( + height: 20, + ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + Flexible( + flex: 1, + child: DateTimePicker( + firstDate: DateTime(2000), + lastDate: DateTime(2100), decoration: normalTextFieldStyle( - 'Province', "Province"), - name: 'province', - items: [], + "Exam date", "Exam date"), initialValue: widget - .eligibityCert - .examAddress - ?.cityMunicipality - ?.province - ?.description == + .eligibityCert.examDate == null - ? 'region' - : 'pprovince'), - const SizedBox( - height: 20, - ), - FormBuilderDropdown( + ? '' + : dteFormat2.format( + widget.eligibityCert.examDate!), + )), + const SizedBox( + width: 12, + ), + Flexible( + flex: 1, + child: DateTimePicker( + firstDate: DateTime(2000), + lastDate: DateTime(2100), decoration: normalTextFieldStyle( - "Municipality", "Municipality"), - name: 'municipality', - items: [], - initialValue: widget - .eligibityCert - .examAddress - ?.cityMunicipality - ?.description == - null - ? 'region' - : 'municipality', + "Validity date", "Validity date"), + initialValue: + widget.eligibityCert.validityDate == + null + ? '' + : dteFormat2.format(widget + .eligibityCert.validityDate!), ), - ], - )), - const SizedBox( - height: 20, - ), - SizedBox( - width: screenWidth, - height: 60, - child: ElevatedButton( - style: mainBtnStyle( - primary, Colors.transparent, second), - onPressed: () {}, - child: const Text(submit)), - ), - const SizedBox(height: 20,), - - ]), - ), - ), - );; + ), + ], + ), + ), + const SizedBox( + height: 20, + ), + Text( + "Placement of Examination/Confinement", + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith(fontSize: blockSizeVertical * 2), + ), + const SizedBox( + height: 12, + ), + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Overseas Address?"), + ), + const SizedBox( + height: 20, + ), + SizedBox( + child: overseas == true + ? FormBuilderDropdown( + items: state.countries + .map>( + (c.Country country) { + + return DropdownMenuItem( + value: country, + child: Text(country.name!)); + }).toList(), + name: 'country', + decoration: normalTextFieldStyle( + "Country", "Country"), + onChanged: (value) { + setState(() { + selectedCountry = value; + }); + }, + ) + : Column( + children: [ + FormBuilderDropdown( + decoration: normalTextFieldStyle( + "Region", "Region"), + name: 'region', + items: [], + initialValue: widget + .eligibityCert + .examAddress + ?.cityMunicipality + ?.province + ?.region + ?.description == + null + ? 'region' + : 'region', + ), + const SizedBox( + height: 20, + ), + FormBuilderDropdown( + decoration: normalTextFieldStyle( + 'Province', "Province"), + name: 'province', + items: [], + initialValue: widget + .eligibityCert + .examAddress + ?.cityMunicipality + ?.province + ?.description == + null + ? 'region' + : 'pprovince'), + const SizedBox( + height: 20, + ), + FormBuilderDropdown( + decoration: normalTextFieldStyle( + "Municipality", "Municipality"), + name: 'municipality', + items: [], + initialValue: widget + .eligibityCert + .examAddress + ?.cityMunicipality + ?.description == + null + ? 'region' + : 'municipality', + ), + ], + )), + const SizedBox( + height: 20, + ), + SizedBox( + width: screenWidth, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () {}, + child: const Text(submit)), + ), + const SizedBox( + height: 20, + ), + ]), + ), + ), + ); + } + return Container(); + }, + ); + } + return Container(); + }, + ); + ; } -} \ No newline at end of file +} diff --git a/lib/screens/profile/components/eligibility_screen.dart b/lib/screens/profile/components/eligibility_screen.dart index 74af261..5ba4448 100644 --- a/lib/screens/profile/components/eligibility_screen.dart +++ b/lib/screens/profile/components/eligibility_screen.dart @@ -18,6 +18,8 @@ import 'package:unit2/widgets/empty_data.dart'; import '../../../utils/alerts.dart'; class EligibiltyScreen extends StatelessWidget { + const EligibiltyScreen({super.key}); + @override Widget build(BuildContext context) { return Scaffold( diff --git a/lib/utils/location_utilities.dart b/lib/utils/location_utilities.dart new file mode 100644 index 0000000..8501ec5 --- /dev/null +++ b/lib/utils/location_utilities.dart @@ -0,0 +1,61 @@ +import 'dart:convert'; + +import 'package:unit2/model/location/country.dart'; +import 'package:http/http.dart' as http; +import 'package:unit2/utils/request.dart'; +import 'package:unit2/utils/urls.dart'; + +import '../model/location/region.dart'; +class LocationUtils { + static final LocationUtils _instance = LocationUtils(); + static LocationUtils get instance => _instance; + + Future>getCountries()async{ + List countries=[]; + String path = Url.instance.getCounties(); + + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + // try{ + http.Response response = await Request.instance.getRequest(path: path, param: {},headers: headers); + if(response.statusCode == 200){ + Map data = jsonDecode(response.body); + if(data['data'] != null){ + data['data'].forEach((var country){ + Country newCOuntry = Country.fromJson(country); + countries.add(newCOuntry); + }); + } + } + // }catch(e){ + // throw(e.toString()); + // } + return countries; + } + + +Future>getRegions()async{ + List regions=[]; + String path = Url.instance.getRegions(); + + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + // try{ + http.Response response = await Request.instance.getRequest(path: path, param: {},headers: headers); + if(response.statusCode == 200){ + Map data = jsonDecode(response.body); + if(data['data'] != null){ + data['data'].forEach((var region){ + Region newRegion = Region.fromJson(region); + regions.add(newRegion); + }); + } + } + // }catch(e){ + // throw(e.toString()); + // } + return regions; + } +} \ No newline at end of file diff --git a/lib/utils/profile_utilities.dart b/lib/utils/profile_utilities.dart new file mode 100644 index 0000000..e13e625 --- /dev/null +++ b/lib/utils/profile_utilities.dart @@ -0,0 +1,38 @@ + +import 'dart:convert'; + +import 'package:unit2/model/location/country.dart'; +import 'package:http/http.dart' as http; +import 'package:unit2/model/location/region.dart'; +import 'package:unit2/model/utils/eligibilities_choices.dart'; +import 'package:unit2/utils/request.dart'; +import 'package:unit2/utils/urls.dart'; +class ProfileUtilities { + static final ProfileUtilities _instance = ProfileUtilities(); + static ProfileUtilities get instance => _instance; + + Future>getEligibilities()async{ + List eligibilities=[]; + String path = Url.instance.eligibilities(); + + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + // try{ + http.Response response = await Request.instance.getRequest(path: path, param: {},headers: headers); + if(response.statusCode == 200){ + Map data = jsonDecode(response.body); + if(data['data'] != null){ + data['data'].forEach((var eligibility){ + EligibilityList newEligibilities = EligibilityList.fromJson(eligibility); + eligibilities.add(newEligibilities); + }); + } + } + // }catch(e){ + // throw(e.toString()); + // } + return eligibilities; + } + +} \ No newline at end of file diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index cee712a..b228757 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -15,4 +15,15 @@ class Url { String profileInformation(){ return '/api/jobnet_app/profile/pds/'; } + +String eligibilities(){ + return "/api/jobnet_app/eligibilities/"; +} + // location utils path + String getCounties(){ + return "/api/jobnet_app/countries/"; + } + String getRegions(){ + return "/api/web_app/location/region/"; + } } \ No newline at end of file From 3a53445ec635e60f0895cad0de4bf60bc7808db3 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Thu, 16 Feb 2023 15:10:54 +0800 Subject: [PATCH 35/86] commit before flutter upgrade --- lib/bloc/profile/profile_bloc.dart | 7 +- lib/bloc/profile/profile_state.dart | 3 +- lib/model/location/provinces.dart | 26 +- lib/model/profile/eligibility.dart | 2 +- .../components/eligibility/edit_modal.dart | 107 ++++++--- .../components/eligibility_screen.dart | 227 ++++++++++-------- lib/screens/unit2/login/login.dart | 4 +- lib/utils/location_utilities.dart | 34 ++- lib/utils/urls.dart | 3 + 9 files changed, 244 insertions(+), 169 deletions(-) diff --git a/lib/bloc/profile/profile_bloc.dart b/lib/bloc/profile/profile_bloc.dart index 2a174ee..2439f6e 100644 --- a/lib/bloc/profile/profile_bloc.dart +++ b/lib/bloc/profile/profile_bloc.dart @@ -10,7 +10,9 @@ import 'package:unit2/utils/location_utilities.dart'; import 'package:unit2/utils/profile_utilities.dart'; import '../../model/location/country.dart' as country; import '../../model/location/region.dart' as region; - +import '../../model/location/provinces.dart' as province; +import '../../model/location/city.dart' as city; +import '../../model/location/barangay.dart' as barangay; part 'profile_event.dart'; part 'profile_state.dart'; @@ -38,7 +40,8 @@ class ProfileBloc extends Bloc { List countries = await LocationUtils.instance.getCountries(); List regions = await LocationUtils.instance.getRegions(); List eligibilities = await ProfileUtilities.instance.getEligibilities(); - emit(EditEligibilityState(eligibityCert: event.eligibityCert,countries: countries,regions: regions,eligibilities: eligibilities)); + List provinces = await LocationUtils.instance.getProvinces(regionCode: event.eligibityCert.examAddress!.cityMunicipality!.province!.region!.code!.toString()); + emit(EditEligibilityState(provinces: provinces, eligibityCert: event.eligibityCert,countries: countries,regions: regions,eligibilities: eligibilities)); // }catch(e){ // emit(ProfileErrorState(mesage: e.toString())); // } diff --git a/lib/bloc/profile/profile_state.dart b/lib/bloc/profile/profile_state.dart index a992a2e..8695782 100644 --- a/lib/bloc/profile/profile_state.dart +++ b/lib/bloc/profile/profile_state.dart @@ -39,7 +39,8 @@ class EditEligibilityState extends ProfileState{ final List eligibilities; final List countries; final List regions; - const EditEligibilityState({required this.eligibityCert, required this.eligibilities, required this.countries, required this.regions}); + List provinces; + EditEligibilityState({ required this.provinces,required this.eligibityCert, required this.eligibilities, required this.countries, required this.regions}); @override List get props => [eligibityCert]; } diff --git a/lib/model/location/provinces.dart b/lib/model/location/provinces.dart index d13da9a..d14881d 100644 --- a/lib/model/location/provinces.dart +++ b/lib/model/location/provinces.dart @@ -5,6 +5,8 @@ import 'package:meta/meta.dart'; import 'dart:convert'; +import 'region.dart'; + Province provinceFromJson(String str) => Province.fromJson(json.decode(str)); String provinceToJson(Province data) => json.encode(data.toJson()); @@ -40,27 +42,3 @@ class Province { "shortname": shortname, }; } - -class Region { - Region({ - required this.code, - required this.description, - required this.psgcCode, - }); - - final int? code; - final String? description; - final String? psgcCode; - - factory Region.fromJson(Map json) => Region( - code: json["code"], - description: json["description"], - psgcCode: json["psgc_code"], - ); - - Map toJson() => { - "code": code, - "description": description, - "psgc_code": psgcCode, - }; -} diff --git a/lib/model/profile/eligibility.dart b/lib/model/profile/eligibility.dart index 5999cf4..674b65e 100644 --- a/lib/model/profile/eligibility.dart +++ b/lib/model/profile/eligibility.dart @@ -36,7 +36,7 @@ class EligibityCert { examDate: json['exam_date'] == null? null: DateTime.parse(json["exam_date"]), attachments: null, eligibility: json['eligibility'] == null?null: Eligibility.fromJson(json["eligibility"]), - examAddress: json['eligibilty'] == null? null: ExamAddress.fromJson(json["exam_address"]), + examAddress: json['exam_address'] == null? null: ExamAddress.fromJson(json["exam_address"]), validityDate: json["validity_date"], licenseNumber: json["license_number"], ); diff --git a/lib/screens/profile/components/eligibility/edit_modal.dart b/lib/screens/profile/components/eligibility/edit_modal.dart index c41c4aa..c43b943 100644 --- a/lib/screens/profile/components/eligibility/edit_modal.dart +++ b/lib/screens/profile/components/eligibility/edit_modal.dart @@ -8,7 +8,10 @@ import 'package:intl/intl.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/eligibility.dart'; +import 'package:unit2/model/utils/eligibilities_choices.dart'; import '../../../../model/location/country.dart' as c; +import '../../../../model/location/region.dart' as r; +import '../../../../model/location/provinces.dart' as p; import '../../../../theme-data.dart/btn-style.dart'; import '../../../../theme-data.dart/colors.dart'; import '../../../../theme-data.dart/form-style.dart'; @@ -30,12 +33,22 @@ class _EditEligibilityScreenState extends State { c.Country? selectedCountry; @override Widget build(BuildContext context) { + //USERBLOC return BlocBuilder( builder: (context, state) { + //LOGGED IN USER STATE if (state is UserLoggedIn) { + //PROFIILE BLOC return BlocBuilder( builder: (context, state) { + //EDIT ELIGIBILITY STATE if (state is EditEligibilityState) { + String? region = state.eligibityCert.examAddress! + .cityMunicipality!.province!.region!.description; + String? eligibiltyTitle = + state.eligibityCert.eligibility!.title!; + String? province = state.eligibityCert.examAddress! + .cityMunicipality!.province!.description!; return Center( child: Padding( padding: const EdgeInsets.symmetric( @@ -46,20 +59,35 @@ class _EditEligibilityScreenState extends State { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ - FormBuilderTextField( + //ELIGIBILITIES DROPDOWN + FormBuilderDropdown( + initialValue: state.eligibityCert.eligibility, + items: state.eligibilities + .map>( + (EligibilityList eligibility) { + return DropdownMenuItem( + value: eligibility, + child: Text(eligibility.title)); + }).toList(), name: "eligibility", - initialValue: - widget.eligibityCert.eligibility!.title!, decoration: normalTextFieldStyle( - "Eligibility", "Eligibility"), + "Eligibility", eligibiltyTitle) + .copyWith( + hintStyle: const TextStyle( + color: Colors.black, + ), + labelStyle: + const TextStyle(color: Colors.black)), ), const SizedBox( height: 20, ), + SizedBox( width: screenWidth, child: Row( children: [ + //LICENSE NUMBER Flexible( flex: 1, child: FormBuilderTextField( @@ -73,6 +101,7 @@ class _EditEligibilityScreenState extends State { const SizedBox( width: 12, ), + //RATING Flexible( flex: 1, child: FormBuilderTextField( @@ -93,6 +122,7 @@ class _EditEligibilityScreenState extends State { width: screenWidth, child: Row( children: [ + //EXAM DATE Flexible( flex: 1, child: DateTimePicker( @@ -110,6 +140,7 @@ class _EditEligibilityScreenState extends State { const SizedBox( width: 12, ), + //VALIDITY DATE Flexible( flex: 1, child: DateTimePicker( @@ -141,6 +172,7 @@ class _EditEligibilityScreenState extends State { const SizedBox( height: 12, ), + //OVERSEAS ADDRESS SWITCH FormBuilderSwitch( initialValue: overseas, activeColor: second, @@ -156,13 +188,14 @@ class _EditEligibilityScreenState extends State { const SizedBox( height: 20, ), + //COUNTRY DROPDOWN + SizedBox( child: overseas == true ? FormBuilderDropdown( items: state.countries .map>( (c.Country country) { - return DropdownMenuItem( value: country, child: Text(country.name!)); @@ -178,39 +211,52 @@ class _EditEligibilityScreenState extends State { ) : Column( children: [ - FormBuilderDropdown( + //REGION DROPDOWN + FormBuilderDropdown( + // initialValue:state.eligibityCert.examAddress!.cityMunicipality!.province!.description!, decoration: normalTextFieldStyle( - "Region", "Region"), + "Region", region ?? + "Region*") + .copyWith( + hintStyle: const TextStyle( + color: Colors.black, + ), + labelStyle: const TextStyle( + color: Colors.black)), name: 'region', - items: [], - initialValue: widget - .eligibityCert - .examAddress - ?.cityMunicipality - ?.province - ?.region - ?.description == - null - ? 'region' - : 'region', + items: state.regions.map< + DropdownMenuItem>( + (r.Region region) { + return DropdownMenuItem( + value: region, + child: Text( + region.description!)); + }).toList(), ), const SizedBox( height: 20, ), + //PROVINCE DROPDOWN FormBuilderDropdown( - decoration: normalTextFieldStyle( - 'Province', "Province"), name: 'province', - items: [], - initialValue: widget - .eligibityCert - .examAddress - ?.cityMunicipality - ?.province - ?.description == - null - ? 'region' - : 'pprovince'), + items: state.provinces.map< + DropdownMenuItem< + p.Province>>( + (p.Province province) { + return DropdownMenuItem( + value: province, + child: Text( + province.description!)); + }).toList(), + decoration: normalTextFieldStyle( + "Province", province) + .copyWith( + hintStyle: const TextStyle( + color: Colors.black, + ), + labelStyle: const TextStyle( + color: Colors.black), + )), const SizedBox( height: 20, ), @@ -257,6 +303,5 @@ class _EditEligibilityScreenState extends State { return Container(); }, ); - ; } } diff --git a/lib/screens/profile/components/eligibility_screen.dart b/lib/screens/profile/components/eligibility_screen.dart index 5ba4448..115fd5d 100644 --- a/lib/screens/profile/components/eligibility_screen.dart +++ b/lib/screens/profile/components/eligibility_screen.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter/src/widgets/placeholder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:fluttericon/font_awesome_icons.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; @@ -32,113 +33,135 @@ class EligibiltyScreen extends StatelessWidget { body: BlocBuilder( builder: (context, state) { if (state is UserLoggedIn) { - return BlocBuilder( - builder: (context, state) { - if (state is EligibilityLoaded) { - return ListView.builder( - padding: const EdgeInsets.symmetric( - vertical: 8, horizontal: 10), - itemCount: state.eligibilities.length, - itemBuilder: (BuildContext context, int index) { - String title = - state.eligibilities[index].eligibility!.title!; - return Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: screenWidth, - padding: const EdgeInsets.symmetric( - horizontal: 12, vertical: 8), - decoration: box1(), - child: Row( + return ProgressHUD( + child: BlocConsumer( + listener: (context, state) { + if (state is EditEligibilityState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + // TODO: implement listener + }, + builder: (context, state) { + return BlocBuilder( + builder: (context, state) { + if (state is EligibilityLoaded) { + return ListView.builder( + padding: const EdgeInsets.symmetric( + vertical: 8, horizontal: 10), + itemCount: state.eligibilities.length, + itemBuilder: (BuildContext context, int index) { + String title = state + .eligibilities[index].eligibility!.title!; + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Expanded( - child: Column( - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - title, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith( - fontWeight: - FontWeight.w500), + Container( + width: screenWidth, + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + decoration: box1(), + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + title, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight + .w500), + ), + const Divider(), + const SizedBox( + height: 5, + ), + Text( + "$licenseNumber: ${state.eligibilities[index].licenseNumber == null ? 'N/A' : state.eligibilities[index].licenseNumber.toString()}", + style: Theme.of(context) + .textTheme + .titleSmall), + const SizedBox( + height: 3, + ), + Text( + " : ${state.eligibilities[index].rating}.", + style: Theme.of(context) + .textTheme + .titleSmall) + ]), + ), + AppPopupMenu( + offset: const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + if (value == 2) { + confirmAlert( + context, + () => null, + "Delete?", + "Confirm Delete?"); + } + if (value == 1) { + final progress = + ProgressHUD.of(context); + progress! + .showWithText("Loading..."); + context.read().add( + EditEligibility( + eligibityCert: + state.eligibilities[ + index])); + } + }, + menuItems: [ + popMenuItem( + text: "Edit", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Delete", + value: 2, + icon: Icons.delete), + popMenuItem( + text: "Attachment", + value: 3, + icon: FontAwesome.attach) + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, ), - const Divider(), - const SizedBox( - height: 5, - ), - Text( - "$licenseNumber: ${state.eligibilities[index].licenseNumber == null ? 'N/A' : state.eligibilities[index].licenseNumber.toString()}", - style: Theme.of(context) - .textTheme - .titleSmall), - const SizedBox( - height: 3, - ), - Text( - " : ${state.eligibilities[index].rating}.", - style: Theme.of(context) - .textTheme - .titleSmall) - ]), - ), - AppPopupMenu( - offset: const Offset(-10, -10), - elevation: 3, - onSelected: (value) { - if (value == 2) { - confirmAlert(context, () => null, - "Delete?", "Confirm Delete?"); - } - if (value == 1) { - context.read().add( - EditEligibility( - eligibityCert: state - .eligibilities[index])); - } - }, - menuItems: [ - popMenuItem( - text: "Edit", - value: 1, - icon: Icons.edit), - popMenuItem( - text: "Delete", - value: 2, - icon: Icons.delete), - popMenuItem( - text: "Attachment", - value: 3, - icon: FontAwesome.attach) - ], - icon: const Icon( - Icons.more_vert, - color: Colors.grey, + tooltip: "Options", + ) + ], ), - tooltip: "Options", + ), + const SizedBox( + height: 5, ) ], - ), - ), - const SizedBox( - height: 5, - ) - ], - ); - }); - } - if (state is EditEligibilityState) { - return EditEligibilityScreen( - eligibityCert: state.eligibityCert); - } - return Container(); - }, + ); + }); + } + if (state is EditEligibilityState) { + return EditEligibilityScreen( + eligibityCert: state.eligibityCert); + } + return Container(); + }, + ); + }, + ), ); } return Container(); diff --git a/lib/screens/unit2/login/login.dart b/lib/screens/unit2/login/login.dart index 045bcc6..cdce5fd 100644 --- a/lib/screens/unit2/login/login.dart +++ b/lib/screens/unit2/login/login.dart @@ -230,8 +230,8 @@ class _UniT2LoginState extends State { BlocProvider.of(context) .add(UserLogin( - username: "rjvincentlopeplopez", - password: "shesthequ33n", + username: "rodolfobacuinjr", + password: "nav071394", // username: _formKey // .currentState! // .value['username'], diff --git a/lib/utils/location_utilities.dart b/lib/utils/location_utilities.dart index 8501ec5..6d19119 100644 --- a/lib/utils/location_utilities.dart +++ b/lib/utils/location_utilities.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:unit2/model/location/country.dart'; import 'package:http/http.dart' as http; +import 'package:unit2/model/location/provinces.dart'; import 'package:unit2/utils/request.dart'; import 'package:unit2/utils/urls.dart'; @@ -10,13 +11,16 @@ class LocationUtils { static final LocationUtils _instance = LocationUtils(); static LocationUtils get instance => _instance; + + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + Future>getCountries()async{ List countries=[]; String path = Url.instance.getCounties(); - Map headers = { - 'Content-Type': 'application/json; charset=UTF-8', - }; + // try{ http.Response response = await Request.instance.getRequest(path: path, param: {},headers: headers); if(response.statusCode == 200){ @@ -39,9 +43,7 @@ Future>getRegions()async{ List regions=[]; String path = Url.instance.getRegions(); - Map headers = { - 'Content-Type': 'application/json; charset=UTF-8', - }; + // try{ http.Response response = await Request.instance.getRequest(path: path, param: {},headers: headers); if(response.statusCode == 200){ @@ -58,4 +60,24 @@ Future>getRegions()async{ // } return regions; } + + Future>getProvinces({required String regionCode})async{ + List provinces = []; + String path = Url.instance.getProvinces()+regionCode; + + try{ + http.Response response = await Request.instance.getRequest(path: path,param:{},headers: headers); + Map data = jsonDecode(response.body); + if(data['data'] != null){ + data['data'].forEach((var province){ + Province newProvince = Province.fromJson(province); + provinces.add(newProvince); + }); + } + + }catch(e){ + throw(e.toString()); + } + return provinces; + } } \ No newline at end of file diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index b228757..5a33754 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -26,4 +26,7 @@ String eligibilities(){ String getRegions(){ return "/api/web_app/location/region/"; } + String getProvinces(){ + return "api/web_app/location/province/"; + } } \ No newline at end of file From fb1ec643cd87f9c13e1c22ba1a8884ad662c2f21 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Mon, 20 Feb 2023 15:48:24 +0800 Subject: [PATCH 36/86] edit eligibility country error fix --- lib/bloc/profile/profile_bloc.dart | 62 ++- lib/bloc/profile/profile_event.dart | 11 +- lib/bloc/profile/profile_state.dart | 65 ++- lib/model/location/address_category.dart | 23 ++ lib/model/location/barangay.dart | 87 +--- lib/model/location/city.dart | 67 +-- lib/model/location/subdivision.dart | 23 ++ .../profile/basic_information/adress.dart | 185 +-------- .../basic_information/citizenship.dart | 26 +- .../contact_information.dart | 77 +--- .../identification_information.dart | 191 +-------- lib/model/profile/eligibility.dart | 171 +------- lib/model/profile/family_backround.dart | 69 +--- lib/model/profile/learning_development.dart | 138 +------ .../non_acedimic_recognition.dart | 49 +-- .../organization_memberships.dart | 76 +--- lib/model/profile/references.dart | 162 +------- lib/model/profile/voluntary_works.dart | 230 +---------- lib/model/profile/work_history.dart | 93 +---- lib/model/utils/agency.dart | 29 ++ lib/model/utils/category.dart | 25 ++ ...bilities_choices.dart => eligibility.dart} | 10 +- lib/model/utils/industry_class.dart | 23 ++ lib/model/utils/position.dart | 19 + .../components/eligibility/edit_modal.dart | 385 +++++++++++++----- .../components/eligibility_screen.dart | 49 ++- lib/screens/unit2/login/login.dart | 4 +- lib/utils/location_utilities.dart | 19 + lib/utils/profile_utilities.dart | 8 +- lib/utils/urls.dart | 3 + pubspec.lock | 2 +- 31 files changed, 658 insertions(+), 1723 deletions(-) create mode 100644 lib/model/location/address_category.dart create mode 100644 lib/model/location/subdivision.dart create mode 100644 lib/model/utils/agency.dart create mode 100644 lib/model/utils/category.dart rename lib/model/utils/{eligibilities_choices.dart => eligibility.dart} (62%) create mode 100644 lib/model/utils/industry_class.dart create mode 100644 lib/model/utils/position.dart diff --git a/lib/bloc/profile/profile_bloc.dart b/lib/bloc/profile/profile_bloc.dart index 2439f6e..10a5515 100644 --- a/lib/bloc/profile/profile_bloc.dart +++ b/lib/bloc/profile/profile_bloc.dart @@ -4,15 +4,15 @@ import 'package:unit2/model/profile/basic_info.dart'; import 'package:unit2/model/profile/basic_information/primary-information.dart'; import 'package:unit2/model/profile/eligibility.dart'; import 'package:unit2/model/profile/profileInfomation.dart'; -import 'package:unit2/model/utils/eligibilities_choices.dart'; +import 'package:unit2/model/utils/eligibility.dart'; import 'package:unit2/sevices/profile/profile_service.dart'; import 'package:unit2/utils/location_utilities.dart'; import 'package:unit2/utils/profile_utilities.dart'; -import '../../model/location/country.dart' as country; -import '../../model/location/region.dart' as region; -import '../../model/location/provinces.dart' as province; -import '../../model/location/city.dart' as city; -import '../../model/location/barangay.dart' as barangay; +import '../../model/location/country.dart'; +import '../../model/location/region.dart'; +import '../../model/location/provinces.dart'; +import '../../model/location/city.dart'; +import '../../model/location/barangay.dart'; part 'profile_event.dart'; part 'profile_state.dart'; @@ -35,17 +35,53 @@ class ProfileBloc extends Bloc { emit(ProfileLoading()); emit(EligibilityLoaded(eligibilities: event.eligibilities)); }); - on((event, emit) async{ + + on((event, emit) async { // try{ - List countries = await LocationUtils.instance.getCountries(); - List regions = await LocationUtils.instance.getRegions(); - List eligibilities = await ProfileUtilities.instance.getEligibilities(); - List provinces = await LocationUtils.instance.getProvinces(regionCode: event.eligibityCert.examAddress!.cityMunicipality!.province!.region!.code!.toString()); - emit(EditEligibilityState(provinces: provinces, eligibityCert: event.eligibityCert,countries: countries,regions: regions,eligibilities: eligibilities)); + emit(ProfileLoading()); + List regions = await LocationUtils.instance.getRegions(); + List eligibilities = + await ProfileUtilities.instance.getEligibilities(); + bool? isOverseas = event.eligibityCert.overseas; + List provinces = + event.eligibityCert.examAddress?.cityMunicipality?.province?.region != null + ? await LocationUtils.instance.getProvinces( + regionCode: event.eligibityCert.examAddress!.cityMunicipality! + .province!.region!.code! + .toString()) + : []; + List citymuns = + event.eligibityCert.examAddress?.cityMunicipality != null + ? await LocationUtils.instance.getCities( + code: event.eligibityCert.examAddress!.cityMunicipality! + .province!.code!) + : []; + emit(EditNotOverseasEligibilityState( + currentEligibility: null, + currentRegion: null, + isOverseas: isOverseas!, + cityMuns: citymuns, + provinces: provinces, + eligibityCert: event.eligibityCert, + regions: regions, + eligibilities: eligibilities)); + // }catch(e){ // emit(ProfileErrorState(mesage: e.toString())); // } - + });on((event,emit)async{ + emit(ProfileLoading()); + List countries = await LocationUtils.instance.getCountries(); + List eligibilities = + await ProfileUtilities.instance.getEligibilities(); + bool? isOverseas = event.eligibityCert.overseas; + emit(EditOverseasEligibilityState( + countries: countries, + currentCOuntry: null, + currentEligibility: null, + isOverseas: isOverseas!, + eligibityCert: event.eligibityCert, + eligibilities: eligibilities)); }); } } diff --git a/lib/bloc/profile/profile_event.dart b/lib/bloc/profile/profile_event.dart index 0548743..df9d027 100644 --- a/lib/bloc/profile/profile_event.dart +++ b/lib/bloc/profile/profile_event.dart @@ -28,9 +28,16 @@ class LoadEligibility extends ProfileEvent{ List get props => []; } -class EditEligibility extends ProfileEvent{ +class EditEligibilityOverseas extends ProfileEvent{ final EligibityCert eligibityCert; - const EditEligibility({required this.eligibityCert}); + const EditEligibilityOverseas({required this.eligibityCert}); + @override + List get props => []; +} + +class EditEligibilityNotOverseas extends ProfileEvent{ + final EligibityCert eligibityCert; + const EditEligibilityNotOverseas({required this.eligibityCert}); @override List get props => []; } diff --git a/lib/bloc/profile/profile_state.dart b/lib/bloc/profile/profile_state.dart index 8695782..4b38a3e 100644 --- a/lib/bloc/profile/profile_state.dart +++ b/lib/bloc/profile/profile_state.dart @@ -2,45 +2,74 @@ part of 'profile_bloc.dart'; abstract class ProfileState extends Equatable { const ProfileState(); - + @override List get props => []; } class ProfileInitial extends ProfileState {} -class ProfileLoaded extends ProfileState{ +class ProfileLoaded extends ProfileState { final ProfileInformation profileInformation; - const ProfileLoaded({required this.profileInformation}); + const ProfileLoaded({required this.profileInformation}); @override List get props => [profileInformation]; } -class ProfileErrorState extends ProfileState{ +class ProfileErrorState extends ProfileState { final String mesage; const ProfileErrorState({required this.mesage}); - @override + @override List get props => [mesage]; } -class ProfileLoading extends ProfileState{ +class ProfileLoading extends ProfileState {} -} - -class EligibilityLoaded extends ProfileState{ +class EligibilityLoaded extends ProfileState { final List eligibilities; const EligibilityLoaded({required this.eligibilities}); - @override + @override List get props => [eligibilities]; } -class EditEligibilityState extends ProfileState{ +class EditNotOverseasEligibilityState extends ProfileState { final EligibityCert eligibityCert; - final List eligibilities; - final List countries; - final List regions; - List provinces; - EditEligibilityState({ required this.provinces,required this.eligibityCert, required this.eligibilities, required this.countries, required this.regions}); - @override - List get props => [eligibityCert]; + final List eligibilities; + + final List regions; + List provinces; + List cityMuns; + Eligibility? currentEligibility; + Region? currentRegion; + Province? currentProvince; + bool? isOverseas; + EditNotOverseasEligibilityState( + {required this.currentEligibility, + required this.currentRegion, + required this.isOverseas, + required this.cityMuns, + required this.provinces, + required this.eligibityCert, + required this.eligibilities, + required this.regions}); + + +} + +class EditOverseasEligibilityState extends ProfileState { + final List eligibilities; + final EligibityCert eligibityCert; + final Eligibility? currentEligibility; + final List countries; + final Country? currentCOuntry; + bool? isOverseas; + + EditOverseasEligibilityState( + {required this.currentEligibility, + required this.eligibilities, + required this.isOverseas, + required this.eligibityCert, + required this.countries, + required this.currentCOuntry}); + } diff --git a/lib/model/location/address_category.dart b/lib/model/location/address_category.dart new file mode 100644 index 0000000..0916912 --- /dev/null +++ b/lib/model/location/address_category.dart @@ -0,0 +1,23 @@ +class AddressCategory { + AddressCategory({ + required this.id, + required this.name, + required this.type, + }); + + final int? id; + final String? name; + final String? type; + + factory AddressCategory.fromJson(Map json) => AddressCategory( + id: json["id"], + name: json["name"], + type: json["type"], + ); + + Map toJson() => { + "id": id, + "name": name, + "type": type, + }; +} \ No newline at end of file diff --git a/lib/model/location/barangay.dart b/lib/model/location/barangay.dart index a56f2f2..f36032c 100644 --- a/lib/model/location/barangay.dart +++ b/lib/model/location/barangay.dart @@ -5,6 +5,9 @@ import 'package:meta/meta.dart'; import 'dart:convert'; +import 'city.dart'; +import 'provinces.dart'; + Barangay barangayFromJson(String str) => Barangay.fromJson(json.decode(str)); String barangayToJson(Barangay data) => json.encode(data.toJson()); @@ -33,90 +36,6 @@ class Barangay { }; } -class CityMunicipality { - CityMunicipality({ - required this.code, - required this.description, - required this.province, - required this.psgcCode, - required this.zipcode, - }); - final String? code; - final String? description; - final Province? province; - final String? psgcCode; - final String? zipcode; - factory CityMunicipality.fromJson(Map json) => CityMunicipality( - code: json["code"], - description: json["description"], - province: json['province'] == null? null:Province.fromJson(json["province"]), - psgcCode: json["psgc_code"], - zipcode: json["zipcode"], - ); - Map toJson() => { - "code": code, - "description": description, - "province": province!.toJson(), - "psgc_code": psgcCode, - "zipcode": zipcode, - }; -} - -class Province { - Province({ - required this.code, - required this.description, - required this.region, - required this.psgcCode, - required this.shortname, - }); - - final String? code; - final String? description; - final Region? region; - final String? psgcCode; - final String? shortname; - - factory Province.fromJson(Map json) => Province( - code: json["code"], - description: json["description"], - region: json['region'] == null? null: Region.fromJson(json["region"]), - psgcCode: json["psgc_code"], - shortname: json["shortname"], - ); - - Map toJson() => { - "code": code, - "description": description, - "region": region!.toJson(), - "psgc_code": psgcCode, - "shortname": shortname, - }; -} - -class Region { - Region({ - required this.code, - required this.description, - required this.psgcCode, - }); - - final int? code; - final String? description; - final String? psgcCode; - - factory Region.fromJson(Map json) => Region( - code: json["code"], - description: json["description"], - psgcCode: json["psgc_code"], - ); - - Map toJson() => { - "code": code, - "description": description, - "psgc_code": psgcCode, - }; -} diff --git a/lib/model/location/city.dart b/lib/model/location/city.dart index e946098..c9c449a 100644 --- a/lib/model/location/city.dart +++ b/lib/model/location/city.dart @@ -5,12 +5,15 @@ import 'package:meta/meta.dart'; import 'dart:convert'; -City cityFromJson(String str) => City.fromJson(json.decode(str)); +import 'provinces.dart'; +import 'region.dart'; -String cityToJson(City data) => json.encode(data.toJson()); +CityMunicipality cityFromJson(String str) => CityMunicipality.fromJson(json.decode(str)); -class City { - City({ +String cityToJson(CityMunicipality data) => json.encode(data.toJson()); + +class CityMunicipality { + CityMunicipality({ required this.code, required this.description, required this.province, @@ -24,7 +27,7 @@ class City { final String? psgcCode; final String? zipcode; - factory City.fromJson(Map json) => City( + factory CityMunicipality.fromJson(Map json) => CityMunicipality( code: json["code"], description: json["description"], province: json['province'] == null? null : Province.fromJson(json["province"]), @@ -41,58 +44,4 @@ class City { }; } -class Province { - Province({ - required this.code, - required this.description, - required this.region, - required this.psgcCode, - required this.shortname, - }); - final String? code; - final String? description; - final Region? region; - final String? psgcCode; - final String? shortname; - - factory Province.fromJson(Map json) => Province( - code: json["code"], - description: json["description"], - region: json['region'] == null ? null : Region.fromJson(json["region"]), - psgcCode: json["psgc_code"], - shortname: json["shortname"], - ); - - Map toJson() => { - "code": code, - "description": description, - "region": region!.toJson(), - "psgc_code": psgcCode, - "shortname": shortname, - }; -} - -class Region { - Region({ - required this.code, - required this.description, - required this.psgcCode, - }); - - final int? code; - final String? description; - final String? psgcCode; - - factory Region.fromJson(Map json) => Region( - code: json["code"], - description: json["description"], - psgcCode: json["psgc_code"], - ); - - Map toJson() => { - "code": code, - "description": description, - "psgc_code": psgcCode, - }; -} diff --git a/lib/model/location/subdivision.dart b/lib/model/location/subdivision.dart new file mode 100644 index 0000000..950d80e --- /dev/null +++ b/lib/model/location/subdivision.dart @@ -0,0 +1,23 @@ +class Subdivision { + Subdivision({ + this.id, + this.lotNo, + this.blockNo, + }); + + final int? id; + final int? lotNo; + final int? blockNo; + + factory Subdivision.fromJson(Map json) => Subdivision( + id: json["id"], + lotNo: json["lot_no"], + blockNo: json["block_no"], + ); + + Map toJson() => { + "id": id, + "lot_no": lotNo, + "block_no": blockNo, + }; +} \ No newline at end of file diff --git a/lib/model/profile/basic_information/adress.dart b/lib/model/profile/basic_information/adress.dart index 68e18d6..4e941ec 100644 --- a/lib/model/profile/basic_information/adress.dart +++ b/lib/model/profile/basic_information/adress.dart @@ -1,5 +1,11 @@ +import '../../location/barangay.dart'; +import '../../location/city.dart'; +import '../../location/country.dart'; +import '../../location/subdivision.dart'; +import '../../utils/category.dart'; + class MainAdress { MainAdress({ this.id, @@ -64,186 +70,7 @@ class AddressClass { }; } -class Barangay { - Barangay({ - this.code, - this.description, - this.cityMunicipality, - }); - final String? code; - final String? description; - final CityMunicipality? cityMunicipality; - factory Barangay.fromJson(Map json) => Barangay( - code: json["code"], - description: json["description"], - cityMunicipality: json["city_municipality"] == null ? null : CityMunicipality.fromJson(json["city_municipality"]), - ); - Map toJson() => { - "code": code, - "description": description, - "city_municipality": cityMunicipality?.toJson(), - }; -} -class CityMunicipality { - CityMunicipality({ - this.code, - this.zipcode, - this.province, - this.psgcCode, - this.description, - }); - - final String? code; - final String? zipcode; - final Province? province; - final String? psgcCode; - final String? description; - - factory CityMunicipality.fromJson(Map json) => CityMunicipality( - code: json["code"], - zipcode: json["zipcode"], - province: json["province"] == null ? null : Province.fromJson(json["province"]), - psgcCode: json["psgc_code"], - description: json["description"], - ); - - Map toJson() => { - "code": code, - "zipcode": zipcode, - "province": province?.toJson(), - "psgc_code": psgcCode, - "description": description, - }; -} - -class Province { - Province({ - this.code, - this.region, - this.psgcCode, - this.shortname, - this.description, - }); - - final String? code; - final Region? region; - final String? psgcCode; - final String? shortname; - final String? description; - - factory Province.fromJson(Map json) => Province( - code: json["code"], - region: json["region"] == null ? null : Region.fromJson(json["region"]), - psgcCode: json["psgc_code"], - shortname: json["shortname"], - description: json["description"], - ); - - Map toJson() => { - "code": code, - "region": region?.toJson(), - "psgc_code": psgcCode, - "shortname": shortname, - "description": description, - }; -} - -class Region { - Region({ - this.code, - this.psgcCode, - this.description, - }); - - final int? code; - final String? psgcCode; - final String? description; - - factory Region.fromJson(Map json) => Region( - code: json["code"], - psgcCode: json["psgc_code"], - description: json["description"], - ); - - Map toJson() => { - "code": code, - "psgc_code": psgcCode, - "description": description, - }; -} - -class Category { - Category({ - this.id, - this.name, - this.type, - }); - - final int? id; - final String? name; - final String? type; - - factory Category.fromJson(Map json) => Category( - id: json["id"], - name: json["name"], - type: json["type"], - ); - - Map toJson() => { - "id": id, - "name": name, - "type": type, - }; -} - -class Country { - Country({ - this.id, - this.code, - this.name, - }); - - final int? id; - final String? code; - final String? name; - - factory Country.fromJson(Map json) => Country( - id: json["id"], - code: json["code"], - name: json["name"], - ); - - Map toJson() => { - "id": id, - "code": code, - "name": name, - }; -} - -class Subdivision { - Subdivision({ - this.id, - this.lotNo, - this.blockNo, - }); - - final int? id; - final int? lotNo; - final int? blockNo; - - factory Subdivision.fromJson(Map json) => Subdivision( - id: json["id"], - lotNo: json["lot_no"], - blockNo: json["block_no"], - ); - - Map toJson() => { - "id": id, - "lot_no": lotNo, - "block_no": blockNo, - }; -} diff --git a/lib/model/profile/basic_information/citizenship.dart b/lib/model/profile/basic_information/citizenship.dart index 397e12d..1c694ed 100644 --- a/lib/model/profile/basic_information/citizenship.dart +++ b/lib/model/profile/basic_information/citizenship.dart @@ -1,4 +1,6 @@ +import '../../location/country.dart'; + class Citizenship { Citizenship({ required this.country, @@ -18,27 +20,3 @@ class Citizenship { "natural_born": naturalBorn, }; } - -class Country { - Country({ - required this.id, - required this.code, - required this.name, - }); - - final int? id; - final String? code; - final String? name; - - factory Country.fromJson(Map json) => Country( - id: json["id"], - code: json["code"], - name: json["name"], - ); - - Map toJson() => { - "id": id, - "code": code, - "name": name, - }; -} diff --git a/lib/model/profile/basic_information/contact_information.dart b/lib/model/profile/basic_information/contact_information.dart index 729e7f5..3c182e9 100644 --- a/lib/model/profile/basic_information/contact_information.dart +++ b/lib/model/profile/basic_information/contact_information.dart @@ -3,6 +3,8 @@ // final contactInformation = contactInformationFromJson(jsonString); +import '../../utils/agency.dart'; + class ContactInfo { ContactInfo({ required this.id, @@ -83,81 +85,6 @@ class ServiceProvider { }; } -class Agency { - Agency({ - required this.id, - required this.name, - required this.category, - required this.privateEntity, - }); - - int? id; - String? name; - Category? category; - bool? privateEntity; - - factory Agency.fromJson(Map json) => Agency( - id: json["id"], - name: json["name"], - category: json["category"] == null? null : Category.fromJson(json["category"]), - privateEntity: json["private_entity"], - ); - - Map toJson() => { - "id": id, - "name": name, - "category": category!.toJson(), - "private_entity": privateEntity, - }; -} - -class Category { - Category({ - required this.id, - required this.name, - required this.industryClass, - }); - - int? id; - String? name; - IndustryClass? industryClass; - - factory Category.fromJson(Map json) => Category( - id: json["id"], - name: json["name"], - industryClass: json["industry_class"] == null? null: IndustryClass.fromJson(json["industry_class"]), - ); - - Map toJson() => { - "id": id, - "name": name, - "industry_class": industryClass!.toJson(), - }; -} - -class IndustryClass { - IndustryClass({ - required this.id, - required this.name, - this.description, - }); - - int? id; - String? name; - String? description; - - factory IndustryClass.fromJson(Map json) => IndustryClass( - id: json["id"], - name: json["name"], - description: json["description"], - ); - - Map toJson() => { - "id": id, - "name": name, - "description": description, - }; -} class ServiceType { ServiceType({ diff --git a/lib/model/profile/basic_information/identification_information.dart b/lib/model/profile/basic_information/identification_information.dart index f0ee066..61411c1 100644 --- a/lib/model/profile/basic_information/identification_information.dart +++ b/lib/model/profile/basic_information/identification_information.dart @@ -1,5 +1,9 @@ import 'dart:convert'; +import '../../location/city.dart'; +import '../../location/country.dart'; +import '../../utils/agency.dart'; + Identification identificationFromJson(String str) => Identification.fromJson(json.decode(str)); String identificationToJson(Identification data) => json.encode(data.toJson()); @@ -44,81 +48,6 @@ class Identification { }; } -class Agency { - Agency({ - required this.id, - required this.name, - required this.category, - required this.privateEntity, - }); - - int? id; - String? name; - Category? category; - bool? privateEntity; - - factory Agency.fromJson(Map json) => Agency( - id: json["id"], - name: json["name"], - category: json["category"] == null? null: Category.fromJson(json["category"]), - privateEntity: json["private_entity"], - ); - - Map toJson() => { - "id": id, - "name": name, - "category": category!.toJson(), - "private_entity": privateEntity, - }; -} - -class Category { - Category({ - required this.id, - required this.name, - required this.industryClass, - }); - - int? id; - String? name; - IndustryClass? industryClass; - - factory Category.fromJson(Map json) => Category( - id: json["id"], - name: json["name"], - industryClass:json['industry_class'] == null? null: IndustryClass.fromJson(json["industry_class"]), - ); - - Map toJson() => { - "id": id, - "name": name, - "industry_class": industryClass!.toJson(), - }; -} - -class IndustryClass { - IndustryClass({ - required this.id, - required this.name, - this.description, - }); - - int? id; - String? name; - String? description; - - factory IndustryClass.fromJson(Map json) => IndustryClass( - id: json["id"], - name: json["name"], - description: json["description"], - ); - - Map toJson() => { - "id": id, - "name": name, - "description": description, - }; -} class IssuedAt { IssuedAt({ @@ -179,115 +108,3 @@ class AddressCategory { "type": type, }; } - -class CityMunicipality { - CityMunicipality({ - required this.code, - required this.zipcode, - required this.province, - required this.psgcCode, - required this.description, - }); - - String? code; - String? zipcode; - Province? province; - String? psgcCode; - String? description; - - factory CityMunicipality.fromJson(Map json) => CityMunicipality( - code: json["code"], - zipcode: json["zipcode"], - province:json["province"] == null? null : Province.fromJson(json["province"]), - psgcCode: json["psgc_code"], - description: json["description"], - ); - - Map toJson() => { - "code": code, - "zipcode": zipcode, - "province": province!.toJson(), - "psgc_code": psgcCode, - "description": description, - }; -} - -class Province { - Province({ - required this.code, - required this.region, - required this.psgcCode, - required this.shortname, - required this.description, - }); - - String? code; - Region? region; - String? psgcCode; - String? shortname; - String? description; - - factory Province.fromJson(Map json) => Province( - code: json["code"], - region: json["region"] == null? null:Region.fromJson(json["region"]), - psgcCode: json["psgc_code"], - shortname: json["shortname"], - description: json["description"], - ); - - Map toJson() => { - "code": code, - "region": region!.toJson(), - "psgc_code": psgcCode, - "shortname": shortname, - "description": description, - }; -} - -class Region { - Region({ - required this.code, - required this.psgcCode, - required this.description, - }); - - int? code; - String? psgcCode; - String? description; - - factory Region.fromJson(Map json) => Region( - code: json["code"], - psgcCode: json["psgc_code"], - description: json["description"], - ); - - Map toJson() => { - "code": code, - "psgc_code": psgcCode, - "description": description, - }; -} - -class Country { - Country({ - required this.id, - required this.code, - required this.name, - }); - - int? id; - String? code; - String? name; - - factory Country.fromJson(Map json) => Country( - id: json["id"], - code: json["code"], - name: json["name"], - ); - - Map toJson() => { - "id": id, - "code": code, - "name": name, - }; -} diff --git a/lib/model/profile/eligibility.dart b/lib/model/profile/eligibility.dart index 674b65e..5f7e7fd 100644 --- a/lib/model/profile/eligibility.dart +++ b/lib/model/profile/eligibility.dart @@ -5,6 +5,13 @@ import 'package:meta/meta.dart'; import 'dart:convert'; +import 'package:unit2/model/location/region.dart'; + +import '../location/address_category.dart'; +import '../location/city.dart'; +import '../location/country.dart'; +import '../utils/eligibility.dart'; + EligibityCert eligibityFromJson(String str) => EligibityCert.fromJson(json.decode(str)); String eligibityToJson(EligibityCert data) => json.encode(data.toJson()); @@ -19,8 +26,9 @@ class EligibityCert { required this.examAddress, required this.validityDate, required this.licenseNumber, - }); - + required this.overseas, + }); + bool? overseas; final int? id; final double? rating; final DateTime? examDate; @@ -39,6 +47,7 @@ class EligibityCert { examAddress: json['exam_address'] == null? null: ExamAddress.fromJson(json["exam_address"]), validityDate: json["validity_date"], licenseNumber: json["license_number"], + overseas: null, ); Map toJson() => { @@ -53,29 +62,6 @@ class EligibityCert { }; } -class Eligibility { - Eligibility({ - required this.id, - required this.type, - required this.title, - }); - - final int? id; - final String? type; - final String? title; - - factory Eligibility.fromJson(Map json) => Eligibility( - id: json["id"], - type: json["type"], - title: json["title"], - ); - - Map toJson() => { - "id": id, - "type": type, - "title": title, - }; -} class ExamAddress { ExamAddress({ @@ -113,138 +99,3 @@ class ExamAddress { }; } -class AddressCategory { - AddressCategory({ - required this.id, - required this.name, - required this.type, - }); - - final int? id; - final String? name; - final String? type; - - factory AddressCategory.fromJson(Map json) => AddressCategory( - id: json["id"], - name: json["name"], - type: json["type"], - ); - - Map toJson() => { - "id": id, - "name": name, - "type": type, - }; -} - -class CityMunicipality { - CityMunicipality({ - required this.code, - required this.zipcode, - required this.province, - required this.psgcCode, - required this.description, - }); - - final String? code; - final String? zipcode; - final Province? province; - final String? psgcCode; - final String? description; - - factory CityMunicipality.fromJson(Map json) => CityMunicipality( - code: json["code"], - zipcode: json["zipcode"], - province: json["province"]== null? null: Province.fromJson(json["province"]), - psgcCode: json["psgc_code"], - description: json["description"], - ); - - Map toJson() => { - "code": code, - "zipcode": zipcode, - "province": province!.toJson(), - "psgc_code": psgcCode, - "description": description, - }; -} - -class Province { - Province({ - required this.code, - required this.region, - required this.psgcCode, - required this.shortname, - required this.description, - }); - - final String? code; - final Region? region; - final String? psgcCode; - final String? shortname; - final String? description; - - factory Province.fromJson(Map json) => Province( - code: json["code"], - region:json["region"] == null? null: Region.fromJson(json["region"]), - psgcCode: json["psgc_code"], - shortname: json["shortname"], - description: json["description"], - ); - - Map toJson() => { - "code": code, - "region": region!.toJson(), - "psgc_code": psgcCode, - "shortname": shortname, - "description": description, - }; -} - -class Region { - Region({ - required this.code, - required this.psgcCode, - required this.description, - }); - - final int? code; - final String? psgcCode; - final String? description; - - factory Region.fromJson(Map json) => Region( - code: json["code"], - psgcCode: json["psgc_code"], - description: json["description"], - ); - - Map toJson() => { - "code": code, - "psgc_code": psgcCode, - "description": description, - }; -} - -class Country { - Country({ - required this.id, - required this.code, - required this.name, - }); - - final int? id; - final String? code; - final String? name; - - factory Country.fromJson(Map json) => Country( - id: json["id"], - code: json["code"], - name: json["name"], - ); - - Map toJson() => { - "id": id, - "code": code, - "name": name, - }; -} diff --git a/lib/model/profile/family_backround.dart b/lib/model/profile/family_backround.dart index 9083cb7..68275a1 100644 --- a/lib/model/profile/family_backround.dart +++ b/lib/model/profile/family_backround.dart @@ -5,6 +5,9 @@ import 'dart:convert'; import 'dart:ffi'; +import '../utils/category.dart'; +import '../utils/position.dart'; + FamilyBackground familyBackgroundFromJson(String str) => FamilyBackground.fromJson(json.decode(str)); String familyBackgroundToJson(FamilyBackground data) => json.encode(data.toJson()); @@ -81,53 +84,6 @@ class Company { }; } -class Category { - Category({ - this.id, - this.name, - this.industryClass, - }); - - final int? id; - final String? name; - final IndustryClass? industryClass; - - factory Category.fromJson(Map json) => Category( - id: json["id"], - name: json["name"], - industryClass: json["industry_class"] == null ? null : IndustryClass.fromJson(json["industry_class"]), - ); - - Map toJson() => { - "id": id, - "name": name, - "industry_class": industryClass?.toJson(), - }; -} - -class IndustryClass { - IndustryClass({ - this.id, - this.name, - this.description, - }); - - final int? id; - final String? name; - final String? description; - - factory IndustryClass.fromJson(Map json) => IndustryClass( - id: json["id"], - name: json["name"], - description: json["description"], - ); - - Map toJson() => { - "id": id, - "name": name, - "description": description, - }; -} class EmergencyContact { EmergencyContact({ @@ -173,25 +129,6 @@ class EmergencyContact { }; } -class Position { - Position({ - this.id, - this.title, - }); - - final int? id; - final String? title; - - factory Position.fromJson(Map json) => Position( - id: json["id"], - title: json["title"], - ); - - Map toJson() => { - "id": id, - "title": title, - }; -} class RelatedPerson { RelatedPerson({ diff --git a/lib/model/profile/learning_development.dart b/lib/model/profile/learning_development.dart index 4131a02..11bb21b 100644 --- a/lib/model/profile/learning_development.dart +++ b/lib/model/profile/learning_development.dart @@ -4,6 +4,10 @@ import 'dart:convert'; +import '../location/city.dart'; +import '../location/country.dart'; +import '../utils/industry_class.dart'; + LearningDevelopement learningDevelopementFromJson(String str) => LearningDevelopement.fromJson(json.decode(str)); String learningDevelopementToJson(LearningDevelopement data) => json.encode(data.toJson()); @@ -144,29 +148,6 @@ class SponsoredByCategory { }; } -class IndustryClass { - IndustryClass({ - this.id, - this.name, - this.description, - }); - - final int? id; - final String? name; - final dynamic description; - - factory IndustryClass.fromJson(Map json) => IndustryClass( - id: json["id"], - name: json["name"], - description: json["description"], - ); - - Map toJson() => { - "id": id, - "name": name, - "description": description, - }; -} class LearningDevelopmentType { LearningDevelopmentType({ @@ -248,114 +229,3 @@ class VenueCategory { }; } -class CityMunicipality { - CityMunicipality({ - this.code, - this.zipcode, - this.province, - this.psgcCode, - this.description, - }); - - final String? code; - final String? zipcode; - final Province? province; - final String? psgcCode; - final String? description; - - factory CityMunicipality.fromJson(Map json) => CityMunicipality( - code: json["code"], - zipcode: json["zipcode"], - province: json["province"] == null ? null : Province.fromJson(json["province"]), - psgcCode: json["psgc_code"], - description: json["description"], - ); - - Map toJson() => { - "code": code, - "zipcode": zipcode, - "province": province?.toJson(), - "psgc_code": psgcCode, - "description": description, - }; -} - -class Province { - Province({ - this.code, - this.region, - this.psgcCode, - this.shortname, - this.description, - }); - - final String? code; - final Region? region; - final String? psgcCode; - final String? shortname; - final String? description; - - factory Province.fromJson(Map json) => Province( - code: json["code"], - region: json["region"] == null ? null : Region.fromJson(json["region"]), - psgcCode: json["psgc_code"], - shortname: json["shortname"], - description: json["description"], - ); - - Map toJson() => { - "code": code, - "region": region?.toJson(), - "psgc_code": psgcCode, - "shortname": shortname, - "description": description, - }; -} - -class Region { - Region({ - this.code, - this.psgcCode, - this.description, - }); - - final int? code; - final String? psgcCode; - final String? description; - - factory Region.fromJson(Map json) => Region( - code: json["code"], - psgcCode: json["psgc_code"], - description: json["description"], - ); - - Map toJson() => { - "code": code, - "psgc_code": psgcCode, - "description": description, - }; -} - -class Country { - Country({ - this.id, - this.code, - this.name, - }); - - final int? id; - final String? code; - final String? name; - - factory Country.fromJson(Map json) => Country( - id: json["id"], - code: json["code"], - name: json["name"], - ); - - Map toJson() => { - "id": id, - "code": code, - "name": name, - }; -} diff --git a/lib/model/profile/other_information/non_acedimic_recognition.dart b/lib/model/profile/other_information/non_acedimic_recognition.dart index 0697a66..9e4b6a7 100644 --- a/lib/model/profile/other_information/non_acedimic_recognition.dart +++ b/lib/model/profile/other_information/non_acedimic_recognition.dart @@ -4,6 +4,8 @@ import 'dart:convert'; +import '../../utils/category.dart'; + NonAcademicRecognition nonAcademicRecognitionFromJson(String str) => NonAcademicRecognition.fromJson(json.decode(str)); String nonAcademicRecognitionToJson(NonAcademicRecognition data) => json.encode(data.toJson()); @@ -60,50 +62,3 @@ class Presenter { }; } -class Category { - Category({ - this.id, - this.name, - this.industryClass, - }); - - final int? id; - final String? name; - final IndustryClass? industryClass; - - factory Category.fromJson(Map json) => Category( - id: json["id"], - name: json["name"], - industryClass: json["industry_class"] == null ? null : IndustryClass.fromJson(json["industry_class"]), - ); - - Map toJson() => { - "id": id, - "name": name, - "industry_class": industryClass?.toJson(), - }; -} - -class IndustryClass { - IndustryClass({ - this.id, - this.name, - this.description, - }); - - final int? id; - final String? name; - final String? description; - - factory IndustryClass.fromJson(Map json) => IndustryClass( - id: json["id"], - name: json["name"], - description: json["description"], - ); - - Map toJson() => { - "id": id, - "name": name, - "description": description, - }; -} diff --git a/lib/model/profile/other_information/organization_memberships.dart b/lib/model/profile/other_information/organization_memberships.dart index 150f54f..9d690a4 100644 --- a/lib/model/profile/other_information/organization_memberships.dart +++ b/lib/model/profile/other_information/organization_memberships.dart @@ -4,6 +4,8 @@ import 'dart:convert'; +import '../../utils/agency.dart'; + OrganizationMembership organizationMembershipFromJson(String str) => OrganizationMembership.fromJson(json.decode(str)); String organizationMembershipToJson(OrganizationMembership data) => json.encode(data.toJson()); @@ -24,78 +26,4 @@ class OrganizationMembership { }; } -class Agency { - Agency({ - this.id, - this.name, - this.category, - this.privateEntity, - }); - final int? id; - final String? name; - final Category? category; - final bool? privateEntity; - - factory Agency.fromJson(Map json) => Agency( - id: json["id"], - name: json["name"], - category: json["category"] == null ? null : Category.fromJson(json["category"]), - privateEntity: json["private_entity"], - ); - - Map toJson() => { - "id": id, - "name": name, - "category": category?.toJson(), - "private_entity": privateEntity, - }; -} - -class Category { - Category({ - this.id, - this.name, - this.industryClass, - }); - - final int? id; - final String? name; - final IndustryClass? industryClass; - - factory Category.fromJson(Map json) => Category( - id: json["id"], - name: json["name"], - industryClass: json["industry_class"] == null ? null : IndustryClass.fromJson(json["industry_class"]), - ); - - Map toJson() => { - "id": id, - "name": name, - "industry_class": industryClass?.toJson(), - }; -} - -class IndustryClass { - IndustryClass({ - this.id, - this.name, - this.description, - }); - - final int? id; - final String? name; - final String? description; - - factory IndustryClass.fromJson(Map json) => IndustryClass( - id: json["id"], - name: json["name"], - description: json["description"], - ); - - Map toJson() => { - "id": id, - "name": name, - "description": description, - }; -} diff --git a/lib/model/profile/references.dart b/lib/model/profile/references.dart index 9fda1eb..d7e1e05 100644 --- a/lib/model/profile/references.dart +++ b/lib/model/profile/references.dart @@ -1,6 +1,12 @@ // To parse this JSON data, do // // final references = referencesFromJson(jsonString); +import '../location/address_category.dart'; +import '../location/barangay.dart'; +import '../location/city.dart'; +import '../location/country.dart'; +import '../location/provinces.dart'; + class PersonalReference { PersonalReference({ required this.id, @@ -73,162 +79,6 @@ class Address { }; } -class AddressCategory { - AddressCategory({ - required this.id, - required this.name, - required this.type, - }); - final int? id; - final String? name; - final String? type; - factory AddressCategory.fromJson(Map json) => AddressCategory( - id: json["id"], - name: json["name"], - type: json["type"], - ); - Map toJson() => { - "id": id, - "name": name, - "type": type, - }; -} - -class Barangay { - Barangay({ - required this.code, - required this.description, - required this.cityMunicipality, - }); - - final String? code; - final String? description; - final CityMunicipality? cityMunicipality; - - factory Barangay.fromJson(Map json) => Barangay( - code: json["code"], - description: json["description"], - cityMunicipality: CityMunicipality.fromJson(json["city_municipality"]), - ); - - Map toJson() => { - "code": code, - "description": description, - "city_municipality": cityMunicipality!.toJson(), - }; -} - -class CityMunicipality { - CityMunicipality({ - required this.code, - required this.zipcode, - required this.province, - required this.psgcCode, - required this.description, - }); - - final String? code; - final String? zipcode; - final Province? province; - final String? psgcCode; - final String? description; - - factory CityMunicipality.fromJson(Map json) => CityMunicipality( - code: json["code"], - zipcode: json["zipcode"], - province: json["province"] == null? null : Province.fromJson(json["province"]), - psgcCode: json["psgc_code"], - description: json["description"], - ); - - Map toJson() => { - "code": code, - "zipcode": zipcode, - "province": province!.toJson(), - "psgc_code": psgcCode, - "description": description, - }; -} - -class Province { - Province({ - required this.code, - required this.region, - required this.psgcCode, - required this.shortname, - required this.description, - }); - - final String? code; - final Region? region; - final String? psgcCode; - final String? shortname; - final String? description; - - factory Province.fromJson(Map json) => Province( - code: json["code"], - region: json["region"] == null? null : Region.fromJson(json["region"]), - psgcCode: json["psgc_code"], - shortname: json["shortname"], - description: json["description"], - ); - - Map toJson() => { - "code": code, - "region": region!.toJson(), - "psgc_code": psgcCode, - "shortname": shortname, - "description": description, - }; -} - -class Region { - Region({ - required this.code, - required this.psgcCode, - required this.description, - }); - - final int? code; - final String? psgcCode; - final String? description; - - factory Region.fromJson(Map json) => Region( - code: json["code"], - psgcCode: json["psgc_code"], - description: json["description"], - ); - - Map toJson() => { - "code": code, - "psgc_code": psgcCode, - "description": description, - }; -} - -class Country { - Country({ - required this.id, - required this.code, - required this.name, - }); - - final int? id; - final String? code; - final String? name; - - factory Country.fromJson(Map json) => Country( - id: json["id"], - code: json["code"], - name: json["name"], - ); - - Map toJson() => { - "id": id, - "code": code, - "name": name, - }; -} diff --git a/lib/model/profile/voluntary_works.dart b/lib/model/profile/voluntary_works.dart index 267fb8d..8f5cdae 100644 --- a/lib/model/profile/voluntary_works.dart +++ b/lib/model/profile/voluntary_works.dart @@ -4,6 +4,12 @@ import 'dart:convert'; +import '../location/address_category.dart'; +import '../location/city.dart'; +import '../location/country.dart'; +import '../utils/agency.dart'; +import '../utils/position.dart'; + VoluntaryWork voluntaryWorkFromJson(String str) => VoluntaryWork.fromJson(json.decode(str)); String voluntaryWorkToJson(VoluntaryWork data) => json.encode(data.toJson()); @@ -80,234 +86,10 @@ class Address { }; } -class AddressCategory { - AddressCategory({ - this.id, - this.name, - this.type, - }); - final int? id; - final String? name; - final String? type; - factory AddressCategory.fromJson(Map json) => AddressCategory( - id: json["id"], - name: json["name"], - type: json["type"], - ); - Map toJson() => { - "id": id, - "name": name, - "type": type, - }; -} -class CityMunicipality { - CityMunicipality({ - this.code, - this.zipcode, - this.province, - this.psgcCode, - this.description, - }); - final String? code; - final String? zipcode; - final Province? province; - final String? psgcCode; - final String? description; - factory CityMunicipality.fromJson(Map json) => CityMunicipality( - code: json["code"], - zipcode: json["zipcode"], - province: json["province"] == null ? null : Province.fromJson(json["province"]), - psgcCode: json["psgc_code"], - description: json["description"], - ); - Map toJson() => { - "code": code, - "zipcode": zipcode, - "province": province?.toJson(), - "psgc_code": psgcCode, - "description": description, - }; -} - -class Province { - Province({ - this.code, - this.region, - this.psgcCode, - this.shortname, - this.description, - }); - - final String? code; - final Region? region; - final String? psgcCode; - final String? shortname; - final String? description; - - factory Province.fromJson(Map json) => Province( - code: json["code"], - region: json["region"] == null ? null : Region.fromJson(json["region"]), - psgcCode: json["psgc_code"], - shortname: json["shortname"], - description: json["description"], - ); - - Map toJson() => { - "code": code, - "region": region?.toJson(), - "psgc_code": psgcCode, - "shortname": shortname, - "description": description, - }; -} - -class Region { - Region({ - this.code, - this.psgcCode, - this.description, - }); - - final int? code; - final String? psgcCode; - final String? description; - - factory Region.fromJson(Map json) => Region( - code: json["code"], - psgcCode: json["psgc_code"], - description: json["description"], - ); - - Map toJson() => { - "code": code, - "psgc_code": psgcCode, - "description": description, - }; -} - -class Country { - Country({ - this.id, - this.code, - this.name, - }); - - final int? id; - final String? code; - final String? name; - - factory Country.fromJson(Map json) => Country( - id: json["id"], - code: json["code"], - name: json["name"], - ); - - Map toJson() => { - "id": id, - "code": code, - "name": name, - }; -} - -class Agency { - Agency({ - this.id, - this.name, - this.category, - this.privateEntity, - }); - - final int? id; - final String? name; - final Category? category; - final bool? privateEntity; - - factory Agency.fromJson(Map json) => Agency( - id: json["id"], - name: json["name"], - category: json["category"] == null ? null : Category.fromJson(json["category"]), - privateEntity: json["private_entity"], - ); - - Map toJson() => { - "id": id, - "name": name, - "category": category?.toJson(), - "private_entity": privateEntity, - }; -} - -class Category { - Category({ - this.id, - this.name, - this.industryClass, - }); - - final int? id; - final String? name; - final IndustryClass? industryClass; - - factory Category.fromJson(Map json) => Category( - id: json["id"], - name: json["name"], - industryClass: json["industry_class"] == null ? null : IndustryClass.fromJson(json["industry_class"]), - ); - - Map toJson() => { - "id": id, - "name": name, - "industry_class": industryClass?.toJson(), - }; -} - -class IndustryClass { - IndustryClass({ - this.id, - this.name, - this.description, - }); - - final int? id; - final String? name; - final String? description; - - factory IndustryClass.fromJson(Map json) => IndustryClass( - id: json["id"], - name: json["name"], - description: json["description"], - ); - - Map toJson() => { - "id": id, - "name": name, - "description": description, - }; -} - -class Position { - Position({ - this.id, - this.title, - }); - - final int? id; - final String? title; - - factory Position.fromJson(Map json) => Position( - id: json["id"], - title: json["title"], - ); - - Map toJson() => { - "id": id, - "title": title, - }; -} diff --git a/lib/model/profile/work_history.dart b/lib/model/profile/work_history.dart index 7a1ff98..687b1f7 100644 --- a/lib/model/profile/work_history.dart +++ b/lib/model/profile/work_history.dart @@ -4,6 +4,11 @@ import 'dart:convert'; +import '../utils/agency.dart'; +import '../utils/category.dart'; +import '../utils/industry_class.dart'; +import '../utils/position.dart'; + WorkHistory workHistoryFromJson(String str) => WorkHistory.fromJson(json.decode(str)); String workHistoryToJson(WorkHistory data) => json.encode(data.toJson()); @@ -60,98 +65,10 @@ class WorkHistory { }; } -class Agency { - Agency({ - this.id, - this.name, - this.category, - this.privateEntity, - }); - final int? id; - final String? name; - final Category? category; - final bool? privateEntity; - factory Agency.fromJson(Map json) => Agency( - id: json["id"], - name: json["name"], - category: json["category"] == null ? null : Category.fromJson(json["category"]), - privateEntity: json["private_entity"], - ); - Map toJson() => { - "id": id, - "name": name, - "category": category?.toJson(), - "private_entity": privateEntity, - }; -} -class Category { - Category({ - this.id, - this.name, - this.industryClass, - }); - final int? id; - final String? name; - final IndustryClass? industryClass; - factory Category.fromJson(Map json) => Category( - id: json["id"], - name: json["name"], - industryClass: json["industry_class"] == null ? null : IndustryClass.fromJson(json["industry_class"]), - ); - Map toJson() => { - "id": id, - "name": name, - "industry_class": industryClass?.toJson(), - }; -} - -class IndustryClass { - IndustryClass({ - this.id, - this.name, - this.description, - }); - - final int? id; - final String? name; - final String? description; - - factory IndustryClass.fromJson(Map json) => IndustryClass( - id: json["id"], - name: json["name"], - description: json["description"], - ); - - Map toJson() => { - "id": id, - "name": name, - "description": description, - }; -} - -class Position { - Position({ - this.id, - this.title, - }); - - final int? id; - final String? title; - - factory Position.fromJson(Map json) => Position( - id: json["id"], - title: json["title"], - ); - - Map toJson() => { - "id": id, - "title": title, - }; -} diff --git a/lib/model/utils/agency.dart b/lib/model/utils/agency.dart new file mode 100644 index 0000000..68056fb --- /dev/null +++ b/lib/model/utils/agency.dart @@ -0,0 +1,29 @@ +import 'package:unit2/model/utils/category.dart'; + +class Agency { + Agency({ + this.id, + this.name, + this.category, + this.privateEntity, + }); + + final int? id; + final String? name; + final Category? category; + final bool? privateEntity; + + factory Agency.fromJson(Map json) => Agency( + id: json["id"], + name: json["name"], + category: json["category"] == null ? null : Category.fromJson(json["category"]), + privateEntity: json["private_entity"], + ); + + Map toJson() => { + "id": id, + "name": name, + "category": category?.toJson(), + "private_entity": privateEntity, + }; +} \ No newline at end of file diff --git a/lib/model/utils/category.dart b/lib/model/utils/category.dart new file mode 100644 index 0000000..8319067 --- /dev/null +++ b/lib/model/utils/category.dart @@ -0,0 +1,25 @@ +import 'package:unit2/model/utils/industry_class.dart'; + +class Category { + Category({ + this.id, + this.name, + this.industryClass, + }); + + final int? id; + final String? name; + final IndustryClass? industryClass; + + factory Category.fromJson(Map json) => Category( + id: json["id"], + name: json["name"], + industryClass: json["industry_class"] == null ? null : IndustryClass.fromJson(json["industry_class"]), + ); + + Map toJson() => { + "id": id, + "name": name, + "industry_class": industryClass?.toJson(), + }; +} \ No newline at end of file diff --git a/lib/model/utils/eligibilities_choices.dart b/lib/model/utils/eligibility.dart similarity index 62% rename from lib/model/utils/eligibilities_choices.dart rename to lib/model/utils/eligibility.dart index eae00f0..c9b3a04 100644 --- a/lib/model/utils/eligibilities_choices.dart +++ b/lib/model/utils/eligibility.dart @@ -5,12 +5,12 @@ import 'package:meta/meta.dart'; import 'dart:convert'; -EligibilityList eligibilitiesFromJson(String str) => EligibilityList.fromJson(json.decode(str)); +Eligibility eligibilitiesFromJson(String str) => Eligibility.fromJson(json.decode(str)); -String eligibilitiesToJson(EligibilityList data) => json.encode(data.toJson()); +String eligibilitiesToJson(Eligibility data) => json.encode(data.toJson()); -class EligibilityList { - EligibilityList({ +class Eligibility { + Eligibility({ required this.id, required this.title, required this.type, @@ -20,7 +20,7 @@ class EligibilityList { final String title; final String type; - factory EligibilityList.fromJson(Map json) => EligibilityList( + factory Eligibility.fromJson(Map json) => Eligibility( id: json["id"], title: json["title"], type: json["type"], diff --git a/lib/model/utils/industry_class.dart b/lib/model/utils/industry_class.dart new file mode 100644 index 0000000..c6bb4bf --- /dev/null +++ b/lib/model/utils/industry_class.dart @@ -0,0 +1,23 @@ +class IndustryClass { + IndustryClass({ + this.id, + this.name, + this.description, + }); + + final int? id; + final String? name; + final String? description; + + factory IndustryClass.fromJson(Map json) => IndustryClass( + id: json["id"], + name: json["name"], + description: json["description"], + ); + + Map toJson() => { + "id": id, + "name": name, + "description": description, + }; +} \ No newline at end of file diff --git a/lib/model/utils/position.dart b/lib/model/utils/position.dart new file mode 100644 index 0000000..ff7ba26 --- /dev/null +++ b/lib/model/utils/position.dart @@ -0,0 +1,19 @@ +class Position { + Position({ + this.id, + this.title, + }); + + final int? id; + final String? title; + + factory Position.fromJson(Map json) => Position( + id: json["id"], + title: json["title"], + ); + + Map toJson() => { + "id": id, + "title": title, + }; +} \ No newline at end of file diff --git a/lib/screens/profile/components/eligibility/edit_modal.dart b/lib/screens/profile/components/eligibility/edit_modal.dart index c43b943..bc76af6 100644 --- a/lib/screens/profile/components/eligibility/edit_modal.dart +++ b/lib/screens/profile/components/eligibility/edit_modal.dart @@ -1,17 +1,17 @@ import 'package:date_time_picker/date_time_picker.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:intl/intl.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; +import 'package:unit2/model/location/city.dart'; +import 'package:unit2/model/login_data/employee_info/employee_info.dart'; import 'package:unit2/model/profile/eligibility.dart'; -import 'package:unit2/model/utils/eligibilities_choices.dart'; -import '../../../../model/location/country.dart' as c; -import '../../../../model/location/region.dart' as r; -import '../../../../model/location/provinces.dart' as p; +import 'package:unit2/model/utils/eligibility.dart'; +import '../../../../model/location/country.dart'; +import '../../../../model/location/region.dart'; +import '../../../../model/location/provinces.dart'; import '../../../../theme-data.dart/btn-style.dart'; import '../../../../theme-data.dart/colors.dart'; import '../../../../theme-data.dart/form-style.dart'; @@ -28,9 +28,10 @@ class EditEligibilityScreen extends StatefulWidget { class _EditEligibilityScreenState extends State { final formKey = GlobalKey(); - bool overseas = false; + final countryKey = GlobalKey(); + bool? overseas; DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); - c.Country? selectedCountry; + Country? selectedCountry; @override Widget build(BuildContext context) { //USERBLOC @@ -42,13 +43,8 @@ class _EditEligibilityScreenState extends State { return BlocBuilder( builder: (context, state) { //EDIT ELIGIBILITY STATE - if (state is EditEligibilityState) { - String? region = state.eligibityCert.examAddress! - .cityMunicipality!.province!.region!.description; - String? eligibiltyTitle = - state.eligibityCert.eligibility!.title!; - String? province = state.eligibityCert.examAddress! - .cityMunicipality!.province!.description!; + if (state is EditNotOverseasEligibilityState) { + overseas = state.isOverseas; return Center( child: Padding( padding: const EdgeInsets.symmetric( @@ -60,18 +56,18 @@ class _EditEligibilityScreenState extends State { crossAxisAlignment: CrossAxisAlignment.center, children: [ //ELIGIBILITIES DROPDOWN - FormBuilderDropdown( - initialValue: state.eligibityCert.eligibility, + FormBuilderDropdown( + initialValue: null, items: state.eligibilities - .map>( - (EligibilityList eligibility) { - return DropdownMenuItem( + .map>( + (Eligibility eligibility) { + return DropdownMenuItem( value: eligibility, child: Text(eligibility.title)); }).toList(), name: "eligibility", decoration: normalTextFieldStyle( - "Eligibility", eligibiltyTitle) + "Eligibility", "") .copyWith( hintStyle: const TextStyle( color: Colors.black, @@ -177,9 +173,12 @@ class _EditEligibilityScreenState extends State { initialValue: overseas, activeColor: second, onChanged: (value) { - setState(() { - overseas = value!; - }); + EligibityCert newEligibility = + state.eligibityCert; + newEligibility.overseas = value!; + context.read().add( + EditEligibilityOverseas( + eligibityCert: newEligibility)); }, decoration: normalTextFieldStyle("", ''), name: 'overseas', @@ -188,97 +187,68 @@ class _EditEligibilityScreenState extends State { const SizedBox( height: 20, ), - //COUNTRY DROPDOWN + Column( + children: [ + //REGION DROPDOWN + FormBuilderDropdown( + onChanged: (Region? region) {}, + // initialValue:state.eligibityCert.examAddress!.cityMunicipality!.province!.description!, - SizedBox( - child: overseas == true - ? FormBuilderDropdown( - items: state.countries - .map>( - (c.Country country) { - return DropdownMenuItem( - value: country, - child: Text(country.name!)); - }).toList(), - name: 'country', - decoration: normalTextFieldStyle( - "Country", "Country"), - onChanged: (value) { - setState(() { - selectedCountry = value; - }); - }, - ) - : Column( - children: [ - //REGION DROPDOWN - FormBuilderDropdown( - // initialValue:state.eligibityCert.examAddress!.cityMunicipality!.province!.description!, - decoration: normalTextFieldStyle( - "Region", region ?? - "Region*") - .copyWith( - hintStyle: const TextStyle( - color: Colors.black, - ), - labelStyle: const TextStyle( - color: Colors.black)), - name: 'region', - items: state.regions.map< - DropdownMenuItem>( - (r.Region region) { - return DropdownMenuItem( - value: region, - child: Text( - region.description!)); - }).toList(), - ), - const SizedBox( - height: 20, - ), - //PROVINCE DROPDOWN - FormBuilderDropdown( - name: 'province', - items: state.provinces.map< - DropdownMenuItem< - p.Province>>( - (p.Province province) { - return DropdownMenuItem( - value: province, - child: Text( - province.description!)); - }).toList(), - decoration: normalTextFieldStyle( - "Province", province) - .copyWith( - hintStyle: const TextStyle( - color: Colors.black, - ), - labelStyle: const TextStyle( - color: Colors.black), - )), - const SizedBox( - height: 20, - ), - FormBuilderDropdown( - decoration: normalTextFieldStyle( - "Municipality", "Municipality"), - name: 'municipality', - items: [], - initialValue: widget - .eligibityCert - .examAddress - ?.cityMunicipality - ?.description == - null - ? 'region' - : 'municipality', - ), - ], - )), - const SizedBox( - height: 20, + decoration: + normalTextFieldStyle("Region*", "Region"), + + name: 'region', + items: state.regions + .map>( + (Region region) { + return DropdownMenuItem( + value: region, + child: Text(region.description!)); + }).toList(), + ), + const SizedBox( + height: 20, + ), + //PROVINCE DROPDOWN + FormBuilderDropdown( + initialValue: null, + name: 'province', + items: state.provinces.isEmpty + ? [] + : state.provinces + .map>( + (Province province) { + return DropdownMenuItem( + value: province, + child: Text( + province.description!)); + }).toList(), + decoration: normalTextFieldStyle( + "Province*", "Province")), + const SizedBox( + height: 20, + ), + FormBuilderDropdown( + decoration: normalTextFieldStyle( + "Municipality*", "Municipality"), + name: 'municipality', + items: state.cityMuns.isEmpty + ? [] + : state.cityMuns.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality c) { + return DropdownMenuItem( + value: c, + child: Text(c.description!)); + }).toList(), + initialValue: null), + const SizedBox( + height: 20, + ), + ], ), + SizedBox( width: screenWidth, height: 60, @@ -296,6 +266,195 @@ class _EditEligibilityScreenState extends State { ), ); } + //=========================================================================== + if (state is EditOverseasEligibilityState) { + overseas = state.isOverseas; + return Center( + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, horizontal: 18), + child: FormBuilder( + key: formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + //ELIGIBILITIES DROPDOWN + FormBuilderDropdown( + initialValue: null, + items: state.eligibilities + .map>( + (Eligibility eligibility) { + return DropdownMenuItem( + value: eligibility, + child: Text(eligibility.title)); + }).toList(), + name: "eligibility", + decoration: normalTextFieldStyle( + "Eligibility", "") + .copyWith( + hintStyle: const TextStyle( + color: Colors.black, + ), + labelStyle: + const TextStyle(color: Colors.black)), + ), + const SizedBox( + height: 20, + ), + + SizedBox( + width: screenWidth, + child: Row( + children: [ + //LICENSE NUMBER + Flexible( + flex: 1, + child: FormBuilderTextField( + name: 'license number', + initialValue: + widget.eligibityCert.licenseNumber, + decoration: normalTextFieldStyle( + "license number", "license number"), + ), + ), + const SizedBox( + width: 12, + ), + //RATING + Flexible( + flex: 1, + child: FormBuilderTextField( + name: 'rating', + initialValue: widget.eligibityCert.rating + .toString(), + decoration: normalTextFieldStyle( + 'rating', 'rating'), + ), + ), + ], + ), + ), + const SizedBox( + height: 20, + ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + //EXAM DATE + Flexible( + flex: 1, + child: DateTimePicker( + firstDate: DateTime(2000), + lastDate: DateTime(2100), + decoration: normalTextFieldStyle( + "Exam date", "Exam date"), + initialValue: widget + .eligibityCert.examDate == + null + ? '' + : dteFormat2.format( + widget.eligibityCert.examDate!), + )), + const SizedBox( + width: 12, + ), + //VALIDITY DATE + Flexible( + flex: 1, + child: DateTimePicker( + firstDate: DateTime(2000), + lastDate: DateTime(2100), + decoration: normalTextFieldStyle( + "Validity date", "Validity date"), + initialValue: + widget.eligibityCert.validityDate == + null + ? '' + : dteFormat2.format(widget + .eligibityCert.validityDate!), + ), + ), + ], + ), + ), + const SizedBox( + height: 20, + ), + Text( + "Placement of Examination/Confinement", + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith(fontSize: blockSizeVertical * 2), + ), + const SizedBox( + height: 12, + ), + //OVERSEAS ADDRESS SWITCH + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + EligibityCert newEligibility = + state.eligibityCert; + newEligibility.overseas = value!; + // countryKey.currentState?.fields['country'] + // ?.reset(); + + + context.read().add( + EditEligibilityNotOverseas( + eligibityCert: newEligibility)); + }, + decoration: normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Overseas Address?"), + ), + const SizedBox( + height: 20, + ), + + FormBuilderDropdown( + key: countryKey, + onChanged: (Country? country) { + + selectedCountry = country; + }, + // initialValue:state.eligibityCert.examAddress!.cityMunicipality!.province!.description!, + initialValue:selectedCountry, + decoration: + normalTextFieldStyle("Country*", "country"), + name: 'country', + items: state.countries.isNotEmpty + ? state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: Text(country.name!)); + }).toList() + : []), + + SizedBox( + width: screenWidth, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () {}, + child: const Text(submit)), + ), + const SizedBox( + height: 20, + ), + ]), + ), + ), + ); + } + return Container(); }, ); diff --git a/lib/screens/profile/components/eligibility_screen.dart b/lib/screens/profile/components/eligibility_screen.dart index 115fd5d..257387d 100644 --- a/lib/screens/profile/components/eligibility_screen.dart +++ b/lib/screens/profile/components/eligibility_screen.dart @@ -36,9 +36,19 @@ class EligibiltyScreen extends StatelessWidget { return ProgressHUD( child: BlocConsumer( listener: (context, state) { - if (state is EditEligibilityState) { + if(state is ProfileLoading){ + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading"); + } + if (state is EditNotOverseasEligibilityState) { final progress = ProgressHUD.of(context); progress!.dismiss(); + }if (state is EditOverseasEligibilityState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + }if(state is EligibilityLoaded){ + final progress = ProgressHUD.of(context); + progress!.dismiss(); } // TODO: implement listener }, @@ -94,7 +104,7 @@ class EligibiltyScreen extends StatelessWidget { height: 3, ), Text( - " : ${state.eligibilities[index].rating}.", + "Rating : ${state.eligibilities[index].rating}.", style: Theme.of(context) .textTheme .titleSmall) @@ -112,15 +122,35 @@ class EligibiltyScreen extends StatelessWidget { "Confirm Delete?"); } if (value == 1) { + EligibityCert eligibityCert = + state.eligibilities[index]; + bool overseas = eligibityCert + .examAddress! + .country! + .id + .toString() == + '175' + ? false + : true; + eligibityCert.overseas = + overseas; final progress = ProgressHUD.of(context); + eligibityCert.overseas = + overseas; progress! .showWithText("Loading..."); - context.read().add( - EditEligibility( - eligibityCert: - state.eligibilities[ - index])); + if (eligibityCert.overseas!) { + context.read().add( + EditEligibilityOverseas( + eligibityCert: + eligibityCert)); + } else { + context.read().add( + EditEligibilityNotOverseas( + eligibityCert: + eligibityCert)); + } } }, menuItems: [ @@ -153,9 +183,12 @@ class EligibiltyScreen extends StatelessWidget { ); }); } - if (state is EditEligibilityState) { + if (state is EditNotOverseasEligibilityState) { return EditEligibilityScreen( eligibityCert: state.eligibityCert); + }if(state is EditOverseasEligibilityState){ + return EditEligibilityScreen( + eligibityCert: state.eligibityCert); } return Container(); }, diff --git a/lib/screens/unit2/login/login.dart b/lib/screens/unit2/login/login.dart index cdce5fd..045bcc6 100644 --- a/lib/screens/unit2/login/login.dart +++ b/lib/screens/unit2/login/login.dart @@ -230,8 +230,8 @@ class _UniT2LoginState extends State { BlocProvider.of(context) .add(UserLogin( - username: "rodolfobacuinjr", - password: "nav071394", + username: "rjvincentlopeplopez", + password: "shesthequ33n", // username: _formKey // .currentState! // .value['username'], diff --git a/lib/utils/location_utilities.dart b/lib/utils/location_utilities.dart index 6d19119..a5678f6 100644 --- a/lib/utils/location_utilities.dart +++ b/lib/utils/location_utilities.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import 'package:unit2/model/location/city.dart'; import 'package:unit2/model/location/country.dart'; import 'package:http/http.dart' as http; import 'package:unit2/model/location/provinces.dart'; @@ -80,4 +81,22 @@ Future>getRegions()async{ } return provinces; } + + Future>getCities({required String code})async{ + List cities = []; + String path = Url.instance.getCities()+code; + try{ + http.Response response = await Request.instance.getRequest(path: path, param: {},headers: headers); + Map data = jsonDecode(response.body); + if(data['data'] != null){ + data['data'].forEach((var city){ + CityMunicipality cityMun = CityMunicipality.fromJson(city); + cities.add(cityMun); + }); + } + }catch(e){ + throw(e.toString()); + } + return cities; + } } \ No newline at end of file diff --git a/lib/utils/profile_utilities.dart b/lib/utils/profile_utilities.dart index e13e625..68ef45b 100644 --- a/lib/utils/profile_utilities.dart +++ b/lib/utils/profile_utilities.dart @@ -4,15 +4,15 @@ import 'dart:convert'; import 'package:unit2/model/location/country.dart'; import 'package:http/http.dart' as http; import 'package:unit2/model/location/region.dart'; -import 'package:unit2/model/utils/eligibilities_choices.dart'; +import 'package:unit2/model/utils/eligibility.dart'; import 'package:unit2/utils/request.dart'; import 'package:unit2/utils/urls.dart'; class ProfileUtilities { static final ProfileUtilities _instance = ProfileUtilities(); static ProfileUtilities get instance => _instance; - Future>getEligibilities()async{ - List eligibilities=[]; + Future>getEligibilities()async{ + List eligibilities=[]; String path = Url.instance.eligibilities(); Map headers = { @@ -24,7 +24,7 @@ class ProfileUtilities { Map data = jsonDecode(response.body); if(data['data'] != null){ data['data'].forEach((var eligibility){ - EligibilityList newEligibilities = EligibilityList.fromJson(eligibility); + Eligibility newEligibilities = Eligibility.fromJson(eligibility); eligibilities.add(newEligibilities); }); } diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index 5a33754..f79b971 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -29,4 +29,7 @@ String eligibilities(){ String getProvinces(){ return "api/web_app/location/province/"; } + String getCities(){ + return "/api/web_app/location/citymun/"; + } } \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 3f8450d..b06be86 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -971,5 +971,5 @@ packages: source: hosted version: "6.2.2" sdks: - dart: ">=2.19.0 <4.0.0" + dart: ">=2.19.0 <3.0.0" flutter: ">=3.3.0" From a7ee63627df94be61516487a400dc90153ff8a3e Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Thu, 23 Feb 2023 08:53:14 +0800 Subject: [PATCH 37/86] commit before stateful builder --- lib/bloc/profile/profile_bloc.dart | 229 +++++- lib/bloc/profile/profile_event.dart | 31 +- lib/bloc/profile/profile_state.dart | 71 +- .../components/eligibility/add_modal.dart | 311 ++++++++ .../components/eligibility/edit_modal.dart | 704 ++++++++---------- .../eligibility/eligibility_screen.dart | 114 --- .../components/eligibility_screen.dart | 103 ++- lib/screens/unit2/login/login.dart | 2 +- lib/sevices/profile/eligibility_services.dart | 35 + lib/utils/alerts.dart | 17 +- lib/utils/request.dart | 43 ++ lib/utils/urls.dart | 3 + 12 files changed, 1036 insertions(+), 627 deletions(-) create mode 100644 lib/screens/profile/components/eligibility/add_modal.dart delete mode 100644 lib/screens/profile/components/eligibility/eligibility_screen.dart create mode 100644 lib/sevices/profile/eligibility_services.dart diff --git a/lib/bloc/profile/profile_bloc.dart b/lib/bloc/profile/profile_bloc.dart index 10a5515..0b755a6 100644 --- a/lib/bloc/profile/profile_bloc.dart +++ b/lib/bloc/profile/profile_bloc.dart @@ -5,7 +5,9 @@ import 'package:unit2/model/profile/basic_information/primary-information.dart'; import 'package:unit2/model/profile/eligibility.dart'; import 'package:unit2/model/profile/profileInfomation.dart'; import 'package:unit2/model/utils/eligibility.dart'; +import 'package:unit2/sevices/profile/eligibility_services.dart'; import 'package:unit2/sevices/profile/profile_service.dart'; +import 'package:unit2/test_data.dart'; import 'package:unit2/utils/location_utilities.dart'; import 'package:unit2/utils/profile_utilities.dart'; import '../../model/location/country.dart'; @@ -19,6 +21,12 @@ part 'profile_state.dart'; class ProfileBloc extends Bloc { ProfileBloc() : super(ProfileInitial()) { ProfileInformation? _profileInformation; + List? _countries; + List? _regions; + List? _eligibilities; + List? _provinces; + List? _cities; + ////========================================================================= on((event, emit) async { // try { emit(ProfileLoading()); @@ -30,58 +38,203 @@ class ProfileBloc extends Bloc { // emit(ProfileErrorState(mesage: e.toString())); // } }); - +////===================================================================== on((event, emit) { emit(ProfileLoading()); emit(EligibilityLoaded(eligibilities: event.eligibilities)); }); - - on((event, emit) async { +////==================================================================== + on((event, emit) async { + Region? currentRegion; + Province? currentProvince; + CityMunicipality? currentCity; // try{ - emit(ProfileLoading()); - List regions = await LocationUtils.instance.getRegions(); - List eligibilities = - await ProfileUtilities.instance.getEligibilities(); + emit(ProfileLoading()); + if (_countries == null) { + List countries = await LocationUtils.instance.getCountries(); + _countries = countries; + } + if (_regions == null) { + List regions = await LocationUtils.instance.getRegions(); + _regions = regions; + } + if (_eligibilities == null) { + List eligibilities = + await ProfileUtilities.instance.getEligibilities(); + _eligibilities = eligibilities; + } + +// get current country + Country? currentCountry = _countries!.firstWhere((Country country) => + country.code == event.eligibityCert.examAddress?.country!.code); +// get current eligibility + Eligibility currentEligibility = _eligibilities!.firstWhere( + (Eligibility eligibility) => + event.eligibityCert.eligibility!.id == eligibility.id); + bool? isOverseas = event.eligibityCert.overseas; - List provinces = - event.eligibityCert.examAddress?.cityMunicipality?.province?.region != null - ? await LocationUtils.instance.getProvinces( - regionCode: event.eligibityCert.examAddress!.cityMunicipality! - .province!.region!.code! - .toString()) - : []; - List citymuns = - event.eligibityCert.examAddress?.cityMunicipality != null - ? await LocationUtils.instance.getCities( + + if (event.selectedRegion != null) { + if (event.selectedProvince == null) { + currentRegion = event.selectedRegion; + List provinces = await LocationUtils.instance + .getProvinces(regionCode: event.selectedRegion!.code.toString()); + _provinces = provinces; + currentProvince = null; + currentCity = null; + } + if (event.selectedProvince != null) { + currentRegion = event.selectedRegion; + List provinces = await LocationUtils.instance + .getProvinces(regionCode: event.selectedRegion!.code.toString()); + _provinces = provinces; + currentProvince = _provinces!.firstWhere( + (Province p) => event.selectedProvince!.code == p.code); + List citymuns = await LocationUtils.instance + .getCities(code: currentProvince.code.toString()); + _cities = citymuns; + currentCity = null; + } + } else { + // if exam address region is not equal to null get the region, provinces and citymunicipalities + if (event.eligibityCert.examAddress?.cityMunicipality?.province + ?.region != + null) { + currentRegion = _regions!.firstWhere((Region r) => + event.eligibityCert.examAddress!.cityMunicipality!.province! + .region!.code == + r.code); + + List provinces = await LocationUtils.instance.getProvinces( + regionCode: event.eligibityCert.examAddress!.cityMunicipality! + .province!.region!.code! + .toString()); + _provinces = provinces; + + currentProvince = _provinces!.firstWhere((Province p) => + event.eligibityCert.examAddress!.cityMunicipality!.province! + .code == + p.code); + List citymuns = await LocationUtils.instance + .getCities( code: event.eligibityCert.examAddress!.cityMunicipality! - .province!.code!) - : []; - emit(EditNotOverseasEligibilityState( - currentEligibility: null, - currentRegion: null, + .province!.code!); + _cities = citymuns; + + currentCity = _cities!.firstWhere((CityMunicipality c) => + event.eligibityCert.examAddress!.cityMunicipality!.code == + c.code); + } + } + emit(EditEligibilityState( isOverseas: isOverseas!, - cityMuns: citymuns, - provinces: provinces, + currentEligibility: currentEligibility, + currentCountry: currentCountry, + currentRegion: currentRegion, + currentCity: currentCity, + cityMuns: _cities, + provinces: _provinces, eligibityCert: event.eligibityCert, - regions: regions, - eligibilities: eligibilities)); + countries: _countries!, + regions: _regions!, + currentProvince: currentProvince, + eligibilities: _eligibilities!)); // }catch(e){ // emit(ProfileErrorState(mesage: e.toString())); // } - });on((event,emit)async{ + + ////==================================================================== + }); + on((event, emit) async { + Region? currentRegion; + Province? currentProvince; + CityMunicipality? currentCity; + List? cities; + List? provinces; + Eligibility? currentEligibility; + final bool overseas = event.overseas; emit(ProfileLoading()); - List countries = await LocationUtils.instance.getCountries(); - List eligibilities = - await ProfileUtilities.instance.getEligibilities(); - bool? isOverseas = event.eligibityCert.overseas; - emit(EditOverseasEligibilityState( - countries: countries, - currentCOuntry: null, - currentEligibility: null, - isOverseas: isOverseas!, - eligibityCert: event.eligibityCert, - eligibilities: eligibilities)); + if (_regions == null) { + List regions = await LocationUtils.instance.getRegions(); + _regions = regions; + } + if (_eligibilities == null) { + List eligibilities = + await ProfileUtilities.instance.getEligibilities(); + _eligibilities = eligibilities; + } + if (_countries == null) { + List countries = await LocationUtils.instance.getCountries(); + _countries = countries; + } + + if(event.selectedEligibility != null){ + currentEligibility = _eligibilities!.firstWhere((Eligibility element) => element.id == event.selectedEligibility!.id); + }else{ + currentEligibility = null; + } + if (event.selectedRegion != null) { + if (event.selectedProvince == null) { + currentRegion = event.selectedRegion; + provinces = await LocationUtils.instance + .getProvinces(regionCode: event.selectedRegion!.code.toString()); + _provinces = provinces; + currentProvince = null; + currentCity = null; + } + if (event.selectedProvince != null) { + currentRegion = event.selectedRegion; + provinces = await LocationUtils.instance + .getProvinces(regionCode: event.selectedRegion!.code.toString()); + _provinces = provinces; + currentProvince = _provinces!.firstWhere( + (Province p) => event.selectedProvince!.code == p.code); + cities = await LocationUtils.instance + .getCities(code: currentProvince.code.toString()); + _cities = cities; + currentCity = null; + } + }else{ + print("executed"); + currentRegion = null; + currentProvince = null; + currentCity = null; + _provinces = null; + _cities = null; + } + + emit(AddEligibilityState( + cities: cities, + provinces: provinces, + currentEligibility: currentEligibility, + currentRegion: overseas? null: currentRegion, + currentProvince: currentProvince, + overseas: overseas, + eligibilities: _eligibilities!, + regions: _regions!, + countries: _countries!)); + }); + ////==================================================================== + on((event, emit) async { + emit(ProfileLoading()); + try { + final bool success = await EligibilityService.instance.delete( + eligibilityId: event.eligibilityId, + profileId: int.parse(event.profileId), + token: event.token); + if (success) { + event.eligibilities.removeWhere( + ((EligibityCert element) => element.id == event.eligibilityId)); + List eligibilities = event.eligibilities; + emit(DeletedState(success: success, eligibilities: eligibilities)); + } else { + emit(DeletedState( + success: success, eligibilities: event.eligibilities)); + } + } catch (e) { + emit(ProfileErrorState(mesage: e.toString())); + } }); } } diff --git a/lib/bloc/profile/profile_event.dart b/lib/bloc/profile/profile_event.dart index df9d027..ebb0beb 100644 --- a/lib/bloc/profile/profile_event.dart +++ b/lib/bloc/profile/profile_event.dart @@ -28,18 +28,35 @@ class LoadEligibility extends ProfileEvent{ List get props => []; } -class EditEligibilityOverseas extends ProfileEvent{ +class EditEligibility extends ProfileEvent{ final EligibityCert eligibityCert; - const EditEligibilityOverseas({required this.eligibityCert}); + final Region? selectedRegion; + final Province? selectedProvince; + const EditEligibility({required this.eligibityCert,this.selectedRegion, required this.selectedProvince}); @override List get props => []; } -class EditEligibilityNotOverseas extends ProfileEvent{ - final EligibityCert eligibityCert; - const EditEligibilityNotOverseas({required this.eligibityCert}); - @override - List get props => []; +class DeleteEligibility extends ProfileEvent{ + + final List eligibilities; + final String profileId; + final int eligibilityId; + final String token; +const DeleteEligibility({ required this.eligibilities, required this.eligibilityId, required this.profileId, required this.token}); + @override + List get props => [eligibilities,profileId,eligibilityId,token]; +} + +class AddEligibility extends ProfileEvent{ + + final bool overseas; + final Eligibility? selectedEligibility; + final Region? selectedRegion; + final Province? selectedProvince; + const AddEligibility({required this.selectedEligibility, required this.overseas, required this.selectedProvince,this.selectedRegion}); + @override + List get props => [overseas]; } diff --git a/lib/bloc/profile/profile_state.dart b/lib/bloc/profile/profile_state.dart index 4b38a3e..f8ef628 100644 --- a/lib/bloc/profile/profile_state.dart +++ b/lib/bloc/profile/profile_state.dart @@ -32,44 +32,63 @@ class EligibilityLoaded extends ProfileState { List get props => [eligibilities]; } -class EditNotOverseasEligibilityState extends ProfileState { +class EditEligibilityState extends ProfileState { final EligibityCert eligibityCert; final List eligibilities; - + final List countries; final List regions; - List provinces; - List cityMuns; + List? provinces; + List? cityMuns; Eligibility? currentEligibility; + Country? currentCountry; Region? currentRegion; Province? currentProvince; - bool? isOverseas; - EditNotOverseasEligibilityState( + CityMunicipality? currentCity; + bool isOverseas; + EditEligibilityState( {required this.currentEligibility, + required this.currentCountry, required this.currentRegion, required this.isOverseas, required this.cityMuns, required this.provinces, required this.eligibityCert, required this.eligibilities, - required this.regions}); - - -} - -class EditOverseasEligibilityState extends ProfileState { - final List eligibilities; - final EligibityCert eligibityCert; - final Eligibility? currentEligibility; - final List countries; - final Country? currentCOuntry; - bool? isOverseas; - - EditOverseasEligibilityState( - {required this.currentEligibility, - required this.eligibilities, - required this.isOverseas, - required this.eligibityCert, required this.countries, - required this.currentCOuntry}); - + required this.regions, + required this.currentProvince, + required this.currentCity}); +} + +class DeletedState extends ProfileState { + final List eligibilities; + final bool success; + const DeletedState({required this.eligibilities, required this.success}); + @override + List get props => [success, eligibilities]; +} + +class AddEligibilityState extends ProfileState { + bool overseas; + Eligibility? currentEligibility; + Region? currentRegion; + Province? currentProvince; + final List eligibilities; + final List countries; + final List regions; + final List? provinces; + final List? cities; + AddEligibilityState( + {required this.overseas, + required this.eligibilities, + required this.countries, + required this.regions, + required this.cities, + required this.provinces, + required this.currentEligibility, + required this.currentProvince, + required this.currentRegion, + }); + @override + List get props => [overseas]; } diff --git a/lib/screens/profile/components/eligibility/add_modal.dart b/lib/screens/profile/components/eligibility/add_modal.dart new file mode 100644 index 0000000..dc80a89 --- /dev/null +++ b/lib/screens/profile/components/eligibility/add_modal.dart @@ -0,0 +1,311 @@ +import 'package:date_time_picker/date_time_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/user/user_bloc.dart'; + +import '../../../../model/location/city.dart'; +import '../../../../model/location/country.dart'; +import '../../../../model/location/provinces.dart'; +import '../../../../model/location/region.dart'; +import '../../../../model/utils/eligibility.dart'; +import '../../../../theme-data.dart/btn-style.dart'; +import '../../../../theme-data.dart/colors.dart'; +import '../../../../theme-data.dart/form-style.dart'; +import '../../../../utils/global.dart'; +import '../../../../utils/text_container.dart'; + +class AddEligibilityScreen extends StatefulWidget { + const AddEligibilityScreen({super.key}); + + @override + State createState() => _AddEligibilityScreenState(); +} + +class _AddEligibilityScreenState extends State { + @override + Widget build(BuildContext context) { + bool? overseas; + final formKey = GlobalKey(); + final regionKey = GlobalKey(); + Region? selectedRegion; + Province? selectedProvince; + Country? selectedCountry; + CityMunicipality selectedCity; + Eligibility? selectedEligibility; + return BlocBuilder( + builder: (context, state) { + if (state is UserLoggedIn) { + return BlocBuilder( + builder: (context, state) { + if (state is AddEligibilityState) { + overseas = state.overseas; + selectedEligibility = state.currentEligibility; + return ProgressHUD( + child: Center( + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, horizontal: 18), + child: FormBuilder( + key: formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + //ELIGIBILITIES DROPDOWN + FormBuilderDropdown( + initialValue: selectedEligibility, + items: state.eligibilities + .map>( + (Eligibility eligibility) { + return DropdownMenuItem( + value: eligibility, + child: Text(eligibility.title)); + }).toList(), + name: "eligibility", + decoration: normalTextFieldStyle( + "Eligibility*", "Eligibility"), + ), + const SizedBox( + height: 20, + ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + //LICENSE NUMBER + Flexible( + flex: 1, + child: FormBuilderTextField( + name: 'license number', + initialValue: "", + decoration: normalTextFieldStyle( + "license number", "license number"), + ), + ), + const SizedBox( + width: 12, + ), + //RATING + Flexible( + flex: 1, + child: FormBuilderTextField( + name: 'rating', + initialValue: "", + decoration: normalTextFieldStyle( + 'rating', 'rating'), + ), + ), + ], + ), + ), + const SizedBox( + height: 20, + ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + //EXAM DATE + Flexible( + flex: 1, + child: DateTimePicker( + firstDate: DateTime(2000), + lastDate: DateTime(2100), + decoration: normalTextFieldStyle( + "Exam date", "Exam date"), + initialValue: "", + )), + const SizedBox( + width: 12, + ), + //VALIDITY DATE + Flexible( + flex: 1, + child: DateTimePicker( + firstDate: DateTime(2000), + lastDate: DateTime(2100), + decoration: normalTextFieldStyle( + "Validity date", "Validity date"), + initialValue: "", + ), + ), + ], + ), + ), + const SizedBox( + height: 20, + ), + Text( + "Placement of Examination/Confinement", + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith(fontSize: blockSizeVertical * 2), + ), + const SizedBox( + height: 12, + ), + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + overseas = value; + + regionKey.currentState?.fields['region']?.reset(); + + context.read().add(AddEligibility( + selectedEligibility: selectedEligibility, + overseas: overseas!, + selectedProvince: null, + selectedRegion: null)); + }, + decoration: normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Overseas Address?"), + ), + const SizedBox( + height: 12, + ), + SizedBox( + child: overseas == true + ? FormBuilderDropdown( + initialValue: null, + items: state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox( + child: Text(country.name!))); + }).toList(), + name: 'country', + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) { + selectedCountry = value; + }, + ) + : Column( + children: [ + //REGION DROPDOWN + FormBuilderDropdown( + onChanged: (Region? region) { + selectedRegion = region; + context.read().add( + AddEligibility( + selectedEligibility: selectedEligibility, + overseas: overseas!, + selectedProvince: + null, + selectedRegion: + selectedRegion)); + }, + initialValue: state.currentRegion, + decoration: normalTextFieldStyle( + "Region*", "Region"), + name: 'region', + items: state.regions + .map>( + (Region region) { + return DropdownMenuItem( + value: region, + child: + Text(region.description!)); + }).toList(), + ), + const SizedBox( + height: 12, + ), + //PROVINCE DROPDOWN + FormBuilderDropdown( + initialValue: state.currentProvince, + name: 'province', + onChanged: (Province? province) { + selectedProvince = province; + context.read().add( + AddEligibility( + selectedEligibility: selectedEligibility, + overseas: overseas!, + selectedProvince: + selectedProvince, + selectedRegion: + state.currentRegion)); + }, + items: state.provinces == null + ? [] + : state.provinces!.map< + DropdownMenuItem< + Province>>( + (Province province) { + return DropdownMenuItem( + value: province, + child: Text(province + .description!)); + }).toList(), + + decoration: normalTextFieldStyle( + "Province*", "Province")), + const SizedBox( + height: 12, + ), + FormBuilderDropdown( + onChanged: + (CityMunicipality? city) { + selectedCity = city!; + }, + decoration: normalTextFieldStyle( + "Municipality*", + "Municipality"), + name: 'municipality', + items: state.cities == null + ? [] + : state.cities!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), + initialValue: null), + const SizedBox( + height: 20, + ), + ], + )), + const Expanded( + child: SizedBox(), + ), + SizedBox( + width: screenWidth, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () {}, + child: const Text(submit)), + ), + const SizedBox( + height: 20, + ), + ], + ), + ), + ), + ), + ); + } + return Container(); + }); + } + return Container(); + }, + ); + } +} diff --git a/lib/screens/profile/components/eligibility/edit_modal.dart b/lib/screens/profile/components/eligibility/edit_modal.dart index bc76af6..ad6eb7e 100644 --- a/lib/screens/profile/components/eligibility/edit_modal.dart +++ b/lib/screens/profile/components/eligibility/edit_modal.dart @@ -2,6 +2,7 @@ import 'package:date_time_picker/date_time_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:intl/intl.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; @@ -9,6 +10,7 @@ import 'package:unit2/model/location/city.dart'; import 'package:unit2/model/login_data/employee_info/employee_info.dart'; import 'package:unit2/model/profile/eligibility.dart'; import 'package:unit2/model/utils/eligibility.dart'; +import 'package:unit2/utils/location_utilities.dart'; import '../../../../model/location/country.dart'; import '../../../../model/location/region.dart'; import '../../../../model/location/provinces.dart'; @@ -28,10 +30,19 @@ class EditEligibilityScreen extends StatefulWidget { class _EditEligibilityScreenState extends State { final formKey = GlobalKey(); - final countryKey = GlobalKey(); bool? overseas; DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); + Region? selectedRegion; + Province? selectedProvince; + CityMunicipality? selectedMunicipality; + Region? regions; + Province? province; + CityMunicipality? city; Country? selectedCountry; + Eligibility? selectedEligibility; + List? provinces; + // final examDateController = TextEditingController(); + // final validityDateController = TextEditingController(); @override Widget build(BuildContext context) { //USERBLOC @@ -43,418 +54,301 @@ class _EditEligibilityScreenState extends State { return BlocBuilder( builder: (context, state) { //EDIT ELIGIBILITY STATE - if (state is EditNotOverseasEligibilityState) { + if (state is EditEligibilityState) { overseas = state.isOverseas; - return Center( - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 25, horizontal: 18), - child: FormBuilder( - key: formKey, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - //ELIGIBILITIES DROPDOWN - FormBuilderDropdown( - initialValue: null, - items: state.eligibilities - .map>( - (Eligibility eligibility) { - return DropdownMenuItem( - value: eligibility, - child: Text(eligibility.title)); - }).toList(), - name: "eligibility", - decoration: normalTextFieldStyle( - "Eligibility", "") - .copyWith( - hintStyle: const TextStyle( - color: Colors.black, - ), - labelStyle: - const TextStyle(color: Colors.black)), - ), - const SizedBox( - height: 20, - ), - - SizedBox( - width: screenWidth, - child: Row( - children: [ - //LICENSE NUMBER - Flexible( - flex: 1, - child: FormBuilderTextField( - name: 'license number', - initialValue: - widget.eligibityCert.licenseNumber, - decoration: normalTextFieldStyle( - "license number", "license number"), - ), - ), - const SizedBox( - width: 12, - ), - //RATING - Flexible( - flex: 1, - child: FormBuilderTextField( - name: 'rating', - initialValue: widget.eligibityCert.rating - .toString(), - decoration: normalTextFieldStyle( - 'rating', 'rating'), - ), - ), - ], - ), - ), - const SizedBox( - height: 20, - ), - SizedBox( - width: screenWidth, - child: Row( - children: [ - //EXAM DATE - Flexible( - flex: 1, - child: DateTimePicker( - firstDate: DateTime(2000), - lastDate: DateTime(2100), - decoration: normalTextFieldStyle( - "Exam date", "Exam date"), - initialValue: widget - .eligibityCert.examDate == - null - ? '' - : dteFormat2.format( - widget.eligibityCert.examDate!), - )), - const SizedBox( - width: 12, - ), - //VALIDITY DATE - Flexible( - flex: 1, - child: DateTimePicker( - firstDate: DateTime(2000), - lastDate: DateTime(2100), - decoration: normalTextFieldStyle( - "Validity date", "Validity date"), - initialValue: - widget.eligibityCert.validityDate == - null - ? '' - : dteFormat2.format(widget - .eligibityCert.validityDate!), - ), - ), - ], - ), - ), - const SizedBox( - height: 20, - ), - Text( - "Placement of Examination/Confinement", - style: Theme.of(context) - .textTheme - .displaySmall! - .copyWith(fontSize: blockSizeVertical * 2), - ), - const SizedBox( - height: 12, - ), - //OVERSEAS ADDRESS SWITCH - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - EligibityCert newEligibility = - state.eligibityCert; - newEligibility.overseas = value!; - context.read().add( - EditEligibilityOverseas( - eligibityCert: newEligibility)); - }, - decoration: normalTextFieldStyle("", ''), - name: 'overseas', - title: const Text("Overseas Address?"), - ), - const SizedBox( - height: 20, - ), - Column( - children: [ - //REGION DROPDOWN - FormBuilderDropdown( - onChanged: (Region? region) {}, - // initialValue:state.eligibityCert.examAddress!.cityMunicipality!.province!.description!, - - decoration: - normalTextFieldStyle("Region*", "Region"), - - name: 'region', - items: state.regions - .map>( - (Region region) { - return DropdownMenuItem( - value: region, - child: Text(region.description!)); - }).toList(), - ), - const SizedBox( - height: 20, - ), - //PROVINCE DROPDOWN - FormBuilderDropdown( - initialValue: null, - name: 'province', - items: state.provinces.isEmpty - ? [] - : state.provinces - .map>( - (Province province) { - return DropdownMenuItem( - value: province, - child: Text( - province.description!)); - }).toList(), - decoration: normalTextFieldStyle( - "Province*", "Province")), - const SizedBox( - height: 20, - ), - FormBuilderDropdown( - decoration: normalTextFieldStyle( - "Municipality*", "Municipality"), - name: 'municipality', - items: state.cityMuns.isEmpty - ? [] - : state.cityMuns.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality c) { - return DropdownMenuItem( - value: c, - child: Text(c.description!)); - }).toList(), - initialValue: null), - const SizedBox( - height: 20, - ), - ], - ), - - SizedBox( - width: screenWidth, - height: 60, - child: ElevatedButton( - style: mainBtnStyle( - primary, Colors.transparent, second), - onPressed: () {}, - child: const Text(submit)), - ), - const SizedBox( - height: 20, - ), - ]), - ), - ), - ); - } - //=========================================================================== - if (state is EditOverseasEligibilityState) { - overseas = state.isOverseas; - return Center( - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 25, horizontal: 18), - child: FormBuilder( - key: formKey, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - //ELIGIBILITIES DROPDOWN - FormBuilderDropdown( - initialValue: null, - items: state.eligibilities - .map>( - (Eligibility eligibility) { - return DropdownMenuItem( - value: eligibility, - child: Text(eligibility.title)); - }).toList(), - name: "eligibility", - decoration: normalTextFieldStyle( - "Eligibility", "") - .copyWith( - hintStyle: const TextStyle( - color: Colors.black, - ), - labelStyle: - const TextStyle(color: Colors.black)), - ), - const SizedBox( - height: 20, - ), - - SizedBox( - width: screenWidth, - child: Row( - children: [ - //LICENSE NUMBER - Flexible( - flex: 1, - child: FormBuilderTextField( - name: 'license number', - initialValue: - widget.eligibityCert.licenseNumber, - decoration: normalTextFieldStyle( - "license number", "license number"), - ), - ), - const SizedBox( - width: 12, - ), - //RATING - Flexible( - flex: 1, - child: FormBuilderTextField( - name: 'rating', - initialValue: widget.eligibityCert.rating - .toString(), - decoration: normalTextFieldStyle( - 'rating', 'rating'), - ), - ), - ], - ), - ), - const SizedBox( - height: 20, - ), - SizedBox( - width: screenWidth, - child: Row( - children: [ - //EXAM DATE - Flexible( - flex: 1, - child: DateTimePicker( - firstDate: DateTime(2000), - lastDate: DateTime(2100), - decoration: normalTextFieldStyle( - "Exam date", "Exam date"), - initialValue: widget - .eligibityCert.examDate == - null - ? '' - : dteFormat2.format( - widget.eligibityCert.examDate!), - )), - const SizedBox( - width: 12, - ), - //VALIDITY DATE - Flexible( - flex: 1, - child: DateTimePicker( - firstDate: DateTime(2000), - lastDate: DateTime(2100), - decoration: normalTextFieldStyle( - "Validity date", "Validity date"), - initialValue: - widget.eligibityCert.validityDate == - null - ? '' - : dteFormat2.format(widget - .eligibityCert.validityDate!), - ), - ), - ], - ), - ), - const SizedBox( - height: 20, - ), - Text( - "Placement of Examination/Confinement", - style: Theme.of(context) - .textTheme - .displaySmall! - .copyWith(fontSize: blockSizeVertical * 2), - ), - const SizedBox( - height: 12, - ), - //OVERSEAS ADDRESS SWITCH - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - EligibityCert newEligibility = - state.eligibityCert; - newEligibility.overseas = value!; - // countryKey.currentState?.fields['country'] - // ?.reset(); - - - context.read().add( - EditEligibilityNotOverseas( - eligibityCert: newEligibility)); - }, - decoration: normalTextFieldStyle("", ''), - name: 'overseas', - title: const Text("Overseas Address?"), - ), - const SizedBox( - height: 20, - ), - - FormBuilderDropdown( - key: countryKey, - onChanged: (Country? country) { - - selectedCountry = country; + return ProgressHUD( + child: Center( + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, horizontal: 18), + child: FormBuilder( + key: formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + //ELIGIBILITIES DROPDOWN + FormBuilderDropdown( + onChanged: (Eligibility? eligibility){ + selectedEligibility = eligibility; }, - // initialValue:state.eligibityCert.examAddress!.cityMunicipality!.province!.description!, - initialValue:selectedCountry, + initialValue: state.currentEligibility, + items: state.eligibilities + .map>( + (Eligibility eligibility) { + return DropdownMenuItem( + value: eligibility, + child: Text(eligibility.title)); + }).toList(), + name: "eligibility", decoration: - normalTextFieldStyle("Country*", "country"), - name: 'country', - items: state.countries.isNotEmpty - ? state.countries - .map>( - (Country country) { - return DropdownMenuItem( - value: country, - child: Text(country.name!)); - }).toList() - : []), + normalTextFieldStyle("Eligibility", "") + .copyWith( + hintStyle: const TextStyle( + color: Colors.black, + ), + labelStyle: const TextStyle( + color: Colors.black)), + ), + const SizedBox( + height: 20, + ), - SizedBox( - width: screenWidth, - height: 60, - child: ElevatedButton( - style: mainBtnStyle( - primary, Colors.transparent, second), - onPressed: () {}, - child: const Text(submit)), - ), - const SizedBox( - height: 20, - ), - ]), + SizedBox( + width: screenWidth, + child: Row( + children: [ + //LICENSE NUMBER + Flexible( + flex: 1, + child: FormBuilderTextField( + name: 'license_number', + initialValue: + widget.eligibityCert.licenseNumber, + decoration: normalTextFieldStyle( + "license number", "license number"), + ), + ), + const SizedBox( + width: 12, + ), + //RATING + Flexible( + flex: 1, + child: FormBuilderTextField( + name: 'rating', + + // ignore: prefer_null_aware_operators + initialValue: widget + .eligibityCert.rating== null?null:widget.eligibityCert.rating.toString(), + + decoration: normalTextFieldStyle( + 'rating', 'rating'), + ), + ), + ], + ), + ), + const SizedBox( + height: 20, + ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + //EXAM DATE + Flexible( + flex: 1, + child: DateTimePicker( + // controller: examDateController, + firstDate: DateTime(2000), + lastDate: DateTime(2100), + decoration: normalTextFieldStyle( + "Exam date", "Exam date"), + initialValue: + widget.eligibityCert.examDate == + null + ? '' + : dteFormat2.format(widget + .eligibityCert.examDate!), + )), + const SizedBox( + width: 12, + ), + //VALIDITY DATE + Flexible( + flex: 1, + child: DateTimePicker( + // controller: validityDateController, + firstDate: DateTime(2000), + lastDate: DateTime(2100), + decoration: normalTextFieldStyle( + "Validity date", "Validity date"), + initialValue: + widget.eligibityCert.validityDate == + null + ? '' + : dteFormat2.format(widget + .eligibityCert + .validityDate!), + ), + ), + ], + ), + ), + const SizedBox( + height: 20, + ), + Text( + "Placement of Examination/Confinement", + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith(fontSize: blockSizeVertical * 2), + ), + const SizedBox( + height: 12, + ), + //OVERSEAS ADDRESS SWITCH + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + EligibityCert newEligibility = + state.eligibityCert; + newEligibility.overseas = value!; + // formKey.currentState!.reset(); + context.read().add( + EditEligibility( + selectedRegion: null, + selectedProvince: null, + eligibityCert: newEligibility)); + }, + decoration: normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Overseas Address?"), + ), + const SizedBox( + height: 20, + ), + //COUNTRY DROPDOWN + SizedBox( + child: overseas == true + ? FormBuilderDropdown( + initialValue: state.currentCountry, + + items: state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox( + child: + Text(country.name!))); + }).toList(), + name: 'country', + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) { + selectedCountry = value; + }, + ) + : Column( + children: [ + //REGION DROPDOWN + FormBuilderDropdown( + onChanged: (Region? region) { + selectedRegion = region; + + context.read().add( + EditEligibility( + eligibityCert: + state.eligibityCert, + selectedProvince: null, + selectedRegion: + selectedRegion)); + }, + initialValue: state.currentRegion, + decoration: normalTextFieldStyle( + "Region*", "Region"), + name: 'region', + items: state.regions.map< + DropdownMenuItem>( + (Region region) { + return DropdownMenuItem( + value: region, + child: Text( + region.description!)); + }).toList(), + ), + const SizedBox( + height: 20, + ), + //PROVINCE DROPDOWN + FormBuilderDropdown( + initialValue:state.currentProvince, + name: 'province', + onChanged: + (Province? province) { + selectedProvince = province; + context + .read() + .add(EditEligibility( + eligibityCert: state + .eligibityCert, + selectedProvince: + selectedProvince, + selectedRegion: state + .currentRegion)); + }, + items: state.provinces == null + ? [] + : state.provinces!.map< + DropdownMenuItem< + Province>>( + (Province province) { + return DropdownMenuItem( + value: province, + child: Text(province + .description!)); + }).toList(), + decoration: + normalTextFieldStyle( + "Province*", + "Province")), + const SizedBox( + height: 20, + ), + FormBuilderDropdown< + CityMunicipality>( + onChanged: (CityMunicipality? city){ + selectedMunicipality = city; + }, + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), + name: 'municipality', + items: state.cityMuns == null + ? [] + : state.cityMuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), + initialValue: + state.currentCity), + const SizedBox( + height: 20, + ), + ], + )), + const Expanded( + child: SizedBox(), + ), + + SizedBox( + width: screenWidth, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () {}, + child: const Text(submit)), + ), + const SizedBox( + height: 20, + ), + ]), + ), ), ), ); } - return Container(); }, ); diff --git a/lib/screens/profile/components/eligibility/eligibility_screen.dart b/lib/screens/profile/components/eligibility/eligibility_screen.dart deleted file mode 100644 index 703629d..0000000 --- a/lib/screens/profile/components/eligibility/eligibility_screen.dart +++ /dev/null @@ -1,114 +0,0 @@ -// import 'package:app_popup_menu/app_popup_menu.dart'; -// import 'package:flutter/material.dart'; -// import 'package:flutter/src/widgets/framework.dart'; -// import 'package:flutter/src/widgets/placeholder.dart'; -// import 'package:flutter_bloc/flutter_bloc.dart'; -// import 'package:fluttericon/font_awesome_icons.dart'; -// import 'package:unit2/bloc/profile/profile_bloc.dart'; -// import 'package:unit2/bloc/user/user_bloc.dart'; -// import 'package:unit2/model/profile/eligibility.dart'; -// import 'package:unit2/screens/profile/components/eligibility/edit_modal.dart'; -// import 'package:unit2/theme-data.dart/box_shadow.dart'; -// import 'package:unit2/theme-data.dart/colors.dart'; -// import 'package:unit2/utils/alerts.dart'; -// import 'package:unit2/utils/global.dart'; -// import 'package:unit2/utils/text_container.dart'; -// import 'package:unit2/widgets/add_leading.dart'; -// import 'package:unit2/widgets/empty_data.dart'; - -// class EligibiltyScreen extends StatefulWidget { -// const EligibiltyScreen({ -// super.key, -// }); - -// @override -// State createState() => _EligibiltyScreenState(); -// } - -// class _EligibiltyScreenState extends State { -// @override -// Widget build(BuildContext context) { -// return Scaffold( -// appBar: AppBar( -// title: const Text(elibilityScreenTitle), -// centerTitle: true, -// backgroundColor: primary, -// actions: context.read()[AddLeading( -// onPressed: () => () {}, -// )], -// ), -// body: BlocBuilder( -// builder: (context, state) { -// return BlocBuilder( -// builder: (context, state) { -// if (state is EligibilityLoaded) { -// return ListView.builder( -// padding: const EdgeInsets.symmetric( -// vertical: 8, horizontal: 10), -// itemCount: state.eligibilities.length, -// itemBuilder: (BuildContext context, int index) { -// String title = -// state.eligibilities[index].eligibility!.title!; -// return Column( -// mainAxisAlignment: MainAxisAlignment.start, -// crossAxisAlignment: CrossAxisAlignment.start, -// children: [ -// Container( -// width: screenWidth, -// padding: const EdgeInsets.symmetric( -// horizontal: 12, vertical: 8), -// decoration: box1(), -// child: Row( -// children: [ -// Expanded( -// child: Column( -// mainAxisAlignment: -// MainAxisAlignment.start, -// crossAxisAlignment: -// CrossAxisAlignment.start, -// children: [ -// Text( -// title, -// style: Theme.of(context) -// .textTheme -// .titleMedium! -// .copyWith( -// fontWeight: -// FontWeight.w500), -// ), -// const Divider(), -// const SizedBox( -// height: 5, -// ), -// Text( -// "$licenseNumber: ${state.eligibilities[index].licenseNumber == null ? 'N/A' : state.eligibilities[index].licenseNumber.toString()}", -// style: Theme.of(context) -// .textTheme -// .titleSmall), -// const SizedBox( -// height: 3, -// ), -// Text( -// " : ${state.eligibilities[index].rating ?? 'N/A'}.", -// style: Theme.of(context) -// .textTheme -// .titleSmall) -// ]), -// ), -// ] -// ), -// ), -// const SizedBox( -// height: 5, -// ) -// ], -// ); -// }); -// } -// return Container(); -// }, -// ); -// }, -// )); -// } -// } diff --git a/lib/screens/profile/components/eligibility_screen.dart b/lib/screens/profile/components/eligibility_screen.dart index 257387d..8544444 100644 --- a/lib/screens/profile/components/eligibility_screen.dart +++ b/lib/screens/profile/components/eligibility_screen.dart @@ -1,20 +1,18 @@ import 'package:app_popup_menu/app_popup_menu.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:fluttericon/font_awesome_icons.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/eligibility.dart'; +import 'package:unit2/screens/profile/components/eligibility/add_modal.dart'; import 'package:unit2/screens/profile/components/eligibility/edit_modal.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/add_leading.dart'; -import 'package:unit2/widgets/empty_data.dart'; import '../../../utils/alerts.dart'; @@ -23,33 +21,56 @@ class EligibiltyScreen extends StatelessWidget { @override Widget build(BuildContext context) { + String? token; + String? profileId; return Scaffold( appBar: AppBar( title: const Text(elibilityScreenTitle), centerTitle: true, backgroundColor: primary, - actions: [AddLeading(onPressed: () {})], + actions: [AddLeading(onPressed: () { + context.read().add(const AddEligibility(overseas: false,selectedProvince: null,selectedRegion: null,selectedEligibility: null)); + })], ), body: BlocBuilder( builder: (context, state) { if (state is UserLoggedIn) { + token = state.userData!.user!.login!.token; + profileId = + state.userData!.user!.login!.user!.profileId.toString(); return ProgressHUD( child: BlocConsumer( listener: (context, state) { - if(state is ProfileLoading){ - final progress = ProgressHUD.of(context); - progress!.showWithText("Loading"); + if (state is EditEligibilityState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); } - if (state is EditNotOverseasEligibilityState) { + if (state is ProfileLoading) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading"); + } + if (state is EligibilityLoaded) { final progress = ProgressHUD.of(context); progress!.dismiss(); - }if (state is EditOverseasEligibilityState) { - final progress = ProgressHUD.of(context); - progress!.dismiss(); - }if(state is EligibilityLoaded){ - final progress = ProgressHUD.of(context); + }if(state is AddEligibilityState){ + final progress = ProgressHUD.of(context); progress!.dismiss(); } + if (state is DeletedState) { + if (state.success) { + successAlert(context, "Deletion Successfull", + "Eligibility has been deleted successfully", () { + context.read().add(LoadEligibility( + eligibilities: state.eligibilities)); + }); + } else { + errorAlert(context, "Deletion Failed", + "Error deleting eligibility", () { + context.read().add(LoadEligibility( + eligibilities: state.eligibilities)); + }); + } + } // TODO: implement listener }, builder: (context, state) { @@ -62,7 +83,7 @@ class EligibiltyScreen extends StatelessWidget { itemCount: state.eligibilities.length, itemBuilder: (BuildContext context, int index) { String title = state - .eligibilities[index].eligibility!.title!; + .eligibilities[index].eligibility!.title; return Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, @@ -104,7 +125,7 @@ class EligibiltyScreen extends StatelessWidget { height: 3, ), Text( - "Rating : ${state.eligibilities[index].rating}.", + "Rating : ${state.eligibilities[index].rating ?? 'N/A'}.", style: Theme.of(context) .textTheme .titleSmall) @@ -115,10 +136,19 @@ class EligibiltyScreen extends StatelessWidget { elevation: 3, onSelected: (value) { if (value == 2) { - confirmAlert( - context, - () => null, - "Delete?", + confirmAlert(context, () { + BlocProvider.of< + ProfileBloc>(context) + .add(DeleteEligibility( + eligibilities: state + .eligibilities, + eligibilityId: state + .eligibilities[ + index] + .id!, + profileId: profileId!, + token: token!)); + }, "Delete?", "Confirm Delete?"); } if (value == 1) { @@ -140,17 +170,12 @@ class EligibiltyScreen extends StatelessWidget { overseas; progress! .showWithText("Loading..."); - if (eligibityCert.overseas!) { - context.read().add( - EditEligibilityOverseas( - eligibityCert: - eligibityCert)); - } else { - context.read().add( - EditEligibilityNotOverseas( - eligibityCert: - eligibityCert)); - } + context.read().add( + EditEligibility( + selectedProvince: null, + selectedRegion: null, + eligibityCert: + eligibityCert)); } }, menuItems: [ @@ -183,12 +208,22 @@ class EligibiltyScreen extends StatelessWidget { ); }); } - if (state is EditNotOverseasEligibilityState) { + if (state is EditEligibilityState) { return EditEligibilityScreen( eligibityCert: state.eligibityCert); - }if(state is EditOverseasEligibilityState){ - return EditEligibilityScreen( - eligibityCert: state.eligibityCert); + }if(state is AddEligibilityState){ + return const AddEligibilityScreen(); + } + if (state is DeletedState) { + return Center( + child: Container( + child: Text(state.success.toString())), + ); + } + if (state is ProfileErrorState) { + return Center( + child: Text(state.mesage), + ); } return Container(); }, diff --git a/lib/screens/unit2/login/login.dart b/lib/screens/unit2/login/login.dart index 045bcc6..317adbc 100644 --- a/lib/screens/unit2/login/login.dart +++ b/lib/screens/unit2/login/login.dart @@ -60,7 +60,7 @@ class _UniT2LoginState extends State { if (state is InvalidCredentials) { final progress = ProgressHUD.of(context); progress!.dismiss(); - errorAlert(context, "Error Login", state.message); + errorAlert(context, "Error Login", state.message,(){}); context.read().add(LoadVersion()); } }, builder: (context, state) { diff --git a/lib/sevices/profile/eligibility_services.dart b/lib/sevices/profile/eligibility_services.dart new file mode 100644 index 0000000..0f3cbda --- /dev/null +++ b/lib/sevices/profile/eligibility_services.dart @@ -0,0 +1,35 @@ +import 'dart:convert'; + +import 'package:unit2/utils/request.dart'; +import 'package:unit2/utils/urls.dart'; +import 'package:http/http.dart' as http; +class EligibilityService{ + static final EligibilityService _instance = EligibilityService(); + static EligibilityService get instance => _instance; + + Future delete({required int eligibilityId, required int profileId,required String token})async{ + bool? success; + String Authtoken = "Token $token"; + String path = "${Url.instance.deleteEligibility()}$profileId/"; + Map body = { "eligibility_id": eligibilityId}; + Map params ={"force_mode":"true"}; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': "Token $token" + }; + // try{ + http.Response response = await Request.instance.deleteRequest(path: path, headers: headers, body: body, param: params); + if(response.statusCode == 200){ + Map data = jsonDecode(response.body); + success = data['success']; + }else{ + success = false; + } + + // }catch(e){ + // throw(e.toString()); + // } + return success!; + } + +} \ No newline at end of file diff --git a/lib/utils/alerts.dart b/lib/utils/alerts.dart index 2a39072..5257be1 100644 --- a/lib/utils/alerts.dart +++ b/lib/utils/alerts.dart @@ -36,7 +36,7 @@ confirmAlert(context, Function() yes,String title, String subtitle) { ).show(); } -errorAlert(context, title, description) { +errorAlert(context, title, description,Function() func) { AwesomeDialog( width: blockSizeHorizontal * 90, context: context, @@ -45,7 +45,20 @@ errorAlert(context, title, description) { headerAnimationLoop: false, title: title, desc: description, - btnOkOnPress: () {}, + btnOkOnPress: func, + btnOkColor: Colors.red, + ).show(); +} +successAlert(context, title, description,Function() func) { + AwesomeDialog( + width: blockSizeHorizontal * 90, + context: context, + dialogType: DialogType.success, + animType: AnimType.scale, + headerAnimationLoop: false, + title: title, + desc: description, + btnOkOnPress: func, btnOkColor: Colors.red, ).show(); } diff --git a/lib/utils/request.dart b/lib/utils/request.dart index 7d5e215..b99eabf 100644 --- a/lib/utils/request.dart +++ b/lib/utils/request.dart @@ -96,4 +96,47 @@ class Request { } return response; } + + Future deleteRequest( + {required String path, + required Map? headers, + required Map? body, + required Map? param}) async { + Response response; + // try { + response = await delete(Uri.http(host, path, param), + headers: headers, body: jsonEncode(body)) + .timeout(Duration(seconds: requestTimeout)); + // } on TimeoutException catch (_) { + // Fluttertoast.showToast( + // msg: timeoutError, + // toastLength: Toast.LENGTH_LONG, + // gravity: ToastGravity.BOTTOM, + // backgroundColor: Colors.black, + // ); + // throw (timeoutError); + // } on SocketException catch (_) { + // Fluttertoast.showToast( + // msg: timeoutError, + // toastLength: Toast.LENGTH_LONG, + // gravity: ToastGravity.BOTTOM, + // backgroundColor: Colors.black, + // ); + // throw (timeoutError); + // } on FormatException catch (_) { + // throw const FormatException(formatError); + // } on HttpException catch (_) { + // throw const HttpException(httpError); + // } on Error catch (e) { + // debugPrint("post request error: $e"); + // Fluttertoast.showToast( + // msg: onError, + // toastLength: Toast.LENGTH_LONG, + // gravity: ToastGravity.BOTTOM, + // backgroundColor: Colors.black, + // ); + // throw (e.toString()); + // } + return response; + } } diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index f79b971..a954fda 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -18,6 +18,9 @@ class Url { String eligibilities(){ return "/api/jobnet_app/eligibilities/"; +} +String deleteEligibility(){ + return "/api/jobnet_app/profile/pds/eligibility/"; } // location utils path String getCounties(){ From 424b1f3b470f528bd541235d60c9453e3f3c1b87 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Thu, 23 Feb 2023 13:51:53 +0800 Subject: [PATCH 38/86] resolve address issues --- lib/bloc/profile/profile_bloc.dart | 107 +--- lib/bloc/profile/profile_event.dart | 14 +- lib/bloc/profile/profile_state.dart | 59 +- .../components/eligibility/add_modal.dart | 605 ++++++++++-------- .../components/eligibility/edit_modal.dart | 328 +++++----- .../components/eligibility_screen.dart | 5 +- lib/sevices/login_service/auth_service.dart | 7 +- lib/utils/urls.dart | 7 +- linux/flutter/generated_plugin_registrant.cc | 4 + linux/flutter/generated_plugins.cmake | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 8 + pubspec.yaml | 1 + .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 15 files changed, 575 insertions(+), 577 deletions(-) diff --git a/lib/bloc/profile/profile_bloc.dart b/lib/bloc/profile/profile_bloc.dart index 0b755a6..b7aace6 100644 --- a/lib/bloc/profile/profile_bloc.dart +++ b/lib/bloc/profile/profile_bloc.dart @@ -74,70 +74,12 @@ class ProfileBloc extends Bloc { bool? isOverseas = event.eligibityCert.overseas; - if (event.selectedRegion != null) { - if (event.selectedProvince == null) { - currentRegion = event.selectedRegion; - List provinces = await LocationUtils.instance - .getProvinces(regionCode: event.selectedRegion!.code.toString()); - _provinces = provinces; - currentProvince = null; - currentCity = null; - } - if (event.selectedProvince != null) { - currentRegion = event.selectedRegion; - List provinces = await LocationUtils.instance - .getProvinces(regionCode: event.selectedRegion!.code.toString()); - _provinces = provinces; - currentProvince = _provinces!.firstWhere( - (Province p) => event.selectedProvince!.code == p.code); - List citymuns = await LocationUtils.instance - .getCities(code: currentProvince.code.toString()); - _cities = citymuns; - currentCity = null; - } - } else { - // if exam address region is not equal to null get the region, provinces and citymunicipalities - if (event.eligibityCert.examAddress?.cityMunicipality?.province - ?.region != - null) { - currentRegion = _regions!.firstWhere((Region r) => - event.eligibityCert.examAddress!.cityMunicipality!.province! - .region!.code == - r.code); - - List provinces = await LocationUtils.instance.getProvinces( - regionCode: event.eligibityCert.examAddress!.cityMunicipality! - .province!.region!.code! - .toString()); - _provinces = provinces; - - currentProvince = _provinces!.firstWhere((Province p) => - event.eligibityCert.examAddress!.cityMunicipality!.province! - .code == - p.code); - List citymuns = await LocationUtils.instance - .getCities( - code: event.eligibityCert.examAddress!.cityMunicipality! - .province!.code!); - _cities = citymuns; - - currentCity = _cities!.firstWhere((CityMunicipality c) => - event.eligibityCert.examAddress!.cityMunicipality!.code == - c.code); - } - } + emit(EditEligibilityState( isOverseas: isOverseas!, - currentEligibility: currentEligibility, - currentCountry: currentCountry, - currentRegion: currentRegion, - currentCity: currentCity, - cityMuns: _cities, - provinces: _provinces, eligibityCert: event.eligibityCert, countries: _countries!, regions: _regions!, - currentProvince: currentProvince, eligibilities: _eligibilities!)); // }catch(e){ @@ -147,13 +89,9 @@ class ProfileBloc extends Bloc { ////==================================================================== }); on((event, emit) async { - Region? currentRegion; - Province? currentProvince; - CityMunicipality? currentCity; List? cities; List? provinces; Eligibility? currentEligibility; - final bool overseas = event.overseas; emit(ProfileLoading()); if (_regions == null) { List regions = await LocationUtils.instance.getRegions(); @@ -168,49 +106,8 @@ class ProfileBloc extends Bloc { List countries = await LocationUtils.instance.getCountries(); _countries = countries; } - - if(event.selectedEligibility != null){ - currentEligibility = _eligibilities!.firstWhere((Eligibility element) => element.id == event.selectedEligibility!.id); - }else{ - currentEligibility = null; - } - if (event.selectedRegion != null) { - if (event.selectedProvince == null) { - currentRegion = event.selectedRegion; - provinces = await LocationUtils.instance - .getProvinces(regionCode: event.selectedRegion!.code.toString()); - _provinces = provinces; - currentProvince = null; - currentCity = null; - } - if (event.selectedProvince != null) { - currentRegion = event.selectedRegion; - provinces = await LocationUtils.instance - .getProvinces(regionCode: event.selectedRegion!.code.toString()); - _provinces = provinces; - currentProvince = _provinces!.firstWhere( - (Province p) => event.selectedProvince!.code == p.code); - cities = await LocationUtils.instance - .getCities(code: currentProvince.code.toString()); - _cities = cities; - currentCity = null; - } - }else{ - print("executed"); - currentRegion = null; - currentProvince = null; - currentCity = null; - _provinces = null; - _cities = null; - } - + emit(AddEligibilityState( - cities: cities, - provinces: provinces, - currentEligibility: currentEligibility, - currentRegion: overseas? null: currentRegion, - currentProvince: currentProvince, - overseas: overseas, eligibilities: _eligibilities!, regions: _regions!, countries: _countries!)); diff --git a/lib/bloc/profile/profile_event.dart b/lib/bloc/profile/profile_event.dart index ebb0beb..11bd0d3 100644 --- a/lib/bloc/profile/profile_event.dart +++ b/lib/bloc/profile/profile_event.dart @@ -30,15 +30,12 @@ class LoadEligibility extends ProfileEvent{ class EditEligibility extends ProfileEvent{ final EligibityCert eligibityCert; - final Region? selectedRegion; - final Province? selectedProvince; - const EditEligibility({required this.eligibityCert,this.selectedRegion, required this.selectedProvince}); + const EditEligibility({required this.eligibityCert}); @override List get props => []; } class DeleteEligibility extends ProfileEvent{ - final List eligibilities; final String profileId; final int eligibilityId; @@ -49,14 +46,7 @@ const DeleteEligibility({ required this.eligibilities, required this.eligibility } class AddEligibility extends ProfileEvent{ - - final bool overseas; - final Eligibility? selectedEligibility; - final Region? selectedRegion; - final Province? selectedProvince; - const AddEligibility({required this.selectedEligibility, required this.overseas, required this.selectedProvince,this.selectedRegion}); - @override - List get props => [overseas]; + } diff --git a/lib/bloc/profile/profile_state.dart b/lib/bloc/profile/profile_state.dart index f8ef628..52a2eb5 100644 --- a/lib/bloc/profile/profile_state.dart +++ b/lib/bloc/profile/profile_state.dart @@ -31,33 +31,22 @@ class EligibilityLoaded extends ProfileState { @override List get props => [eligibilities]; } - class EditEligibilityState extends ProfileState { final EligibityCert eligibityCert; final List eligibilities; final List countries; final List regions; - List? provinces; - List? cityMuns; - Eligibility? currentEligibility; - Country? currentCountry; - Region? currentRegion; - Province? currentProvince; - CityMunicipality? currentCity; - bool isOverseas; - EditEligibilityState( - {required this.currentEligibility, - required this.currentCountry, - required this.currentRegion, - required this.isOverseas, - required this.cityMuns, - required this.provinces, - required this.eligibityCert, - required this.eligibilities, - required this.countries, - required this.regions, - required this.currentProvince, - required this.currentCity}); + final bool isOverseas; + const EditEligibilityState({ + required this.isOverseas, + required this.eligibityCert, + required this.eligibilities, + required this.countries, + required this.regions, + }); + @override + List get props => + [isOverseas, eligibityCert, eligibilities, regions, countries]; } class DeletedState extends ProfileState { @@ -69,26 +58,14 @@ class DeletedState extends ProfileState { } class AddEligibilityState extends ProfileState { - bool overseas; - Eligibility? currentEligibility; - Region? currentRegion; - Province? currentProvince; final List eligibilities; final List countries; final List regions; - final List? provinces; - final List? cities; - AddEligibilityState( - {required this.overseas, - required this.eligibilities, - required this.countries, - required this.regions, - required this.cities, - required this.provinces, - required this.currentEligibility, - required this.currentProvince, - required this.currentRegion, - }); - @override - List get props => [overseas]; + const AddEligibilityState({ + required this.eligibilities, + required this.countries, + required this.regions, + }); + @override + List get props => [eligibilities,countries,regions]; } diff --git a/lib/screens/profile/components/eligibility/add_modal.dart b/lib/screens/profile/components/eligibility/add_modal.dart index dc80a89..7a79eea 100644 --- a/lib/screens/profile/components/eligibility/add_modal.dart +++ b/lib/screens/profile/components/eligibility/add_modal.dart @@ -1,10 +1,10 @@ import 'package:date_time_picker/date_time_picker.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:intl/intl.dart'; +import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; @@ -17,6 +17,7 @@ import '../../../../theme-data.dart/btn-style.dart'; import '../../../../theme-data.dart/colors.dart'; import '../../../../theme-data.dart/form-style.dart'; import '../../../../utils/global.dart'; +import '../../../../utils/location_utilities.dart'; import '../../../../utils/text_container.dart'; class AddEligibilityScreen extends StatefulWidget { @@ -27,285 +28,355 @@ class AddEligibilityScreen extends StatefulWidget { } class _AddEligibilityScreenState extends State { + final formKey = GlobalKey(); + final provinceKey = GlobalKey(); + bool? overseas; + DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); + Region? selectedRegion; + Province? selectedProvince; + CityMunicipality? selectedMunicipality; + Country? selectedCountry; + Eligibility? selectedEligibility; + List? provinces; + List? citymuns; + bool provinceCall = false; + bool cityCall = false; @override Widget build(BuildContext context) { - bool? overseas; - final formKey = GlobalKey(); - final regionKey = GlobalKey(); - Region? selectedRegion; - Province? selectedProvince; - Country? selectedCountry; - CityMunicipality selectedCity; - Eligibility? selectedEligibility; + //USERBLOC return BlocBuilder( builder: (context, state) { + //LOGGED IN USER STATE if (state is UserLoggedIn) { + //PROFIILE BLOC return BlocBuilder( - builder: (context, state) { - if (state is AddEligibilityState) { - overseas = state.overseas; - selectedEligibility = state.currentEligibility; - return ProgressHUD( - child: Center( - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 25, horizontal: 18), - child: FormBuilder( - key: formKey, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - //ELIGIBILITIES DROPDOWN - FormBuilderDropdown( - initialValue: selectedEligibility, - items: state.eligibilities - .map>( - (Eligibility eligibility) { - return DropdownMenuItem( - value: eligibility, - child: Text(eligibility.title)); - }).toList(), - name: "eligibility", - decoration: normalTextFieldStyle( - "Eligibility*", "Eligibility"), - ), - const SizedBox( - height: 20, - ), - SizedBox( - width: screenWidth, - child: Row( - children: [ - //LICENSE NUMBER - Flexible( - flex: 1, - child: FormBuilderTextField( - name: 'license number', - initialValue: "", - decoration: normalTextFieldStyle( - "license number", "license number"), - ), + buildWhen: (previous, current) { + if (state is EditEligibilityState) {} + return false; + }, + builder: (context, state) { + //EDIT ELIGIBILITY STATE + if (state is AddEligibilityState) { + return ProgressHUD( + child: Center( + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, horizontal: 18), + child: FormBuilder( + key: formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + //ELIGIBILITIES DROPDOWN + FormBuilderDropdown( + onChanged: (Eligibility? eligibility) { + selectedEligibility = eligibility; + }, + initialValue: state.eligibilities[0], + items: state.eligibilities + .map>( + (Eligibility eligibility) { + return DropdownMenuItem( + value: eligibility, + child: Text(eligibility.title)); + }).toList(), + name: "eligibility", + decoration: + normalTextFieldStyle("Eligibility", "") + .copyWith( + hintStyle: const TextStyle( + color: Colors.black, + ), + labelStyle: const TextStyle( + color: Colors.black)), + ), + const SizedBox( + height: 20, + ), + + SizedBox( + width: screenWidth, + child: Row( + children: [ + //LICENSE NUMBER + Flexible( + flex: 1, + child: FormBuilderTextField( + name: 'license_number', + initialValue:null, + + decoration: normalTextFieldStyle( + "license number", "license number"), + ), + ), + const SizedBox( + width: 12, + ), + //RATING + Flexible( + flex: 1, + child: FormBuilderTextField( + name: 'rating', + + // ignore: prefer_null_aware_operators + initialValue:null, + + + decoration: normalTextFieldStyle( + 'rating', 'rating'), + ), + ), + ], ), - const SizedBox( - width: 12, - ), - //RATING - Flexible( - flex: 1, - child: FormBuilderTextField( - name: 'rating', - initialValue: "", - decoration: normalTextFieldStyle( - 'rating', 'rating'), - ), - ), - ], - ), - ), - const SizedBox( - height: 20, - ), - SizedBox( - width: screenWidth, - child: Row( - children: [ - //EXAM DATE - Flexible( - flex: 1, - child: DateTimePicker( - firstDate: DateTime(2000), - lastDate: DateTime(2100), - decoration: normalTextFieldStyle( - "Exam date", "Exam date"), - initialValue: "", - )), - const SizedBox( - width: 12, - ), - //VALIDITY DATE - Flexible( - flex: 1, - child: DateTimePicker( - firstDate: DateTime(2000), - lastDate: DateTime(2100), - decoration: normalTextFieldStyle( - "Validity date", "Validity date"), - initialValue: "", - ), - ), - ], - ), - ), - const SizedBox( - height: 20, - ), - Text( - "Placement of Examination/Confinement", - style: Theme.of(context) - .textTheme - .displaySmall! - .copyWith(fontSize: blockSizeVertical * 2), - ), - const SizedBox( - height: 12, - ), - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - overseas = value; - - regionKey.currentState?.fields['region']?.reset(); - - context.read().add(AddEligibility( - selectedEligibility: selectedEligibility, - overseas: overseas!, - selectedProvince: null, - selectedRegion: null)); - }, - decoration: normalTextFieldStyle("", ''), - name: 'overseas', - title: const Text("Overseas Address?"), - ), - const SizedBox( - height: 12, - ), - SizedBox( - child: overseas == true - ? FormBuilderDropdown( - initialValue: null, - items: state.countries - .map>( - (Country country) { - return DropdownMenuItem( - value: country, - child: FittedBox( - child: Text(country.name!))); - }).toList(), - name: 'country', - decoration: normalTextFieldStyle( - "Country*", "Country"), - onChanged: (Country? value) { - selectedCountry = value; - }, - ) - : Column( - children: [ - //REGION DROPDOWN - FormBuilderDropdown( - onChanged: (Region? region) { - selectedRegion = region; - context.read().add( - AddEligibility( - selectedEligibility: selectedEligibility, - overseas: overseas!, - selectedProvince: - null, - selectedRegion: - selectedRegion)); - }, - initialValue: state.currentRegion, + ), + const SizedBox( + height: 20, + ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + //EXAM DATE + Flexible( + flex: 1, + child: DateTimePicker( + // controller: examDateController, + firstDate: DateTime(2000), + lastDate: DateTime(2100), decoration: normalTextFieldStyle( - "Region*", "Region"), - name: 'region', - items: state.regions - .map>( - (Region region) { - return DropdownMenuItem( - value: region, - child: - Text(region.description!)); - }).toList(), - ), - const SizedBox( - height: 12, - ), - //PROVINCE DROPDOWN - FormBuilderDropdown( - initialValue: state.currentProvince, - name: 'province', - onChanged: (Province? province) { - selectedProvince = province; - context.read().add( - AddEligibility( - selectedEligibility: selectedEligibility, - overseas: overseas!, - selectedProvince: - selectedProvince, - selectedRegion: - state.currentRegion)); - }, - items: state.provinces == null - ? [] - : state.provinces!.map< - DropdownMenuItem< - Province>>( - (Province province) { - return DropdownMenuItem( - value: province, - child: Text(province - .description!)); - }).toList(), - - decoration: normalTextFieldStyle( - "Province*", "Province")), - const SizedBox( - height: 12, - ), - FormBuilderDropdown( - onChanged: - (CityMunicipality? city) { - selectedCity = city!; - }, - decoration: normalTextFieldStyle( - "Municipality*", - "Municipality"), - name: 'municipality', - items: state.cities == null - ? [] - : state.cities!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality c) { - return DropdownMenuItem( - value: c, - child: Text(c - .description!)); - }).toList(), - initialValue: null), - const SizedBox( - height: 20, - ), - ], - )), - const Expanded( - child: SizedBox(), - ), - SizedBox( - width: screenWidth, - height: 60, - child: ElevatedButton( - style: mainBtnStyle( - primary, Colors.transparent, second), - onPressed: () {}, - child: const Text(submit)), - ), - const SizedBox( - height: 20, - ), - ], + "Exam date", "Exam date"), + initialValue:null, + )), + const SizedBox( + width: 12, + ), + //VALIDITY DATE + Flexible( + flex: 1, + child: DateTimePicker( + // controller: validityDateController, + firstDate: DateTime(2000), + lastDate: DateTime(2100), + decoration: normalTextFieldStyle( + "Validity date", "Validity date"), + initialValue:null, + ), + ), + ], + ), + ), + const SizedBox( + height: 20, + ), + Text( + "Placement of Examination/Confinement", + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith(fontSize: blockSizeVertical * 2), + ), + const SizedBox( + height: 12, + ), + //OVERSEAS ADDRESS SWITCH + Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value; + }); + }, + decoration: normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Overseas Address?"), + ), + const SizedBox( + height: 20, + ), + //COUNTRY DROPDOWN + SizedBox( + child: overseas == true + ? FormBuilderDropdown( + initialValue: null, + items: state.countries.map< + DropdownMenuItem< + Country>>( + (Country country) { + return DropdownMenuItem< + Country>( + value: country, + child: FittedBox( + child: Text( + country.name!))); + }).toList(), + name: 'country', + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) { + selectedCountry = value; + }, + ) + : Column( + children: [ + //REGION DROPDOWN + FormBuilderDropdown( + onChanged: + (Region? region) async { + setState(() { + provinceCall = true; + }); + selectedRegion = region; + getProvinces(); + }, + initialValue: selectedRegion, + decoration: + normalTextFieldStyle( + "Region*", "Region"), + name: 'region', + items: state.regions.map< + DropdownMenuItem< + Region>>( + (Region region) { + return DropdownMenuItem< + Region>( + value: region, + child: Text(region + .description!)); + }).toList(), + ), + const SizedBox( + height: 20, + ), + //PROVINCE DROPDOWN + SizedBox( + height: 50, + child: ModalProgressHUD( + inAsyncCall: cityCall, + child: DropdownButtonFormField< + Province?>( + isExpanded: true, + value: selectedProvince, + onChanged: (Province? + province) { + setState(() { + cityCall = true; + }); + selectedProvince = province; + getCities(); + }, + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province + province) { + return DropdownMenuItem( + value: + province, + child: FittedBox( + child: Text( + province + .description!), + )); + }).toList(), + decoration: + normalTextFieldStyle( + "Province*", + "Province")), + ), + ), + const SizedBox( + height: 20, + ), + SizedBox( + height: 50, + child: DropdownButtonFormField< + CityMunicipality>( + onChanged: + (CityMunicipality? + city) { + selectedMunicipality = + city; + }, + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality + c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), + ), + ), + const SizedBox( + height: 20, + ), + ], + )), + ], + ), + + const Expanded( + child: SizedBox(), + ), + + SizedBox( + width: screenWidth, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () {}, + child: const Text(submit)), + ), + const SizedBox( + height: 20, + ), + ]), ), ), ), - ), - ); - } - return Container(); - }); + ); + } + return Container(); + }, + ); } return Container(); }, ); } + + Future getProvinces() async { + List _provinces = await LocationUtils.instance + .getProvinces(regionCode: selectedRegion!.code.toString()); + setState(() { + provinces = _provinces; + selectedProvince = provinces![0]; + getCities(); + provinceCall = false; + }); + } + Future getCities()async{ + List _cities = await LocationUtils.instance.getCities(code: selectedProvince!.code.toString()); + citymuns = _cities; + setState(() { + selectedMunicipality = _cities[0]; + cityCall = false; + }); + } } diff --git a/lib/screens/profile/components/eligibility/edit_modal.dart b/lib/screens/profile/components/eligibility/edit_modal.dart index ad6eb7e..2f3170a 100644 --- a/lib/screens/profile/components/eligibility/edit_modal.dart +++ b/lib/screens/profile/components/eligibility/edit_modal.dart @@ -4,6 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:intl/intl.dart'; +import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/location/city.dart'; @@ -30,17 +31,18 @@ class EditEligibilityScreen extends StatefulWidget { class _EditEligibilityScreenState extends State { final formKey = GlobalKey(); + final provinceKey = GlobalKey(); bool? overseas; DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); Region? selectedRegion; Province? selectedProvince; CityMunicipality? selectedMunicipality; - Region? regions; - Province? province; - CityMunicipality? city; Country? selectedCountry; Eligibility? selectedEligibility; List? provinces; + List? citymuns; + bool provinceCall = false; + bool cityCall = false; // final examDateController = TextEditingController(); // final validityDateController = TextEditingController(); @override @@ -52,10 +54,13 @@ class _EditEligibilityScreenState extends State { if (state is UserLoggedIn) { //PROFIILE BLOC return BlocBuilder( + buildWhen: (previous, current) { + if (state is EditEligibilityState) {} + return false; + }, builder: (context, state) { //EDIT ELIGIBILITY STATE if (state is EditEligibilityState) { - overseas = state.isOverseas; return ProgressHUD( child: Center( child: Padding( @@ -69,10 +74,10 @@ class _EditEligibilityScreenState extends State { children: [ //ELIGIBILITIES DROPDOWN FormBuilderDropdown( - onChanged: (Eligibility? eligibility){ + onChanged: (Eligibility? eligibility) { selectedEligibility = eligibility; }, - initialValue: state.currentEligibility, + initialValue: null, items: state.eligibilities .map>( (Eligibility eligibility) { @@ -117,11 +122,14 @@ class _EditEligibilityScreenState extends State { flex: 1, child: FormBuilderTextField( name: 'rating', - + // ignore: prefer_null_aware_operators - initialValue: widget - .eligibityCert.rating== null?null:widget.eligibityCert.rating.toString(), - + initialValue: + widget.eligibityCert.rating == null + ? null + : widget.eligibityCert.rating + .toString(), + decoration: normalTextFieldStyle( 'rating', 'rating'), ), @@ -190,143 +198,156 @@ class _EditEligibilityScreenState extends State { height: 12, ), //OVERSEAS ADDRESS SWITCH - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - EligibityCert newEligibility = - state.eligibityCert; - newEligibility.overseas = value!; - // formKey.currentState!.reset(); - context.read().add( - EditEligibility( - selectedRegion: null, - selectedProvince: null, - eligibityCert: newEligibility)); - }, - decoration: normalTextFieldStyle("", ''), - name: 'overseas', - title: const Text("Overseas Address?"), - ), - const SizedBox( - height: 20, - ), - //COUNTRY DROPDOWN - SizedBox( - child: overseas == true - ? FormBuilderDropdown( - initialValue: state.currentCountry, - - items: state.countries - .map>( + Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value; + }); + }, + decoration: normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Overseas Address?"), + ), + const SizedBox( + height: 20, + ), + //COUNTRY DROPDOWN + SizedBox( + child: overseas == true + ? FormBuilderDropdown( + initialValue: null, + items: state.countries.map< + DropdownMenuItem< + Country>>( (Country country) { - return DropdownMenuItem( - value: country, - child: FittedBox( - child: - Text(country.name!))); - }).toList(), - name: 'country', - decoration: normalTextFieldStyle( - "Country*", "Country"), - onChanged: (Country? value) { - selectedCountry = value; - }, - ) - : Column( - children: [ - //REGION DROPDOWN - FormBuilderDropdown( - onChanged: (Region? region) { - selectedRegion = region; - - context.read().add( - EditEligibility( - eligibityCert: - state.eligibityCert, - selectedProvince: null, - selectedRegion: - selectedRegion)); - }, - initialValue: state.currentRegion, - decoration: normalTextFieldStyle( - "Region*", "Region"), - name: 'region', - items: state.regions.map< - DropdownMenuItem>( - (Region region) { - return DropdownMenuItem( - value: region, - child: Text( - region.description!)); + return DropdownMenuItem< + Country>( + value: country, + child: FittedBox( + child: Text( + country.name!))); }).toList(), - ), - const SizedBox( - height: 20, - ), - //PROVINCE DROPDOWN - FormBuilderDropdown( - initialValue:state.currentProvince, - name: 'province', - onChanged: - (Province? province) { - selectedProvince = province; - context - .read() - .add(EditEligibility( - eligibityCert: state - .eligibityCert, - selectedProvince: - selectedProvince, - selectedRegion: state - .currentRegion)); - }, - items: state.provinces == null - ? [] - : state.provinces!.map< - DropdownMenuItem< - Province>>( - (Province province) { - return DropdownMenuItem( - value: province, - child: Text(province - .description!)); - }).toList(), - decoration: - normalTextFieldStyle( - "Province*", - "Province")), - const SizedBox( - height: 20, - ), - FormBuilderDropdown< - CityMunicipality>( - onChanged: (CityMunicipality? city){ - selectedMunicipality = city; + name: 'country', + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) { + selectedCountry = value; + }, + ) + : Column( + children: [ + //REGION DROPDOWN + FormBuilderDropdown( + onChanged: + (Region? region) async { + setState(() { + provinceCall = true; + }); + selectedRegion = region; + getProvinces(); + }, + initialValue: selectedRegion, + decoration: + normalTextFieldStyle( + "Region*", "Region"), + name: 'region', + items: state.regions.map< + DropdownMenuItem< + Region>>( + (Region region) { + return DropdownMenuItem< + Region>( + value: region, + child: Text(region + .description!)); + }).toList(), + ), + const SizedBox( + height: 20, + ), + //PROVINCE DROPDOWN + SizedBox( + height: 50, + child: ModalProgressHUD( + inAsyncCall: cityCall, + child: DropdownButtonFormField< + Province?>( + isExpanded: true, + value: selectedProvince, + onChanged: (Province? + province) { + setState(() { + cityCall = true; + }); + selectedProvince = province; + getCities(); + }, + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province + province) { + return DropdownMenuItem( + value: + province, + child: FittedBox( + child: Text( + province + .description!), + )); + }).toList(), + decoration: + normalTextFieldStyle( + "Province*", + "Province")), + ), + ), + const SizedBox( + height: 20, + ), + SizedBox( + height: 50, + child: DropdownButtonFormField< + CityMunicipality>( + onChanged: + (CityMunicipality? + city) { + selectedMunicipality = + city; }, - decoration: - normalTextFieldStyle( - "Municipality*", - "Municipality"), - name: 'municipality', - items: state.cityMuns == null - ? [] - : state.cityMuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality c) { - return DropdownMenuItem( - value: c, - child: Text(c - .description!)); - }).toList(), - initialValue: - state.currentCity), - const SizedBox( - height: 20, - ), - ], - )), + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality + c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), + ), + ), + const SizedBox( + height: 20, + ), + ], + )), + ], + ), + const Expanded( child: SizedBox(), ), @@ -357,4 +378,23 @@ class _EditEligibilityScreenState extends State { }, ); } + + Future getProvinces() async { + List _provinces = await LocationUtils.instance + .getProvinces(regionCode: selectedRegion!.code.toString()); + setState(() { + provinces = _provinces; + selectedProvince = provinces![0]; + getCities(); + provinceCall = false; + }); + } + Future getCities()async{ + List _cities = await LocationUtils.instance.getCities(code: selectedProvince!.code.toString()); + citymuns = _cities; + setState(() { + selectedMunicipality = _cities[0]; + cityCall = false; + }); + } } diff --git a/lib/screens/profile/components/eligibility_screen.dart b/lib/screens/profile/components/eligibility_screen.dart index 8544444..44d548d 100644 --- a/lib/screens/profile/components/eligibility_screen.dart +++ b/lib/screens/profile/components/eligibility_screen.dart @@ -29,7 +29,7 @@ class EligibiltyScreen extends StatelessWidget { centerTitle: true, backgroundColor: primary, actions: [AddLeading(onPressed: () { - context.read().add(const AddEligibility(overseas: false,selectedProvince: null,selectedRegion: null,selectedEligibility: null)); + context.read().add( AddEligibility()); })], ), body: BlocBuilder( @@ -172,8 +172,7 @@ class EligibiltyScreen extends StatelessWidget { .showWithText("Loading..."); context.read().add( EditEligibility( - selectedProvince: null, - selectedRegion: null, + eligibityCert: eligibityCert)); } diff --git a/lib/sevices/login_service/auth_service.dart b/lib/sevices/login_service/auth_service.dart index 885bd76..477abe8 100644 --- a/lib/sevices/login_service/auth_service.dart +++ b/lib/sevices/login_service/auth_service.dart @@ -20,10 +20,9 @@ class AuthService { 'X-User': "" }; try { - http.Response response = await http.get( - Uri.https('unitylb1.agusandelnorte.gov.ph', - '/unit2/api/sys/apk_version/latest/'), - headers: headers); + String path = Url.instance.latestApk(); + http.Response response = await Request.instance.getRequest(path: path,headers: headers,param: {}); + if (response.statusCode == 200) { Map data = jsonDecode(response.body); versionInfo = VersionInfo.fromJson(data['data']); diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index a954fda..d53cead 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -5,7 +5,8 @@ class Url { String host() { // return '192.168.10.221:3003'; // return 'agusandelnorte.gov.ph'; - return 'devweb.agusandelnorte.gov.ph'; + return "192.168.10.219:3000"; + // return 'devweb.agusandelnorte.gov.ph'; } String authentication() { @@ -16,6 +17,10 @@ class Url { return '/api/jobnet_app/profile/pds/'; } + String latestApk(){ + return "/api/system_app/apk_version/latest"; + } + String eligibilities(){ return "/api/jobnet_app/eligibilities/"; } diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index e71a16d..a124bbe 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,6 +6,10 @@ #include "generated_plugin_registrant.h" +#include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) modal_progress_hud_nsn_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "ModalProgressHudNsnPlugin"); + modal_progress_hud_nsn_plugin_register_with_registrar(modal_progress_hud_nsn_registrar); } diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 2e1de87..f6f1987 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + modal_progress_hud_nsn ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 63c4df1..4567379 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,12 +5,14 @@ import FlutterMacOS import Foundation +import modal_progress_hud_nsn import package_info_plus import path_provider_foundation import shared_preferences_foundation import sqflite func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + ModalProgressHudNsnPlugin.register(with: registry.registrar(forPlugin: "ModalProgressHudNsnPlugin")) FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) diff --git a/pubspec.lock b/pubspec.lock index b06be86..5132e7a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -517,6 +517,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.0" + modal_progress_hud_nsn: + dependency: "direct main" + description: + name: modal_progress_hud_nsn + sha256: "408b9bcce97567de94637de932260e50be48db1842edc761aeea61670e5ec30c" + url: "https://pub.dev" + source: hosted + version: "0.3.0" nested: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 85eecab..acba4dd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -69,6 +69,7 @@ dependencies: expandable_group: ^0.0.8 badges: ^3.0.2 app_popup_menu: ^1.0.0 + modal_progress_hud_nsn: ^0.3.0 dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 48de52b..c1aa7e0 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,9 +6,12 @@ #include "generated_plugin_registrant.h" +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { + ModalProgressHudNsnPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ModalProgressHudNsnPlugin")); PermissionHandlerWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 0e69e40..2446653 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + modal_progress_hud_nsn permission_handler_windows ) From 66adcf924fc1d30949ac668f8e7e2d1520fd9df4 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Mon, 27 Feb 2023 14:26:27 +0800 Subject: [PATCH 39/86] add eligibility implemented --- ios/Podfile.lock | 6 + lib/bloc/profile/profile_bloc.dart | 99 +++--- lib/bloc/profile/profile_event.dart | 47 +-- lib/bloc/profile/profile_state.dart | 9 + lib/main.dart | 3 + lib/model/location/city.dart | 6 +- lib/model/location/country.dart | 6 + lib/model/location/provinces.dart | 5 + lib/model/location/region.dart | 5 + lib/model/profile/eligibility.dart | 124 ++++--- lib/model/utils/eligibility.dart | 5 + .../components/eligibility/add_modal.dart | 252 ++++++++++----- .../components/eligibility_screen.dart | 304 ++++++++++-------- lib/sevices/profile/eligibility_services.dart | 74 ++++- lib/theme-data.dart/form-style.dart | 2 + lib/utils/alerts.dart | 7 +- lib/utils/urls.dart | 8 +- 17 files changed, 599 insertions(+), 363 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 8fa5b72..e6a0ac0 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -12,6 +12,8 @@ PODS: - FMDB (2.7.5): - FMDB/standard (= 2.7.5) - FMDB/standard (2.7.5) + - modal_progress_hud_nsn (0.0.1): + - Flutter - MTBBarcodeScanner (5.0.11) - package_info_plus (0.4.5): - Flutter @@ -34,6 +36,7 @@ DEPENDENCIES: - easy_app_installer (from `.symlinks/plugins/easy_app_installer/ios`) - Flutter (from `Flutter`) - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) + - modal_progress_hud_nsn (from `.symlinks/plugins/modal_progress_hud_nsn/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) @@ -56,6 +59,8 @@ EXTERNAL SOURCES: :path: Flutter fluttertoast: :path: ".symlinks/plugins/fluttertoast/ios" + modal_progress_hud_nsn: + :path: ".symlinks/plugins/modal_progress_hud_nsn/ios" package_info_plus: :path: ".symlinks/plugins/package_info_plus/ios" path_provider_foundation: @@ -73,6 +78,7 @@ SPEC CHECKSUMS: Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 fluttertoast: eb263d302cc92e04176c053d2385237e9f43fad0 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a + modal_progress_hud_nsn: f6fb744cd060653d66ed8f325360ef3650eb2fde MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852 diff --git a/lib/bloc/profile/profile_bloc.dart b/lib/bloc/profile/profile_bloc.dart index b7aace6..5db9328 100644 --- a/lib/bloc/profile/profile_bloc.dart +++ b/lib/bloc/profile/profile_bloc.dart @@ -1,39 +1,34 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; -import 'package:unit2/model/profile/basic_info.dart'; -import 'package:unit2/model/profile/basic_information/primary-information.dart'; import 'package:unit2/model/profile/eligibility.dart'; import 'package:unit2/model/profile/profileInfomation.dart'; import 'package:unit2/model/utils/eligibility.dart'; import 'package:unit2/sevices/profile/eligibility_services.dart'; import 'package:unit2/sevices/profile/profile_service.dart'; -import 'package:unit2/test_data.dart'; import 'package:unit2/utils/location_utilities.dart'; import 'package:unit2/utils/profile_utilities.dart'; import '../../model/location/country.dart'; import '../../model/location/region.dart'; import '../../model/location/provinces.dart'; import '../../model/location/city.dart'; -import '../../model/location/barangay.dart'; part 'profile_event.dart'; part 'profile_state.dart'; class ProfileBloc extends Bloc { ProfileBloc() : super(ProfileInitial()) { - ProfileInformation? _profileInformation; - List? _countries; - List? _regions; - List? _eligibilities; - List? _provinces; - List? _cities; + ProfileInformation? globalProfileInformation; + List? globalCountries; + List? globalRegions; + List? globalEligibilities; + List? eligibilities; ////========================================================================= on((event, emit) async { // try { emit(ProfileLoading()); ProfileInformation? profileInformation = await ProfileService.instance.getProfile(event.token, event.userID); - _profileInformation = profileInformation; - emit(ProfileLoaded(profileInformation: _profileInformation!)); + globalProfileInformation = profileInformation; + emit(ProfileLoaded(profileInformation: globalProfileInformation!)); // } catch (e) { // emit(ProfileErrorState(mesage: e.toString())); // } @@ -41,46 +36,34 @@ class ProfileBloc extends Bloc { ////===================================================================== on((event, emit) { emit(ProfileLoading()); + eligibilities = event.eligibilities; emit(EligibilityLoaded(eligibilities: event.eligibilities)); }); ////==================================================================== on((event, emit) async { - Region? currentRegion; - Province? currentProvince; - CityMunicipality? currentCity; // try{ emit(ProfileLoading()); - if (_countries == null) { + if (globalCountries == null) { List countries = await LocationUtils.instance.getCountries(); - _countries = countries; + globalCountries = countries; } - if (_regions == null) { + if (globalRegions == null) { List regions = await LocationUtils.instance.getRegions(); - _regions = regions; + globalRegions = regions; } - if (_eligibilities == null) { + if (globalEligibilities == null) { List eligibilities = await ProfileUtilities.instance.getEligibilities(); - _eligibilities = eligibilities; + globalEligibilities = eligibilities; } - -// get current country - Country? currentCountry = _countries!.firstWhere((Country country) => - country.code == event.eligibityCert.examAddress?.country!.code); -// get current eligibility - Eligibility currentEligibility = _eligibilities!.firstWhere( - (Eligibility eligibility) => - event.eligibityCert.eligibility!.id == eligibility.id); - bool? isOverseas = event.eligibityCert.overseas; - emit(EditEligibilityState( isOverseas: isOverseas!, eligibityCert: event.eligibityCert, - countries: _countries!, - regions: _regions!, - eligibilities: _eligibilities!)); + countries: globalCountries!, + regions: globalRegions!, + eligibilities: globalEligibilities!)); // }catch(e){ // emit(ProfileErrorState(mesage: e.toString())); @@ -88,29 +71,26 @@ class ProfileBloc extends Bloc { ////==================================================================== }); - on((event, emit) async { - List? cities; - List? provinces; - Eligibility? currentEligibility; + on((event, emit) async { emit(ProfileLoading()); - if (_regions == null) { + if (globalRegions == null) { List regions = await LocationUtils.instance.getRegions(); - _regions = regions; + globalRegions = regions; } - if (_eligibilities == null) { + if (globalEligibilities == null) { List eligibilities = await ProfileUtilities.instance.getEligibilities(); - _eligibilities = eligibilities; + globalEligibilities = eligibilities; } - if (_countries == null) { + if (globalCountries == null) { List countries = await LocationUtils.instance.getCountries(); - _countries = countries; + globalCountries = countries; } - + emit(AddEligibilityState( - eligibilities: _eligibilities!, - regions: _regions!, - countries: _countries!)); + eligibilities: globalEligibilities!, + regions: globalRegions!, + countries: globalCountries!)); }); ////==================================================================== on((event, emit) async { @@ -133,5 +113,28 @@ class ProfileBloc extends Bloc { emit(ProfileErrorState(mesage: e.toString())); } }); + on( + (event, emit) async { + try { + emit(ProfileLoading()); + Map status = await EligibilityService.instance.add( + eligibityCert: event.eligibityCert, + token: event.token, + profileId: int.parse(event.profileId)); + if (status['success']) { + EligibityCert? eligibityCert = + EligibityCert.fromJson(status['data']); + eligibilities!.add(eligibityCert); + emit(EligibilityAddedState( + eligibilities: eligibilities!, response: status)); + } else { + emit(EligibilityAddedState( + eligibilities: eligibilities!, response: status)); + } + } catch (e) { + emit(ProfileErrorState(mesage: e.toString())); + } + }, + ); } } diff --git a/lib/bloc/profile/profile_event.dart b/lib/bloc/profile/profile_event.dart index 11bd0d3..7d1b96e 100644 --- a/lib/bloc/profile/profile_event.dart +++ b/lib/bloc/profile/profile_event.dart @@ -7,46 +7,55 @@ abstract class ProfileEvent extends Equatable { List get props => []; } -class LoadProfile extends ProfileEvent{ -final String token; -final int userID; -const LoadProfile({required this.token, required this.userID}); - @override - List get props => [token,userID]; - +class LoadProfile extends ProfileEvent { + final String token; + final int userID; + const LoadProfile({required this.token, required this.userID}); + @override + List get props => [token, userID]; } -class LoadProfileInformation extends ProfileEvent{ +class LoadProfileInformation extends ProfileEvent { @override List get props => []; } -class LoadEligibility extends ProfileEvent{ +class LoadEligibility extends ProfileEvent { final List eligibilities; const LoadEligibility({required this.eligibilities}); - @override + @override List get props => []; } -class EditEligibility extends ProfileEvent{ +class EditEligibility extends ProfileEvent { final EligibityCert eligibityCert; const EditEligibility({required this.eligibityCert}); - @override + @override List get props => []; } -class DeleteEligibility extends ProfileEvent{ +class DeleteEligibility extends ProfileEvent { final List eligibilities; final String profileId; final int eligibilityId; final String token; -const DeleteEligibility({ required this.eligibilities, required this.eligibilityId, required this.profileId, required this.token}); - @override - List get props => [eligibilities,profileId,eligibilityId,token]; + const DeleteEligibility( + {required this.eligibilities, + required this.eligibilityId, + required this.profileId, + required this.token}); + @override + List get props => [eligibilities, profileId, eligibilityId, token]; } +class ShowAddEligibilityForm extends ProfileEvent { + +} class AddEligibility extends ProfileEvent{ - + final EligibityCert eligibityCert; + final String profileId; + final String token; + const AddEligibility({required this.eligibityCert, required this.profileId, required this.token}); + @override + List get props => [eligibityCert, profileId, token]; } - - diff --git a/lib/bloc/profile/profile_state.dart b/lib/bloc/profile/profile_state.dart index 52a2eb5..d84f750 100644 --- a/lib/bloc/profile/profile_state.dart +++ b/lib/bloc/profile/profile_state.dart @@ -69,3 +69,12 @@ class AddEligibilityState extends ProfileState { @override List get props => [eligibilities,countries,regions]; } + +class EligibilityAddedState extends ProfileState{ + final List eligibilities; + final Map response; + + const EligibilityAddedState({required this.eligibilities, required this.response}); + @override + List get props =>[eligibilities,response]; +} diff --git a/lib/main.dart b/lib/main.dart index d41d56d..d4dcacd 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,6 +4,7 @@ import 'package:device_preview/device_preview.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/app_router.dart'; import 'package:unit2/utils/global_context.dart'; import 'package:unit2/utils/global_context.dart'; @@ -55,8 +56,10 @@ class MyApp extends StatelessWidget { // routeInformationParser: goRouter.routeInformationParser, // routerDelegate: goRouter.routerDelegate, // routeInformationProvider: goRouter.routeInformationProvider, + title: 'uniT2 - Universal Tracker and Tracer', theme: ThemeData( + primarySwatch: Colors.red, appBarTheme: const AppBarTheme( systemOverlayStyle: SystemUiOverlayStyle( statusBarBrightness: Brightness.dark, diff --git a/lib/model/location/city.dart b/lib/model/location/city.dart index c9c449a..1fc77cd 100644 --- a/lib/model/location/city.dart +++ b/lib/model/location/city.dart @@ -38,10 +38,14 @@ class CityMunicipality { Map toJson() => { "code": code, "description": description, - "province": province!.toJson(), + "province": province?.toJson(), "psgc_code": psgcCode, "zipcode": zipcode, }; + @override + String toString(){ + return 'CityMunicipality{code:$code, description:$description, provice:${province.toString()} }'; + } } diff --git a/lib/model/location/country.dart b/lib/model/location/country.dart index e1136b1..fe54ce8 100644 --- a/lib/model/location/country.dart +++ b/lib/model/location/country.dart @@ -31,4 +31,10 @@ class Country { "name": name, "code": code, }; + + @override + String toString() { + return '$id $name $code '; + + } } diff --git a/lib/model/location/provinces.dart b/lib/model/location/provinces.dart index d14881d..597587d 100644 --- a/lib/model/location/provinces.dart +++ b/lib/model/location/provinces.dart @@ -41,4 +41,9 @@ class Province { "psgc_code": psgcCode, "shortname": shortname, }; + + @override + String toString(){ + return 'Province(name:$description ${region.toString()})'; + } } diff --git a/lib/model/location/region.dart b/lib/model/location/region.dart index 99a7c8d..17bd41d 100644 --- a/lib/model/location/region.dart +++ b/lib/model/location/region.dart @@ -31,4 +31,9 @@ class Region { "description": description, "psgc_code": psgcCode, }; + + @override + String toString(){ + return 'Region(name:$description)'; + } } diff --git a/lib/model/profile/eligibility.dart b/lib/model/profile/eligibility.dart index 5f7e7fd..84566da 100644 --- a/lib/model/profile/eligibility.dart +++ b/lib/model/profile/eligibility.dart @@ -12,90 +12,110 @@ import '../location/city.dart'; import '../location/country.dart'; import '../utils/eligibility.dart'; -EligibityCert eligibityFromJson(String str) => EligibityCert.fromJson(json.decode(str)); +EligibityCert eligibityFromJson(String str) => + EligibityCert.fromJson(json.decode(str)); String eligibityToJson(EligibityCert data) => json.encode(data.toJson()); class EligibityCert { - EligibityCert({ - required this.id, - required this.rating, - required this.examDate, - required this.attachments, - required this.eligibility, - required this.examAddress, - required this.validityDate, - required this.licenseNumber, - required this.overseas, - }); - bool? overseas; - final int? id; - final double? rating; - final DateTime? examDate; - final dynamic attachments; - final Eligibility? eligibility; - final ExamAddress? examAddress; - final DateTime? validityDate; - final String? licenseNumber; + EligibityCert({ + required this.id, + required this.rating, + required this.examDate, + required this.attachments, + required this.eligibility, + required this.examAddress, + required this.validityDate, + required this.licenseNumber, + required this.overseas, + }); + bool? overseas; + final int? id; + final double? rating; + final DateTime? examDate; + final dynamic attachments; + final Eligibility? eligibility; + final ExamAddress? examAddress; + final DateTime? validityDate; + final String? licenseNumber; - factory EligibityCert.fromJson(Map json) => EligibityCert( + factory EligibityCert.fromJson(Map json) => EligibityCert( id: json["id"], rating: json["rating"]?.toDouble(), - examDate: json['exam_date'] == null? null: DateTime.parse(json["exam_date"]), + examDate: json['exam_date'] == null + ? null + : DateTime.parse(json["exam_date"]), attachments: null, - eligibility: json['eligibility'] == null?null: Eligibility.fromJson(json["eligibility"]), - examAddress: json['exam_address'] == null? null: ExamAddress.fromJson(json["exam_address"]), + eligibility: json['eligibility'] == null + ? null + : Eligibility.fromJson(json["eligibility"]), + examAddress: json['exam_address'] == null + ? null + : ExamAddress.fromJson(json["exam_address"]), validityDate: json["validity_date"], licenseNumber: json["license_number"], overseas: null, - ); + ); - Map toJson() => { + Map toJson() => { "id": id, "rating": rating, - "exam_date": "${examDate!.year.toString().padLeft(4, '0')}-${examDate!.month.toString().padLeft(2, '0')}-${examDate!.day.toString().padLeft(2, '0')}", + "exam_date": + "${examDate!.year.toString().padLeft(4, '0')}-${examDate!.month.toString().padLeft(2, '0')}-${examDate!.day.toString().padLeft(2, '0')}", "attachments": attachments, "eligibility": eligibility!.toJson(), "exam_address": examAddress!.toJson(), "validity_date": validityDate, "license_number": licenseNumber, - }; + }; + @override + String toString() { + return 'eligibility:${eligibility.toString()}, rating:$rating, examDate:${examDate.toString()},validydate:${validityDate.toString()}, lisence:$licenseNumber, examAddress:${examAddress.toString()}'; + } } - class ExamAddress { - ExamAddress({ - required this.id, - required this.examAddressClass, - required this.country, - required this.barangay, - required this.addressCategory, - required this.cityMunicipality, - }); + ExamAddress({ + required this.id, + required this.examAddressClass, + required this.country, + required this.barangay, + required this.addressCategory, + required this.cityMunicipality, + }); - final int? id; - final dynamic examAddressClass; - final Country? country; - final dynamic barangay; - final AddressCategory? addressCategory; - final CityMunicipality? cityMunicipality; + final int? id; + final dynamic examAddressClass; + final Country? country; + final dynamic barangay; + final AddressCategory? addressCategory; + final CityMunicipality? cityMunicipality; - factory ExamAddress.fromJson(Map json) => ExamAddress( + factory ExamAddress.fromJson(Map json) => ExamAddress( id: json["id"], examAddressClass: json["class"], - country:json["country"] == null? null: Country.fromJson(json["country"]), + country: + json["country"] == null ? null : Country.fromJson(json["country"]), barangay: json["barangay"], - addressCategory: json["address_category"] == null?null: AddressCategory.fromJson(json["address_category"]), - cityMunicipality: json["city_municipality"]==null? null: CityMunicipality.fromJson(json["city_municipality"]), - ); + addressCategory: json["address_category"] == null + ? null + : AddressCategory.fromJson(json["address_category"]), + cityMunicipality: json["city_municipality"] == null + ? null + : CityMunicipality.fromJson(json["city_municipality"]), + ); - Map toJson() => { + Map toJson() => { "id": id, "class": examAddressClass, "country": country!.toJson(), "barangay": barangay, "address_category": addressCategory!.toJson(), "city_municipality": cityMunicipality!.toJson(), - }; -} + }; + @override + String toString() { + return 'country:${country.toString()} , address:${cityMunicipality.toString()}'; + } +} diff --git a/lib/model/utils/eligibility.dart b/lib/model/utils/eligibility.dart index c9b3a04..b2ec606 100644 --- a/lib/model/utils/eligibility.dart +++ b/lib/model/utils/eligibility.dart @@ -31,4 +31,9 @@ class Eligibility { "title": title, "type": type, }; + @override + String toString() { +return title; + + } } diff --git a/lib/screens/profile/components/eligibility/add_modal.dart b/lib/screens/profile/components/eligibility/add_modal.dart index 7a79eea..f0b247c 100644 --- a/lib/screens/profile/components/eligibility/add_modal.dart +++ b/lib/screens/profile/components/eligibility/add_modal.dart @@ -3,10 +3,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:intl/intl.dart'; import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; +import 'package:unit2/model/profile/eligibility.dart'; import '../../../../model/location/city.dart'; import '../../../../model/location/country.dart'; @@ -28,7 +30,7 @@ class AddEligibilityScreen extends StatefulWidget { } class _AddEligibilityScreenState extends State { - final formKey = GlobalKey(); + final formKey = GlobalKey(); final provinceKey = GlobalKey(); bool? overseas; DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); @@ -41,6 +43,12 @@ class _AddEligibilityScreenState extends State { List? citymuns; bool provinceCall = false; bool cityCall = false; + final examDateController = TextEditingController(); + final validityDateController = TextEditingController(); + String? token; + String? profileId; + String? rating; + String? license; @override Widget build(BuildContext context) { //USERBLOC @@ -49,6 +57,8 @@ class _AddEligibilityScreenState extends State { //LOGGED IN USER STATE if (state is UserLoggedIn) { //PROFIILE BLOC + token = state.userData!.user!.login!.token; + profileId = state.userData!.user!.login!.user!.profileId.toString(); return BlocBuilder( buildWhen: (previous, current) { if (state is EditEligibilityState) {} @@ -70,27 +80,23 @@ class _AddEligibilityScreenState extends State { children: [ //ELIGIBILITIES DROPDOWN FormBuilderDropdown( - onChanged: (Eligibility? eligibility) { - selectedEligibility = eligibility; - }, - initialValue: state.eligibilities[0], - items: state.eligibilities - .map>( - (Eligibility eligibility) { - return DropdownMenuItem( - value: eligibility, - child: Text(eligibility.title)); - }).toList(), - name: "eligibility", - decoration: - normalTextFieldStyle("Eligibility", "") - .copyWith( - hintStyle: const TextStyle( - color: Colors.black, - ), - labelStyle: const TextStyle( - color: Colors.black)), - ), + onChanged: (Eligibility? eligibility) { + selectedEligibility = eligibility; + }, + autovalidateMode: + AutovalidateMode.onUserInteraction, + validator: (value) => + value == null ? 'required' : null, + items: state.eligibilities + .map>( + (Eligibility eligibility) { + return DropdownMenuItem( + value: eligibility, + child: Text(eligibility.title)); + }).toList(), + name: "eligibility", + decoration: normalTextFieldStyle( + "Eligibility", "Eligibility")), const SizedBox( height: 20, ), @@ -103,9 +109,10 @@ class _AddEligibilityScreenState extends State { Flexible( flex: 1, child: FormBuilderTextField( + onChanged: (value) { + license = value; + }, name: 'license_number', - initialValue:null, - decoration: normalTextFieldStyle( "license number", "license number"), ), @@ -117,14 +124,12 @@ class _AddEligibilityScreenState extends State { Flexible( flex: 1, child: FormBuilderTextField( + onChanged: (value) { + rating = value; + }, name: 'rating', - - // ignore: prefer_null_aware_operators - initialValue:null, - - decoration: normalTextFieldStyle( - 'rating', 'rating'), + 'rating %', 'rating'), ), ), ], @@ -141,12 +146,21 @@ class _AddEligibilityScreenState extends State { Flexible( flex: 1, child: DateTimePicker( - // controller: examDateController, - firstDate: DateTime(2000), - lastDate: DateTime(2100), + use24HourFormat: false, + icon: const Icon(Icons.date_range), + controller: examDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + timeHintText: + "Date of Examination/Conferment", decoration: normalTextFieldStyle( - "Exam date", "Exam date"), - initialValue:null, + "Exam date", "") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + initialValue: null, )), const SizedBox( width: 12, @@ -155,12 +169,18 @@ class _AddEligibilityScreenState extends State { Flexible( flex: 1, child: DateTimePicker( - // controller: validityDateController, - firstDate: DateTime(2000), + controller: validityDateController, + firstDate: DateTime(1970), lastDate: DateTime(2100), decoration: normalTextFieldStyle( - "Validity date", "Validity date"), - initialValue:null, + "Validity date", + "Validity date") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + initialValue: null, ), ), ], @@ -170,7 +190,7 @@ class _AddEligibilityScreenState extends State { height: 20, ), Text( - "Placement of Examination/Confinement", + "Placement of Examination/Conferment", style: Theme.of(context) .textTheme .displaySmall! @@ -202,6 +222,8 @@ class _AddEligibilityScreenState extends State { child: overseas == true ? FormBuilderDropdown( initialValue: null, + validator: (value) => + value == null ? 'required' : null, items: state.countries.map< DropdownMenuItem< Country>>( @@ -224,6 +246,11 @@ class _AddEligibilityScreenState extends State { children: [ //REGION DROPDOWN FormBuilderDropdown( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null ? 'required' : null, onChanged: (Region? region) async { setState(() { @@ -253,19 +280,26 @@ class _AddEligibilityScreenState extends State { ), //PROVINCE DROPDOWN SizedBox( - height: 50, + height: 70, child: ModalProgressHUD( + color: Colors.transparent, inAsyncCall: cityCall, child: DropdownButtonFormField< Province?>( - isExpanded: true, + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null ? 'required' : null, + isExpanded: true, value: selectedProvince, onChanged: (Province? province) { setState(() { - cityCall = true; + cityCall = true; }); - selectedProvince = province; + selectedProvince = + province; getCities(); }, items: provinces == null @@ -278,7 +312,8 @@ class _AddEligibilityScreenState extends State { return DropdownMenuItem( value: province, - child: FittedBox( + child: + FittedBox( child: Text( province .description!), @@ -294,33 +329,37 @@ class _AddEligibilityScreenState extends State { height: 20, ), SizedBox( - height: 50, - child: DropdownButtonFormField< + height: 70, + child: + DropdownButtonFormField< CityMunicipality>( - onChanged: - (CityMunicipality? - city) { - selectedMunicipality = - city; - }, - decoration: - normalTextFieldStyle( - "Municipality*", - "Municipality"), - value: selectedMunicipality, - items: citymuns == null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality - c) { - return DropdownMenuItem( - value: c, - child: Text(c - .description!)); - }).toList(), - ), + validator: (value) => + value == null ? 'required' : null, + isExpanded: true, + onChanged: + (CityMunicipality? + city) { + selectedMunicipality = + city; + }, + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality + c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), + ), ), const SizedBox( height: 20, @@ -340,7 +379,56 @@ class _AddEligibilityScreenState extends State { child: ElevatedButton( style: mainBtnStyle( primary, Colors.transparent, second), - onPressed: () {}, + onPressed: () { + double? rate = rating == null + ? null + : double.parse(rating!); + String? licenseNumber = license; + CityMunicipality? cityMunicipality = + selectedMunicipality; + DateTime? examDate = + examDateController.text.isEmpty + ? null + : DateTime.parse( + examDateController.text); + DateTime? validityDate = + validityDateController.text.isEmpty + ? null + : DateTime.parse( + validityDateController.text); + + ExamAddress examAddress = ExamAddress( + barangay: null, + id: null, + addressCategory: null, + examAddressClass: null, + country: selectedCountry ?? + Country( + id: 175, + name: 'Philippines', + code: 'PH'), + cityMunicipality: cityMunicipality); + EligibityCert eligibityCert = + EligibityCert( + id: null, + rating: rate, + examDate: examDate, + attachments: null, + eligibility: selectedEligibility, + examAddress: examAddress, + validityDate: validityDate, + licenseNumber: licenseNumber, + overseas: overseas); + if (formKey.currentState! + .saveAndValidate()) { + context.read().add( + AddEligibility( + eligibityCert: eligibityCert, + profileId: profileId!, + token: token!)); + } + // context.read().add(AddEligibility(eligibityCert: eligibityCert, profileId: profileId, token: token)) + }, child: const Text(submit)), ), const SizedBox( @@ -362,21 +450,23 @@ class _AddEligibilityScreenState extends State { } Future getProvinces() async { - List _provinces = await LocationUtils.instance + List newProvinces = await LocationUtils.instance .getProvinces(regionCode: selectedRegion!.code.toString()); setState(() { - provinces = _provinces; + provinces = newProvinces; selectedProvince = provinces![0]; getCities(); provinceCall = false; }); } - Future getCities()async{ - List _cities = await LocationUtils.instance.getCities(code: selectedProvince!.code.toString()); - citymuns = _cities; - setState(() { - selectedMunicipality = _cities[0]; - cityCall = false; - }); + + Future getCities() async { + List newCities = await LocationUtils.instance + .getCities(code: selectedProvince!.code.toString()); + citymuns = newCities; + setState(() { + selectedMunicipality = newCities[0]; + cityCall = false; + }); } } diff --git a/lib/screens/profile/components/eligibility_screen.dart b/lib/screens/profile/components/eligibility_screen.dart index 44d548d..ba937eb 100644 --- a/lib/screens/profile/components/eligibility_screen.dart +++ b/lib/screens/profile/components/eligibility_screen.dart @@ -13,6 +13,7 @@ import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/add_leading.dart'; +import 'package:unit2/widgets/empty_data.dart'; import '../../../utils/alerts.dart'; @@ -28,9 +29,11 @@ class EligibiltyScreen extends StatelessWidget { title: const Text(elibilityScreenTitle), centerTitle: true, backgroundColor: primary, - actions: [AddLeading(onPressed: () { - context.read().add( AddEligibility()); - })], + actions: [ + AddLeading(onPressed: () { + context.read().add(ShowAddEligibilityForm()); + }) + ], ), body: BlocBuilder( builder: (context, state) { @@ -49,12 +52,11 @@ class EligibiltyScreen extends StatelessWidget { final progress = ProgressHUD.of(context); progress!.showWithText("Loading"); } - if (state is EligibilityLoaded) { + if (state is EligibilityLoaded || + state is AddEligibilityState || + state is ProfileErrorState) { final progress = ProgressHUD.of(context); progress!.dismiss(); - }if(state is AddEligibilityState){ - final progress = ProgressHUD.of(context); - progress!.dismiss(); } if (state is DeletedState) { if (state.success) { @@ -66,158 +68,180 @@ class EligibiltyScreen extends StatelessWidget { } else { errorAlert(context, "Deletion Failed", "Error deleting eligibility", () { + Navigator.of(context).pop(); + context.read().add(LoadEligibility( + eligibilities: state.eligibilities)); + }); + } + } + if (state is EligibilityAddedState) { + if (state.response['success']) { + Navigator.of(context).pop(); + successAlert(context, "Adding Successfull!", + state.response['message'], () { + context.read().add(LoadEligibility( + eligibilities: state.eligibilities)); + }); + } else { + errorAlert(context, "Adding Failed", + "Something went wrong. Please try again.", () { + Navigator.of(context).pop(); context.read().add(LoadEligibility( eligibilities: state.eligibilities)); }); } } - // TODO: implement listener }, builder: (context, state) { return BlocBuilder( builder: (context, state) { if (state is EligibilityLoaded) { - return ListView.builder( - padding: const EdgeInsets.symmetric( - vertical: 8, horizontal: 10), - itemCount: state.eligibilities.length, - itemBuilder: (BuildContext context, int index) { - String title = state - .eligibilities[index].eligibility!.title; - return Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: screenWidth, - padding: const EdgeInsets.symmetric( - horizontal: 12, vertical: 8), - decoration: box1(), - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - title, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith( - fontWeight: - FontWeight - .w500), - ), - const Divider(), - const SizedBox( - height: 5, - ), - Text( - "$licenseNumber: ${state.eligibilities[index].licenseNumber == null ? 'N/A' : state.eligibilities[index].licenseNumber.toString()}", + if (state.eligibilities.isNotEmpty) { + return ListView.builder( + padding: const EdgeInsets.symmetric( + vertical: 8, horizontal: 10), + itemCount: state.eligibilities.length, + itemBuilder: (BuildContext context, int index) { + String title = state + .eligibilities[index].eligibility!.title; + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Container( + width: screenWidth, + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + decoration: box1(), + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + title, style: Theme.of(context) .textTheme - .titleSmall), - const SizedBox( - height: 3, - ), - Text( - "Rating : ${state.eligibilities[index].rating ?? 'N/A'}.", - style: Theme.of(context) - .textTheme - .titleSmall) - ]), - ), - AppPopupMenu( - offset: const Offset(-10, -10), - elevation: 3, - onSelected: (value) { - if (value == 2) { - confirmAlert(context, () { - BlocProvider.of< - ProfileBloc>(context) - .add(DeleteEligibility( - eligibilities: state - .eligibilities, - eligibilityId: state - .eligibilities[ - index] - .id!, - profileId: profileId!, - token: token!)); - }, "Delete?", - "Confirm Delete?"); - } - if (value == 1) { - EligibityCert eligibityCert = - state.eligibilities[index]; - bool overseas = eligibityCert - .examAddress! - .country! - .id - .toString() == - '175' - ? false - : true; - eligibityCert.overseas = - overseas; - final progress = - ProgressHUD.of(context); - eligibityCert.overseas = - overseas; - progress! - .showWithText("Loading..."); - context.read().add( - EditEligibility( - - eligibityCert: - eligibityCert)); - } - }, - menuItems: [ - popMenuItem( - text: "Edit", - value: 1, - icon: Icons.edit), - popMenuItem( - text: "Delete", - value: 2, - icon: Icons.delete), - popMenuItem( - text: "Attachment", - value: 3, - icon: FontAwesome.attach) - ], - icon: const Icon( - Icons.more_vert, - color: Colors.grey, + .titleMedium! + .copyWith( + fontWeight: + FontWeight + .w500), + ), + const Divider(), + const SizedBox( + height: 5, + ), + Text( + "$licenseNumber: ${state.eligibilities[index].licenseNumber == null ? 'N/A' : state.eligibilities[index].licenseNumber.toString()}", + style: Theme.of(context) + .textTheme + .titleSmall), + const SizedBox( + height: 3, + ), + Text( + "Rating : ${state.eligibilities[index].rating ?? 'N/A'}.", + style: Theme.of(context) + .textTheme + .titleSmall) + ]), ), - tooltip: "Options", - ) - ], + AppPopupMenu( + offset: const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + if (value == 2) { + confirmAlert(context, () { + BlocProvider.of< + ProfileBloc>( + context) + .add(DeleteEligibility( + eligibilities: state + .eligibilities, + eligibilityId: state + .eligibilities[ + index] + .id!, + profileId: + profileId!, + token: token!)); + }, "Delete?", + "Confirm Delete?"); + } + if (value == 1) { + EligibityCert eligibityCert = + state + .eligibilities[index]; + bool overseas = eligibityCert + .examAddress! + .country! + .id + .toString() == + '175' + ? false + : true; + eligibityCert.overseas = + overseas; + final progress = + ProgressHUD.of(context); + eligibityCert.overseas = + overseas; + progress!.showWithText( + "Loading..."); + context + .read() + .add(EditEligibility( + eligibityCert: + eligibityCert)); + } + }, + menuItems: [ + popMenuItem( + text: "Edit", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Delete", + value: 2, + icon: Icons.delete), + popMenuItem( + text: "Attachment", + value: 3, + icon: FontAwesome.attach) + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ) + ], + ), ), - ), - const SizedBox( - height: 5, - ) - ], - ); - }); + const SizedBox( + height: 5, + ) + ], + ); + }); + } else { + return const EmptyData( + message: + "You don't have any eligibilities added. Please click + to add"); + } } if (state is EditEligibilityState) { return EditEligibilityScreen( eligibityCert: state.eligibityCert); - }if(state is AddEligibilityState){ - return const AddEligibilityScreen(); } - if (state is DeletedState) { - return Center( - child: Container( - child: Text(state.success.toString())), - ); + if (state is AddEligibilityState) { + return const AddEligibilityScreen(); } if (state is ProfileErrorState) { return Center( diff --git a/lib/sevices/profile/eligibility_services.dart b/lib/sevices/profile/eligibility_services.dart index 0f3cbda..fa24736 100644 --- a/lib/sevices/profile/eligibility_services.dart +++ b/lib/sevices/profile/eligibility_services.dart @@ -1,35 +1,77 @@ import 'dart:convert'; +import 'package:unit2/model/profile/eligibility.dart'; import 'package:unit2/utils/request.dart'; import 'package:unit2/utils/urls.dart'; import 'package:http/http.dart' as http; -class EligibilityService{ + +class EligibilityService { static final EligibilityService _instance = EligibilityService(); static EligibilityService get instance => _instance; - Future delete({required int eligibilityId, required int profileId,required String token})async{ - bool? success; - String Authtoken = "Token $token"; - String path = "${Url.instance.deleteEligibility()}$profileId/"; - Map body = { "eligibility_id": eligibilityId}; - Map params ={"force_mode":"true"}; - Map headers = { + Future delete( + {required int eligibilityId, + required int profileId, + required String token}) async { + bool? success; + String authtoken = "Token $token"; + String path = "${Url.instance.deleteEligibility()}$profileId/"; + Map body = {"eligibility_id": eligibilityId}; + Map params = {"force_mode": "true"}; + Map headers = { 'Content-Type': 'application/json; charset=UTF-8', - 'Authorization': "Token $token" + 'Authorization': authtoken }; // try{ - http.Response response = await Request.instance.deleteRequest(path: path, headers: headers, body: body, param: params); - if(response.statusCode == 200){ - Map data = jsonDecode(response.body); + http.Response response = await Request.instance + .deleteRequest(path: path, headers: headers, body: body, param: params); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); success = data['success']; - }else{ + } else { success = false; - } - + } + // }catch(e){ // throw(e.toString()); // } return success!; } -} \ No newline at end of file + Future> add( + {required EligibityCert eligibityCert, + required String token, + required int profileId}) async { + Map? _response={}; + String authtoken = "Token $token+1"; + String path = '${Url.instance.addEligibility()}$profileId/'; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authtoken + }; + + Map body = { + 'eligibility_id': eligibityCert.eligibility!.id, + 'license_number': eligibityCert.licenseNumber, + 'exam_date': eligibityCert.examDate?.toString(), + 'validity_date': eligibityCert.validityDate?.toString(), + 'rating': eligibityCert.rating, + '_citymunCode': eligibityCert.examAddress?.cityMunicipality?.code, + '_countryId': eligibityCert.examAddress?.country!.id + }; + try { + http.Response response = await Request.instance + .postRequest(path: path, body: body, headers: headers, param: {}); + if (response.statusCode == 201) { + Map data = jsonDecode(response.body); + _response = data; + } else { + _response.addAll({'success':false}); + } + + return _response; + } catch (e) { + throw e.toString(); + } + } +} diff --git a/lib/theme-data.dart/form-style.dart b/lib/theme-data.dart/form-style.dart index 7089e98..22d1e79 100644 --- a/lib/theme-data.dart/form-style.dart +++ b/lib/theme-data.dart/form-style.dart @@ -49,6 +49,8 @@ InputDecoration normalTextFieldStyle(String labelText, String hintText) { filled: false); } + + InputDecoration loginTextFieldStyle() { return InputDecoration( floatingLabelBehavior: FloatingLabelBehavior.never, diff --git a/lib/utils/alerts.dart b/lib/utils/alerts.dart index 5257be1..0994f39 100644 --- a/lib/utils/alerts.dart +++ b/lib/utils/alerts.dart @@ -1,5 +1,6 @@ import 'package:awesome_dialog/awesome_dialog.dart'; import 'package:flutter/material.dart'; +import 'package:unit2/theme-data.dart/btn-style.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; @@ -45,8 +46,7 @@ errorAlert(context, title, description,Function() func) { headerAnimationLoop: false, title: title, desc: description, - btnOkOnPress: func, - btnOkColor: Colors.red, + btnOk: SizedBox(height: 50,child: ElevatedButton(onPressed:func, style: mainBtnStyle(primary, Colors.transparent, second), child: const Text("OK")), ) ).show(); } successAlert(context, title, description,Function() func) { @@ -58,7 +58,6 @@ successAlert(context, title, description,Function() func) { headerAnimationLoop: false, title: title, desc: description, - btnOkOnPress: func, - btnOkColor: Colors.red, + btnOk: SizedBox(height: 50,child: ElevatedButton(style: mainBtnStyle(primary, Colors.transparent, second), onPressed: func, child: const Text("OK")), ) ).show(); } diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index d53cead..953752f 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -5,8 +5,8 @@ class Url { String host() { // return '192.168.10.221:3003'; // return 'agusandelnorte.gov.ph'; - return "192.168.10.219:3000"; - // return 'devweb.agusandelnorte.gov.ph'; + // return "192.168.10.219:3000"; + return 'devweb.agusandelnorte.gov.ph'; } String authentication() { @@ -24,6 +24,10 @@ class Url { String eligibilities(){ return "/api/jobnet_app/eligibilities/"; } + +String addEligibility(){ + return "/api/jobnet_app/profile/pds/eligibility/"; +} String deleteEligibility(){ return "/api/jobnet_app/profile/pds/eligibility/"; } From 5dcc1c1efba1e83ae1481bfb30404fdcafa0a27c Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Thu, 2 Mar 2023 08:40:47 +0800 Subject: [PATCH 40/86] add and edit eligibility with individual pds info API --- lib/bloc/profile/profile_bloc.dart | 165 ++++-- lib/bloc/profile/profile_event.dart | 27 +- lib/bloc/profile/profile_state.dart | 21 + lib/model/profile/eligibility.dart | 6 +- lib/model/profile/profileInfomation.dart | 20 +- .../basic_information/address_screen.dart | 2 +- .../contact_information_screen.dart | 2 +- .../identification_information_screen.dart | 2 +- .../profile/components/education_screen.dart | 2 +- .../components/eligibility/add_modal.dart | 137 +++-- .../components/eligibility/edit_modal.dart | 490 ++++++++++------- .../components/eligibility_screen.dart | 509 ++++++++++-------- .../learning_and_development_screen.dart | 2 +- .../non_academic_recognition_screen.dart | 2 +- .../org_membership_screen.dart | 2 +- .../skills_and_hobbies_screen.dart | 2 +- .../profile/components/references_screen.dart | 2 +- .../components/voluntary_works_screen.dart | 2 +- .../components/work_history_screen.dart | 2 +- lib/screens/profile/profile.dart | 137 ++--- lib/screens/unit2/login/login.dart | 27 +- lib/sevices/profile/eligibility_services.dart | 76 ++- lib/sevices/profile/profile_service.dart | 182 ++++--- lib/utils/alerts.dart | 2 +- lib/utils/request.dart | 102 ++-- lib/utils/urls.dart | 17 +- lib/widgets/{ => Leadings}/add_leading.dart | 0 lib/widgets/Leadings/close_leading.dart | 13 + macos/Podfile.lock | 6 + 29 files changed, 1216 insertions(+), 743 deletions(-) rename lib/widgets/{ => Leadings}/add_leading.dart (100%) create mode 100644 lib/widgets/Leadings/close_leading.dart diff --git a/lib/bloc/profile/profile_bloc.dart b/lib/bloc/profile/profile_bloc.dart index 5db9328..d6c2c50 100644 --- a/lib/bloc/profile/profile_bloc.dart +++ b/lib/bloc/profile/profile_bloc.dart @@ -23,15 +23,20 @@ class ProfileBloc extends Bloc { List? eligibilities; ////========================================================================= on((event, emit) async { - // try { - emit(ProfileLoading()); - ProfileInformation? profileInformation = - await ProfileService.instance.getProfile(event.token, event.userID); - globalProfileInformation = profileInformation; - emit(ProfileLoaded(profileInformation: globalProfileInformation!)); - // } catch (e) { - // emit(ProfileErrorState(mesage: e.toString())); - // } + try { + emit(ProfileLoading()); + ProfileInformation? profileInformation = + await ProfileService.instance.getProfile(event.token, event.userID); + globalProfileInformation = profileInformation; + emit(ProfileLoaded(profileInformation: globalProfileInformation!)); + } catch (e) { + emit(ProfileErrorState(mesage: e.toString())); + } + }); + + on((event, emit) { + emit(const ProfileErrorState( + mesage: "Something went wrong. Please try again")); }); ////===================================================================== on((event, emit) { @@ -40,36 +45,121 @@ class ProfileBloc extends Bloc { emit(EligibilityLoaded(eligibilities: event.eligibilities)); }); ////==================================================================== - on((event, emit) async { - // try{ - emit(ProfileLoading()); - if (globalCountries == null) { - List countries = await LocationUtils.instance.getCountries(); - globalCountries = countries; - } - if (globalRegions == null) { - List regions = await LocationUtils.instance.getRegions(); - globalRegions = regions; - } - if (globalEligibilities == null) { - List eligibilities = - await ProfileUtilities.instance.getEligibilities(); - globalEligibilities = eligibilities; - } - bool? isOverseas = event.eligibityCert.overseas; +on((event,emit)async{ - emit(EditEligibilityState( - isOverseas: isOverseas!, - eligibityCert: event.eligibityCert, - countries: globalCountries!, - regions: globalRegions!, - eligibilities: globalEligibilities!)); + print(eligibilities?.length); + try{ + if(eligibilities != null){ + emit(EligibilityLoaded(eligibilities: eligibilities!)); + }else{ + emit(ProfileLoading()); + eligibilities = await EligibilityService.instance.getEligibilities(event.profileId, event.token); + emit(EligibilityLoaded(eligibilities: eligibilities!)); + } + + }catch(e){ + emit(ProfileErrorState(mesage: e.toString())); + } +}); +////==================================================================== + on((event, emit) async { + try { + emit(ProfileLoading()); + if (globalCountries == null) { + List countries = await LocationUtils.instance.getCountries(); + globalCountries = countries; + } + if (globalRegions == null) { + List regions = await LocationUtils.instance.getRegions(); + globalRegions = regions; + } + if (globalEligibilities == null) { + List eligibilities = + await ProfileUtilities.instance.getEligibilities(); + globalEligibilities = eligibilities; + } + Eligibility currentEligibility = globalEligibilities!.firstWhere( + (Eligibility eligibility) => + event.eligibityCert.eligibility!.id == eligibility.id); + bool? isOverseas = event.eligibityCert.overseas; + Country currentCountry = globalCountries!.firstWhere( + (Country country) => + event.eligibityCert.examAddress!.country!.code == country.code); + if (event.eligibityCert.examAddress?.cityMunicipality?.province + ?.region != + null) { + Region currrentRegion = globalRegions!.firstWhere((Region region) => + event.eligibityCert.examAddress!.cityMunicipality!.province! + .region!.code == + region.code); + List provinces = await LocationUtils.instance + .getProvinces(regionCode: currrentRegion.code.toString()); + Province currentProvince = provinces.firstWhere((Province province) => + event.eligibityCert.examAddress!.cityMunicipality!.province! + .code == + province.code); + List cities = await LocationUtils.instance + .getCities(code: currentProvince.code.toString()); + CityMunicipality currentCity = cities.firstWhere( + (CityMunicipality cityMunicipality) => + event.eligibityCert.examAddress!.cityMunicipality!.code == + cityMunicipality.code); - // }catch(e){ - // emit(ProfileErrorState(mesage: e.toString())); - // } - - ////==================================================================== + emit(EditEligibilityState( + currentCity: currentCity, + selectedCountry: currentCountry, + currentProvince: currentProvince, + currentRegion: currrentRegion, + currentEligibility: currentEligibility, + provinces: provinces, + cities: cities, + isOverseas: isOverseas!, + eligibityCert: event.eligibityCert, + countries: globalCountries!, + regions: globalRegions!, + eligibilities: globalEligibilities!)); + } else { + emit(EditEligibilityState( + selectedCountry: currentCountry, + currentCity: null, + currentProvince: null, + currentRegion: null, + provinces: null, + cities: null, + currentEligibility: currentEligibility, + isOverseas: isOverseas!, + eligibityCert: event.eligibityCert, + countries: globalCountries!, + regions: globalRegions!, + eligibilities: globalEligibilities!)); + } + } catch (e) { + emit(ProfileErrorState(mesage: e.toString())); + } + }); + ////==================================================================== + on((event, emit) async { + try { + emit(ProfileLoading()); + Map status = await EligibilityService.instance.update( + eligibityCert: event.eligibityCert, + token: event.token, + profileId: int.parse(event.profileId), + oldEligibility: event.oldEligibility); + if (status['success']) { + EligibityCert newEligibility = EligibityCert.fromJson(status['data']); + eligibilities!.removeWhere( + (EligibityCert element) => element.id == event.eligibityCert.id); + eligibilities!.add(newEligibility); + emit(EligibilityEditedState( + eligibilities: eligibilities!, response: status)); + } else { + emit(EligibilityEditedState( + eligibilities: eligibilities!, response: status)); + } + } catch (e) { + emit(ProfileErrorState(mesage: e.toString())); + } }); on((event, emit) async { emit(ProfileLoading()); @@ -113,6 +203,7 @@ class ProfileBloc extends Bloc { emit(ProfileErrorState(mesage: e.toString())); } }); + ////==================================================================== on( (event, emit) async { try { diff --git a/lib/bloc/profile/profile_event.dart b/lib/bloc/profile/profile_event.dart index 7d1b96e..5951ff8 100644 --- a/lib/bloc/profile/profile_event.dart +++ b/lib/bloc/profile/profile_event.dart @@ -27,9 +27,9 @@ class LoadEligibility extends ProfileEvent { List get props => []; } -class EditEligibility extends ProfileEvent { +class ShowEditEligibilityForm extends ProfileEvent { final EligibityCert eligibityCert; - const EditEligibility({required this.eligibityCert}); + const ShowEditEligibilityForm({required this.eligibityCert}); @override List get props => []; } @@ -51,6 +51,15 @@ class DeleteEligibility extends ProfileEvent { class ShowAddEligibilityForm extends ProfileEvent { } + +class GetEligibilities extends ProfileEvent{ + final int profileId; + final String token; + const GetEligibilities({required this.profileId, required this.token}); + @override + List get props => [profileId,token]; +} + class AddEligibility extends ProfileEvent{ final EligibityCert eligibityCert; final String profileId; @@ -59,3 +68,17 @@ class AddEligibility extends ProfileEvent{ @override List get props => [eligibityCert, profileId, token]; } +class UpdateEligibility extends ProfileEvent{ + final EligibityCert eligibityCert; + final String profileId; + final String token; + final int oldEligibility; + const UpdateEligibility({required this.eligibityCert, required this.oldEligibility,required this.profileId, required this.token}); + + @override + List get props =>[eligibityCert,profileId,token,oldEligibility]; +} + +class CallErrorState extends ProfileEvent{ + +} diff --git a/lib/bloc/profile/profile_state.dart b/lib/bloc/profile/profile_state.dart index d84f750..a311398 100644 --- a/lib/bloc/profile/profile_state.dart +++ b/lib/bloc/profile/profile_state.dart @@ -36,13 +36,27 @@ class EditEligibilityState extends ProfileState { final List eligibilities; final List countries; final List regions; + final List? provinces; + final List? cities; final bool isOverseas; + final Eligibility currentEligibility; + final Region? currentRegion; + final Province? currentProvince; + final CityMunicipality? currentCity; + final Country selectedCountry; const EditEligibilityState({ + required this.provinces, + required this.cities, + required this.currentProvince, + required this.currentCity, + required this.currentRegion, + required this.currentEligibility, required this.isOverseas, required this.eligibityCert, required this.eligibilities, required this.countries, required this.regions, + required this.selectedCountry, }); @override List get props => @@ -69,6 +83,13 @@ class AddEligibilityState extends ProfileState { @override List get props => [eligibilities,countries,regions]; } +class EligibilityEditedState extends ProfileState{ + final List eligibilities; + final Map response; + const EligibilityEditedState({required this.eligibilities, required this.response}); + @override + List get props =>[eligibilities, response]; +} class EligibilityAddedState extends ProfileState{ final List eligibilities; diff --git a/lib/model/profile/eligibility.dart b/lib/model/profile/eligibility.dart index 84566da..f57007d 100644 --- a/lib/model/profile/eligibility.dart +++ b/lib/model/profile/eligibility.dart @@ -52,7 +52,9 @@ class EligibityCert { examAddress: json['exam_address'] == null ? null : ExamAddress.fromJson(json["exam_address"]), - validityDate: json["validity_date"], + validityDate: json['validity_date'] == null + ? null + : DateTime.parse(json["validity_date"]), licenseNumber: json["license_number"], overseas: null, ); @@ -65,7 +67,7 @@ class EligibityCert { "attachments": attachments, "eligibility": eligibility!.toJson(), "exam_address": examAddress!.toJson(), - "validity_date": validityDate, + "validity_date": "${validityDate!.year.toString().padLeft(4, '0')}-${validityDate!.month.toString().padLeft(2, '0')}-${validityDate!.day.toString().padLeft(2, '0')}", "license_number": licenseNumber, }; @override diff --git a/lib/model/profile/profileInfomation.dart b/lib/model/profile/profileInfomation.dart index 37987fb..dc4e69c 100644 --- a/lib/model/profile/profileInfomation.dart +++ b/lib/model/profile/profileInfomation.dart @@ -10,14 +10,14 @@ import 'package:unit2/model/profile/voluntary_works.dart'; import 'package:unit2/model/profile/work_history.dart'; class ProfileInformation{ - BasicInfo basicInfo; - OtherInformation otherInformation; - List eligibilities; - List references; - List learningsAndDevelopment; - List educationalBackgrounds; - List families; - ListworkExperiences; - List voluntaryWorks; - ProfileInformation({required this.families, required this.otherInformation, required this.voluntaryWorks, required this.workExperiences, required this.basicInfo,required this.eligibilities,required this.references, required this.learningsAndDevelopment,required this.educationalBackgrounds}); + final BasicInfo basicInfo; + // OtherInformation otherInformation; + // List eligibilities; + // List references; + // List learningsAndDevelopment; + // List educationalBackgrounds; + // List families; + // ListworkExperiences; + // List voluntaryWorks; + ProfileInformation({required this.basicInfo}); } \ No newline at end of file diff --git a/lib/screens/profile/components/basic_information/address_screen.dart b/lib/screens/profile/components/basic_information/address_screen.dart index ffb8e61..3032355 100644 --- a/lib/screens/profile/components/basic_information/address_screen.dart +++ b/lib/screens/profile/components/basic_information/address_screen.dart @@ -4,7 +4,7 @@ import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; -import 'package:unit2/widgets/add_leading.dart'; +import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; class AddressScreen extends StatelessWidget { diff --git a/lib/screens/profile/components/basic_information/contact_information_screen.dart b/lib/screens/profile/components/basic_information/contact_information_screen.dart index f89c626..7c54f71 100644 --- a/lib/screens/profile/components/basic_information/contact_information_screen.dart +++ b/lib/screens/profile/components/basic_information/contact_information_screen.dart @@ -3,7 +3,7 @@ import 'package:unit2/model/profile/basic_information/contact_information.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; -import 'package:unit2/widgets/add_leading.dart'; +import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; class ContactInformationScreen extends StatelessWidget { diff --git a/lib/screens/profile/components/basic_information/identification_information_screen.dart b/lib/screens/profile/components/basic_information/identification_information_screen.dart index a3584da..8f47db9 100644 --- a/lib/screens/profile/components/basic_information/identification_information_screen.dart +++ b/lib/screens/profile/components/basic_information/identification_information_screen.dart @@ -4,7 +4,7 @@ import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; -import 'package:unit2/widgets/add_leading.dart'; +import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; class IdentificationsScreen extends StatelessWidget { diff --git a/lib/screens/profile/components/education_screen.dart b/lib/screens/profile/components/education_screen.dart index 99af383..6602b57 100644 --- a/lib/screens/profile/components/education_screen.dart +++ b/lib/screens/profile/components/education_screen.dart @@ -4,7 +4,7 @@ import 'package:unit2/model/profile/educational_background.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; -import 'package:unit2/widgets/add_leading.dart'; +import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; class EducationScreen extends StatelessWidget { diff --git a/lib/screens/profile/components/eligibility/add_modal.dart b/lib/screens/profile/components/eligibility/add_modal.dart index f0b247c..f57b2db 100644 --- a/lib/screens/profile/components/eligibility/add_modal.dart +++ b/lib/screens/profile/components/eligibility/add_modal.dart @@ -124,6 +124,8 @@ class _AddEligibilityScreenState extends State { Flexible( flex: 1, child: FormBuilderTextField( + keyboardType: const TextInputType + .numberWithOptions(), onChanged: (value) { rating = value; }, @@ -149,8 +151,8 @@ class _AddEligibilityScreenState extends State { use24HourFormat: false, icon: const Icon(Icons.date_range), controller: examDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), + firstDate: DateTime(1970), + lastDate: DateTime(2100), timeHintText: "Date of Examination/Conferment", decoration: normalTextFieldStyle( @@ -222,8 +224,10 @@ class _AddEligibilityScreenState extends State { child: overseas == true ? FormBuilderDropdown( initialValue: null, - validator: (value) => - value == null ? 'required' : null, + validator: (value) => + value == null + ? 'required' + : null, items: state.countries.map< DropdownMenuItem< Country>>( @@ -249,8 +253,10 @@ class _AddEligibilityScreenState extends State { autovalidateMode: AutovalidateMode .onUserInteraction, - validator: (value) => - value == null ? 'required' : null, + validator: (value) => + value == null + ? 'required' + : null, onChanged: (Region? region) async { setState(() { @@ -283,14 +289,16 @@ class _AddEligibilityScreenState extends State { height: 70, child: ModalProgressHUD( color: Colors.transparent, - inAsyncCall: cityCall, + inAsyncCall: provinceCall, child: DropdownButtonFormField< Province?>( autovalidateMode: AutovalidateMode .onUserInteraction, - validator: (value) => - value == null ? 'required' : null, + validator: (value) => + value == null + ? 'required' + : null, isExpanded: true, value: selectedProvince, onChanged: (Province? @@ -325,40 +333,46 @@ class _AddEligibilityScreenState extends State { "Province")), ), ), - const SizedBox( - height: 20, - ), + + // CityMunicipalities dropdown SizedBox( height: 70, - child: - DropdownButtonFormField< - CityMunicipality>( - validator: (value) => - value == null ? 'required' : null, - isExpanded: true, - onChanged: - (CityMunicipality? - city) { - selectedMunicipality = - city; - }, - decoration: - normalTextFieldStyle( - "Municipality*", - "Municipality"), - value: selectedMunicipality, - items: citymuns == null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality - c) { - return DropdownMenuItem( - value: c, - child: Text(c - .description!)); - }).toList(), + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: + DropdownButtonFormField< + CityMunicipality>( + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + onChanged: + (CityMunicipality? + city) { + selectedMunicipality = + city; + }, + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: + selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality + c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), + ), ), ), const SizedBox( @@ -380,9 +394,11 @@ class _AddEligibilityScreenState extends State { style: mainBtnStyle( primary, Colors.transparent, second), onPressed: () { + //rating double? rate = rating == null ? null : double.parse(rating!); + //lisence String? licenseNumber = license; CityMunicipality? cityMunicipality = selectedMunicipality; @@ -450,23 +466,32 @@ class _AddEligibilityScreenState extends State { } Future getProvinces() async { - List newProvinces = await LocationUtils.instance - .getProvinces(regionCode: selectedRegion!.code.toString()); - setState(() { - provinces = newProvinces; - selectedProvince = provinces![0]; - getCities(); - provinceCall = false; - }); + try { + List newProvinces = await LocationUtils.instance + .getProvinces(regionCode: selectedRegion!.code.toString()); + setState(() { + provinces = newProvinces; + selectedProvince = provinces![0]; + provinceCall = false; + cityCall = true; + getCities(); + }); + } catch (e) { + context.read().add(CallErrorState()); + } } Future getCities() async { - List newCities = await LocationUtils.instance - .getCities(code: selectedProvince!.code.toString()); - citymuns = newCities; - setState(() { - selectedMunicipality = newCities[0]; - cityCall = false; - }); + try { + List newCities = await LocationUtils.instance + .getCities(code: selectedProvince!.code.toString()); + citymuns = newCities; + setState(() { + selectedMunicipality = newCities[0]; + cityCall = false; + }); + } catch (e) { + context.read().add(CallErrorState()); + } } } diff --git a/lib/screens/profile/components/eligibility/edit_modal.dart b/lib/screens/profile/components/eligibility/edit_modal.dart index 2f3170a..6f32e97 100644 --- a/lib/screens/profile/components/eligibility/edit_modal.dart +++ b/lib/screens/profile/components/eligibility/edit_modal.dart @@ -2,13 +2,11 @@ import 'package:date_time_picker/date_time_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; -import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:intl/intl.dart'; import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/location/city.dart'; -import 'package:unit2/model/login_data/employee_info/employee_info.dart'; import 'package:unit2/model/profile/eligibility.dart'; import 'package:unit2/model/utils/eligibility.dart'; import 'package:unit2/utils/location_utilities.dart'; @@ -33,18 +31,23 @@ class _EditEligibilityScreenState extends State { final formKey = GlobalKey(); final provinceKey = GlobalKey(); bool? overseas; + List? provinces; + List? citymuns; + List? regions; DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); Region? selectedRegion; Province? selectedProvince; CityMunicipality? selectedMunicipality; Country? selectedCountry; Eligibility? selectedEligibility; - List? provinces; - List? citymuns; bool provinceCall = false; bool cityCall = false; - // final examDateController = TextEditingController(); - // final validityDateController = TextEditingController(); + String? token; + String? profileId; + String? rating; + String? license; + final examDateController = TextEditingController(); + final validityDateController = TextEditingController(); @override Widget build(BuildContext context) { //USERBLOC @@ -53,6 +56,8 @@ class _EditEligibilityScreenState extends State { //LOGGED IN USER STATE if (state is UserLoggedIn) { //PROFIILE BLOC + token = state.userData!.user!.login!.token; + profileId = state.userData!.user!.login!.user!.profileId.toString(); return BlocBuilder( buildWhen: (previous, current) { if (state is EditEligibilityState) {} @@ -61,23 +66,38 @@ class _EditEligibilityScreenState extends State { builder: (context, state) { //EDIT ELIGIBILITY STATE if (state is EditEligibilityState) { - return ProgressHUD( - child: Center( - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 25, horizontal: 18), - child: FormBuilder( - key: formKey, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - //ELIGIBILITIES DROPDOWN - FormBuilderDropdown( + examDateController.text = state.eligibityCert.examDate == null?'': state.eligibityCert.examDate.toString(); + validityDateController.text = state.eligibityCert.validityDate == null?'': state.eligibityCert.validityDate.toString(); + + provinces = state.provinces; + citymuns = state.cities; + regions = state.regions; + overseas = state.isOverseas; + selectedRegion = state.currentRegion; + selectedProvince = state.currentProvince; + selectedMunicipality = state.currentCity; + selectedEligibility= state.currentEligibility; + rating = state.eligibityCert.rating?.toString(); + license = state.eligibityCert.licenseNumber; + return Center( + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, horizontal: 18), + child: FormBuilder( + key: formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + //ELIGIBILITIES DROPDOWN + DropdownButtonFormField( + validator: (value) => + value == null ? 'required' : null, + isExpanded: true, onChanged: (Eligibility? eligibility) { selectedEligibility = eligibility; }, - initialValue: null, + value: selectedEligibility, items: state.eligibilities .map>( (Eligibility eligibility) { @@ -85,120 +105,116 @@ class _EditEligibilityScreenState extends State { value: eligibility, child: Text(eligibility.title)); }).toList(), - name: "eligibility", decoration: - normalTextFieldStyle("Eligibility", "") - .copyWith( - hintStyle: const TextStyle( - color: Colors.black, - ), - labelStyle: const TextStyle( - color: Colors.black)), - ), - const SizedBox( - height: 20, - ), + normalTextFieldStyle("Eligibility", "")), + const SizedBox( + height: 20, + ), - SizedBox( - width: screenWidth, - child: Row( - children: [ - //LICENSE NUMBER - Flexible( - flex: 1, - child: FormBuilderTextField( - name: 'license_number', - initialValue: - widget.eligibityCert.licenseNumber, - decoration: normalTextFieldStyle( - "license number", "license number"), - ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + //LICENSE NUMBER + Flexible( + flex: 1, + child: FormBuilderTextField( + onChanged: (value) { + license = value; + }, + name: 'license_number', + initialValue: + license, + decoration: normalTextFieldStyle( + "license number", "license number"), ), - const SizedBox( - width: 12, + ), + const SizedBox( + width: 12, + ), + //RATING + Flexible( + flex: 1, + child: FormBuilderTextField( + keyboardType: const TextInputType + .numberWithOptions(), + onChanged: (value) { + rating = value; + }, + name: 'rating', + initialValue: rating == null + ? 'N/A' + : rating.toString(), + decoration: normalTextFieldStyle( + 'rating', 'rating'), ), - //RATING - Flexible( - flex: 1, - child: FormBuilderTextField( - name: 'rating', - - // ignore: prefer_null_aware_operators - initialValue: - widget.eligibityCert.rating == null - ? null - : widget.eligibityCert.rating - .toString(), - - decoration: normalTextFieldStyle( - 'rating', 'rating'), - ), - ), - ], - ), + ), + ], ), - const SizedBox( - height: 20, - ), - SizedBox( - width: screenWidth, - child: Row( - children: [ - //EXAM DATE - Flexible( - flex: 1, - child: DateTimePicker( - // controller: examDateController, - firstDate: DateTime(2000), - lastDate: DateTime(2100), - decoration: normalTextFieldStyle( - "Exam date", "Exam date"), - initialValue: - widget.eligibityCert.examDate == - null - ? '' - : dteFormat2.format(widget - .eligibityCert.examDate!), - )), - const SizedBox( - width: 12, - ), - //VALIDITY DATE - Flexible( + ), + const SizedBox( + height: 20, + ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + //EXAM DATE + Flexible( flex: 1, child: DateTimePicker( - // controller: validityDateController, - firstDate: DateTime(2000), + use24HourFormat: false, + controller: examDateController, + firstDate: DateTime(1970), lastDate: DateTime(2100), decoration: normalTextFieldStyle( - "Validity date", "Validity date"), - initialValue: - widget.eligibityCert.validityDate == - null - ? '' - : dteFormat2.format(widget - .eligibityCert - .validityDate!), - ), + "Exam date", "") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + )), + const SizedBox( + width: 12, + ), + //VALIDITY DATE + Flexible( + flex: 1, + child: DateTimePicker( + use24HourFormat: false, + controller: validityDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + decoration: normalTextFieldStyle( + "validity date", "") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), ), - ], - ), + ), + ], ), - const SizedBox( - height: 20, - ), - Text( - "Placement of Examination/Confinement", - style: Theme.of(context) - .textTheme - .displaySmall! - .copyWith(fontSize: blockSizeVertical * 2), - ), - const SizedBox( - height: 12, - ), - //OVERSEAS ADDRESS SWITCH - Column( + ), + const SizedBox( + height: 20, + ), + Text( + "Placement of Examination/Confinement", + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith(fontSize: blockSizeVertical * 2), + ), + const SizedBox( + height: 12, + ), + //OVERSEAS ADDRESS SWITCH + StatefulBuilder( + builder: (context, StateSetter setState) { + return Column( children: [ FormBuilderSwitch( initialValue: overseas, @@ -219,7 +235,12 @@ class _EditEligibilityScreenState extends State { SizedBox( child: overseas == true ? FormBuilderDropdown( - initialValue: null, + validator: (value) => + value == null + ? 'required' + : null, + initialValue: + state.selectedCountry, items: state.countries.map< DropdownMenuItem< Country>>( @@ -241,50 +262,96 @@ class _EditEligibilityScreenState extends State { : Column( children: [ //REGION DROPDOWN - FormBuilderDropdown( + DropdownButtonFormField< + Region?>( + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, onChanged: (Region? region) async { setState(() { provinceCall = true; }); selectedRegion = region; - getProvinces(); + provinces = await LocationUtils + .instance + .getProvinces( + regionCode: + selectedRegion! + .code + .toString()); + selectedProvince = + provinces![0]; + setState(() { + provinceCall = false; + cityCall = true; + }); + citymuns = await LocationUtils + .instance + .getCities( + code: + selectedProvince! + .code!); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + }); }, - initialValue: selectedRegion, + value: selectedRegion, decoration: normalTextFieldStyle( "Region*", "Region"), - name: 'region', - items: state.regions.map< - DropdownMenuItem< - Region>>( - (Region region) { - return DropdownMenuItem< - Region>( - value: region, - child: Text(region - .description!)); - }).toList(), + items: regions == null + ? [] + : regions!.map< + DropdownMenuItem< + Region>>( + (Region region) { + return DropdownMenuItem< + Region>( + value: region, + child: Text(region + .description!)); + }).toList(), ), const SizedBox( height: 20, ), //PROVINCE DROPDOWN SizedBox( - height: 50, + height: 70, child: ModalProgressHUD( - inAsyncCall: cityCall, + color: Colors.transparent, + inAsyncCall: provinceCall, child: DropdownButtonFormField< Province?>( - isExpanded: true, + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, value: selectedProvince, onChanged: (Province? - province) { + province) async { setState(() { - cityCall = true; + cityCall = true; + }); + selectedProvince = + province; + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code + .toString()); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; }); - selectedProvince = province; - getCities(); }, items: provinces == null ? [] @@ -296,7 +363,8 @@ class _EditEligibilityScreenState extends State { return DropdownMenuItem( value: province, - child: FittedBox( + child: + FittedBox( child: Text( province .description!), @@ -308,13 +376,21 @@ class _EditEligibilityScreenState extends State { "Province")), ), ), - const SizedBox( - height: 20, - ), + + // City municipality SizedBox( - height: 50, - child: DropdownButtonFormField< - CityMunicipality>( + height: 70, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: cityCall, + child: + DropdownButtonFormField< + CityMunicipality>( + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, onChanged: (CityMunicipality? city) { @@ -325,7 +401,8 @@ class _EditEligibilityScreenState extends State { normalTextFieldStyle( "Municipality*", "Municipality"), - value: selectedMunicipality, + value: + selectedMunicipality, items: citymuns == null ? [] : citymuns!.map< @@ -338,7 +415,8 @@ class _EditEligibilityScreenState extends State { child: Text(c .description!)); }).toList(), - ), + ), + ), ), const SizedBox( height: 20, @@ -346,26 +424,75 @@ class _EditEligibilityScreenState extends State { ], )), ], - ), + ); + }), - const Expanded( - child: SizedBox(), - ), + const Expanded( + child: SizedBox(), + ), - SizedBox( - width: screenWidth, - height: 60, - child: ElevatedButton( - style: mainBtnStyle( - primary, Colors.transparent, second), - onPressed: () {}, - child: const Text(submit)), - ), - const SizedBox( - height: 20, - ), - ]), - ), + SizedBox( + width: screenWidth, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + + //rating + double? rate = rating == null + ? null + : double.parse(rating!); + //license + String? newLicense = license; + //city municipality + CityMunicipality? cityMunicipality = + selectedMunicipality; + //exam date + DateTime? examDate = + examDateController.text.isEmpty + ? null + : DateTime.parse( + examDateController.text); + // validity date + DateTime? validityDate = + validityDateController.text.isEmpty + ? null + : DateTime.parse( + validityDateController.text); + // exam address + ExamAddress examAddress = ExamAddress( + barangay: state.eligibityCert.examAddress?.barangay, + id: state.eligibityCert.examAddress?.id, + addressCategory: state.eligibityCert.examAddress?.addressCategory, + examAddressClass: state.eligibityCert.examAddress?.examAddressClass, + country: selectedCountry ??= Country( + id: 175, + name: 'Philippines', + code: 'PH'), + cityMunicipality: cityMunicipality); + EligibityCert eligibityCert = + EligibityCert( + id: state.eligibityCert.id, + rating: rate, + examDate: examDate, + attachments: null, + eligibility: selectedEligibility, + examAddress: examAddress, + validityDate: validityDate, + licenseNumber: newLicense, + overseas: overseas); + if (formKey.currentState! + .saveAndValidate()) { + context.read().add(UpdateEligibility(eligibityCert: eligibityCert, oldEligibility: state.eligibityCert.eligibility!.id, profileId: profileId!, token: token!)); + } + }, + child: const Text(submit)), + ), + const SizedBox( + height: 20, + ), + ]), ), ), ); @@ -378,23 +505,4 @@ class _EditEligibilityScreenState extends State { }, ); } - - Future getProvinces() async { - List _provinces = await LocationUtils.instance - .getProvinces(regionCode: selectedRegion!.code.toString()); - setState(() { - provinces = _provinces; - selectedProvince = provinces![0]; - getCities(); - provinceCall = false; - }); - } - Future getCities()async{ - List _cities = await LocationUtils.instance.getCities(code: selectedProvince!.code.toString()); - citymuns = _cities; - setState(() { - selectedMunicipality = _cities[0]; - cityCall = false; - }); - } } diff --git a/lib/screens/profile/components/eligibility_screen.dart b/lib/screens/profile/components/eligibility_screen.dart index ba937eb..3e3623a 100644 --- a/lib/screens/profile/components/eligibility_screen.dart +++ b/lib/screens/profile/components/eligibility_screen.dart @@ -2,6 +2,7 @@ import 'package:app_popup_menu/app_popup_menu.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:fluttericon/font_awesome_icons.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; @@ -12,9 +13,9 @@ import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; -import 'package:unit2/widgets/add_leading.dart'; +import 'package:unit2/widgets/Leadings/add_leading.dart'; +import 'package:unit2/widgets/Leadings/close_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; - import '../../../utils/alerts.dart'; class EligibiltyScreen extends StatelessWidget { @@ -24,240 +25,298 @@ class EligibiltyScreen extends StatelessWidget { Widget build(BuildContext context) { String? token; String? profileId; - return Scaffold( - appBar: AppBar( - title: const Text(elibilityScreenTitle), - centerTitle: true, - backgroundColor: primary, - actions: [ - AddLeading(onPressed: () { - context.read().add(ShowAddEligibilityForm()); - }) - ], - ), - body: BlocBuilder( - builder: (context, state) { - if (state is UserLoggedIn) { - token = state.userData!.user!.login!.token; - profileId = - state.userData!.user!.login!.user!.profileId.toString(); - return ProgressHUD( - child: BlocConsumer( - listener: (context, state) { - if (state is EditEligibilityState) { - final progress = ProgressHUD.of(context); - progress!.dismiss(); - } - if (state is ProfileLoading) { - final progress = ProgressHUD.of(context); - progress!.showWithText("Loading"); - } - if (state is EligibilityLoaded || - state is AddEligibilityState || - state is ProfileErrorState) { - final progress = ProgressHUD.of(context); - progress!.dismiss(); - } - if (state is DeletedState) { - if (state.success) { - successAlert(context, "Deletion Successfull", - "Eligibility has been deleted successfully", () { - context.read().add(LoadEligibility( - eligibilities: state.eligibilities)); - }); - } else { - errorAlert(context, "Deletion Failed", - "Error deleting eligibility", () { - Navigator.of(context).pop(); - context.read().add(LoadEligibility( - eligibilities: state.eligibilities)); - }); + List? eligibilities; + return WillPopScope( + onWillPop: () async { + return true; + }, + child: Scaffold( + appBar: AppBar( + title: context.watch().state is AddEligibilityState + ? const Text("Add Eligiblity") + : context.watch().state is EditEligibilityState + ? const Text("Edit Eligibilty") + : const Text(elibilityScreenTitle), + centerTitle: true, + backgroundColor: primary, + actions: (context.watch().state is EligibilityLoaded || + context.watch().state is ProfileLoading) + ? [ + AddLeading(onPressed: () { + context.read().add(ShowAddEligibilityForm()); + }) + ] + : [ + CloseLeading(onPressed: () { + context + .read() + .add(GetEligibilities(profileId: int.parse(profileId!), token: token!)); + }) + ], + ), + body: BlocBuilder( + builder: (context, state) { + if (state is UserLoggedIn) { + token = state.userData!.user!.login!.token; + profileId = + state.userData!.user!.login!.user!.profileId.toString(); + return ProgressHUD( + padding: const EdgeInsets.all(24), + indicatorWidget: const SpinKitFadingCircle( + color: Colors.white, + ), + backgroundColor: Colors.black87, + child: BlocConsumer( + listener: (context, state) { + if (state is ProfileLoading) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading"); } - } - if (state is EligibilityAddedState) { - if (state.response['success']) { - Navigator.of(context).pop(); - successAlert(context, "Adding Successfull!", - state.response['message'], () { - context.read().add(LoadEligibility( - eligibilities: state.eligibilities)); - }); - } else { - errorAlert(context, "Adding Failed", - "Something went wrong. Please try again.", () { - Navigator.of(context).pop(); - context.read().add(LoadEligibility( - eligibilities: state.eligibilities)); - }); + if (state is EligibilityLoaded || + state is AddEligibilityState || + state is ProfileErrorState || + state is EditEligibilityState || + state is DeletedState || + state is EligibilityAddedState || + state is EligibilityEditedState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); } - } - }, - builder: (context, state) { - return BlocBuilder( - builder: (context, state) { - if (state is EligibilityLoaded) { - if (state.eligibilities.isNotEmpty) { - return ListView.builder( - padding: const EdgeInsets.symmetric( - vertical: 8, horizontal: 10), - itemCount: state.eligibilities.length, - itemBuilder: (BuildContext context, int index) { - String title = state - .eligibilities[index].eligibility!.title; - return Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Container( - width: screenWidth, - padding: const EdgeInsets.symmetric( - horizontal: 12, vertical: 8), - decoration: box1(), - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - title, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith( - fontWeight: - FontWeight - .w500), - ), - const Divider(), - const SizedBox( - height: 5, - ), - Text( - "$licenseNumber: ${state.eligibilities[index].licenseNumber == null ? 'N/A' : state.eligibilities[index].licenseNumber.toString()}", + //DELETED STATE + if (state is DeletedState) { + if (state.success) { + successAlert(context, "Deletion Successfull", + "Eligibility has been deleted successfully", () { + Navigator.of(context).pop(); + context.read().add(LoadEligibility( + eligibilities: state.eligibilities)); + }); + } else { + errorAlert(context, "Deletion Failed", + "Error deleting eligibility", () { + Navigator.of(context).pop(); + context.read().add(LoadEligibility( + eligibilities: state.eligibilities)); + }); + } + } + //ADDED STATE + if (state is EligibilityAddedState) { + if (state.response['success']) { + successAlert(context, "Adding Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context.read().add(LoadEligibility( + eligibilities: state.eligibilities)); + }); + } else { + errorAlert(context, "Adding Failed", + "Something went wrong. Please try again.", () { + Navigator.of(context).pop(); + context.read().add(LoadEligibility( + eligibilities: state.eligibilities)); + }); + } + } + //UPDATED STATE + if (state is EligibilityEditedState) { + if (state.response['success']) { + successAlert(context, "Update Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context.read().add(LoadEligibility( + eligibilities: state.eligibilities)); + }); + } else { + errorAlert(context, "Update Failed", + "Something went wrong. Please try again.", () { + Navigator.of(context).pop(); + context.read().add(LoadEligibility( + eligibilities: state.eligibilities)); + }); + } + } + }, + builder: (context, state) { + return BlocBuilder( + builder: (context, state) { + if (state is EligibilityLoaded) { + eligibilities = state.eligibilities; + if (state.eligibilities.isNotEmpty) { + return ListView.builder( + padding: const EdgeInsets.symmetric( + vertical: 8, horizontal: 10), + itemCount: state.eligibilities.length, + itemBuilder: + (BuildContext context, int index) { + String title = state.eligibilities[index] + .eligibility!.title; + return Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Container( + width: screenWidth, + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + decoration: box1(), + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + Text( + title, style: Theme.of(context) .textTheme - .titleSmall), - const SizedBox( - height: 3, - ), - Text( - "Rating : ${state.eligibilities[index].rating ?? 'N/A'}.", - style: Theme.of(context) - .textTheme - .titleSmall) - ]), - ), - AppPopupMenu( - offset: const Offset(-10, -10), - elevation: 3, - onSelected: (value) { - if (value == 2) { - confirmAlert(context, () { - BlocProvider.of< - ProfileBloc>( - context) - .add(DeleteEligibility( - eligibilities: state - .eligibilities, - eligibilityId: state - .eligibilities[ - index] - .id!, - profileId: - profileId!, - token: token!)); - }, "Delete?", - "Confirm Delete?"); - } - if (value == 1) { - EligibityCert eligibityCert = - state - .eligibilities[index]; - bool overseas = eligibityCert - .examAddress! - .country! - .id - .toString() == - '175' - ? false - : true; - eligibityCert.overseas = - overseas; + .titleMedium! + .copyWith( + fontWeight: + FontWeight + .w500), + ), + const Divider(), + const SizedBox( + height: 5, + ), + Text( + "$licenseNumber: ${state.eligibilities[index].licenseNumber == null ? 'N/A' : state.eligibilities[index].licenseNumber.toString()}", + style: + Theme.of(context) + .textTheme + .titleSmall), + const SizedBox( + height: 3, + ), + Text( + "Rating : ${state.eligibilities[index].rating ?? 'N/A'}.", + style: + Theme.of(context) + .textTheme + .titleSmall) + ]), + ), + AppPopupMenu( + offset: const Offset(-10, -10), + elevation: 3, + onSelected: (value) { final progress = ProgressHUD.of(context); - eligibityCert.overseas = - overseas; progress!.showWithText( "Loading..."); - context - .read() - .add(EditEligibility( - eligibityCert: - eligibityCert)); - } - }, - menuItems: [ - popMenuItem( - text: "Edit", - value: 1, - icon: Icons.edit), - popMenuItem( - text: "Delete", - value: 2, - icon: Icons.delete), - popMenuItem( - text: "Attachment", - value: 3, - icon: FontAwesome.attach) - ], - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - ), - tooltip: "Options", - ) - ], + ////delete eligibilty-= = = = = = = = =>> + if (value == 2) { + confirmAlert(context, () { + BlocProvider.of< + ProfileBloc>( + context) + .add(DeleteEligibility( + eligibilities: + state + .eligibilities, + eligibilityId: state + .eligibilities[ + index] + .id!, + profileId: + profileId!, + token: token!)); + }, "Delete?", + "Confirm Delete?"); + } + if (value == 1) { + ////edit eligibilty-= = = = = = = = =>> + EligibityCert + eligibityCert = + state.eligibilities[ + index]; + bool overseas = eligibityCert + .examAddress! + .country! + .id + .toString() == + '175' + ? false + : true; + eligibityCert.overseas = + overseas; + + eligibityCert.overseas = + overseas; + + context.read().add( + ShowEditEligibilityForm( + eligibityCert: + eligibityCert)); + } + }, + menuItems: [ + popMenuItem( + text: "Edit", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Delete", + value: 2, + icon: Icons.delete), + popMenuItem( + text: "Attachment", + value: 3, + icon: FontAwesome.attach) + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ) + ], + ), ), - ), - const SizedBox( - height: 5, - ) - ], - ); - }); - } else { - return const EmptyData( - message: - "You don't have any eligibilities added. Please click + to add"); + const SizedBox( + height: 5, + ) + ], + ); + }); + } else { + return const EmptyData( + message: + "You don't have any eligibilities added. Please click + to add"); + } } - } - if (state is EditEligibilityState) { - return EditEligibilityScreen( - eligibityCert: state.eligibityCert); - } - if (state is AddEligibilityState) { - return const AddEligibilityScreen(); - } - if (state is ProfileErrorState) { - return Center( - child: Text(state.mesage), + if (state is EditEligibilityState) { + return EditEligibilityScreen( + eligibityCert: state.eligibityCert); + } + if (state is AddEligibilityState) { + return const AddEligibilityScreen(); + } + if (state is ProfileErrorState) { + return Center( + child: Text(state.mesage), + ); + } + return Container( + color: Colors.grey.shade200, ); - } - return Container(); - }, - ); - }, - ), - ); - } - return Container(); - }, - )); + }, + ); + }, + ), + ); + } + return Container(); + }, + )), + ); } PopupMenuItem popMenuItem({String? text, int? value, IconData? icon}) { diff --git a/lib/screens/profile/components/learning_and_development_screen.dart b/lib/screens/profile/components/learning_and_development_screen.dart index 5b53706..e7c9a4a 100644 --- a/lib/screens/profile/components/learning_and_development_screen.dart +++ b/lib/screens/profile/components/learning_and_development_screen.dart @@ -7,7 +7,7 @@ import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; -import 'package:unit2/widgets/add_leading.dart'; +import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; class LearningAndDevelopmentScreen extends StatelessWidget { diff --git a/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart b/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart index a85f8ba..6aa6465 100644 --- a/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart +++ b/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart @@ -6,7 +6,7 @@ import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; -import 'package:unit2/widgets/add_leading.dart'; +import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; class NonAcademicRecognitionScreen extends StatelessWidget { diff --git a/lib/screens/profile/components/other_information/org_membership_screen.dart b/lib/screens/profile/components/other_information/org_membership_screen.dart index 007c020..01b870f 100644 --- a/lib/screens/profile/components/other_information/org_membership_screen.dart +++ b/lib/screens/profile/components/other_information/org_membership_screen.dart @@ -3,7 +3,7 @@ import 'package:unit2/model/profile/other_information/organization_memberships.d import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; -import 'package:unit2/widgets/add_leading.dart'; +import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; import '../../../../utils/global.dart'; diff --git a/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart b/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart index cbc57e2..07f6455 100644 --- a/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart +++ b/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart @@ -5,7 +5,7 @@ import 'package:unit2/model/profile/other_information/skills_and_hobbies.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; -import 'package:unit2/widgets/add_leading.dart'; +import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; class SkillHobbiesScreen extends StatelessWidget { diff --git a/lib/screens/profile/components/references_screen.dart b/lib/screens/profile/components/references_screen.dart index 319fd18..b02608e 100644 --- a/lib/screens/profile/components/references_screen.dart +++ b/lib/screens/profile/components/references_screen.dart @@ -5,7 +5,7 @@ import 'package:unit2/model/profile/references.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; -import 'package:unit2/widgets/add_leading.dart'; +import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; class ReferencesScreen extends StatelessWidget { diff --git a/lib/screens/profile/components/voluntary_works_screen.dart b/lib/screens/profile/components/voluntary_works_screen.dart index eebdaed..17c33b8 100644 --- a/lib/screens/profile/components/voluntary_works_screen.dart +++ b/lib/screens/profile/components/voluntary_works_screen.dart @@ -4,7 +4,7 @@ import 'package:unit2/model/profile/voluntary_works.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; -import 'package:unit2/widgets/add_leading.dart'; +import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; class VolunataryWorkScreen extends StatelessWidget { diff --git a/lib/screens/profile/components/work_history_screen.dart b/lib/screens/profile/components/work_history_screen.dart index 1d896dd..71deb9f 100644 --- a/lib/screens/profile/components/work_history_screen.dart +++ b/lib/screens/profile/components/work_history_screen.dart @@ -4,7 +4,7 @@ import 'package:unit2/model/profile/work_history.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; -import 'package:unit2/widgets/add_leading.dart'; +import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; import '../../../utils/global.dart'; diff --git a/lib/screens/profile/profile.dart b/lib/screens/profile/profile.dart index 1049507..2d305ed 100644 --- a/lib/screens/profile/profile.dart +++ b/lib/screens/profile/profile.dart @@ -2,6 +2,7 @@ import 'package:expandable_group/expandable_group_widget.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:fluttericon/brandico_icons.dart'; import 'package:fluttericon/elusive_icons.dart'; import 'package:fluttericon/entypo_icons.dart'; @@ -38,6 +39,8 @@ class ProfileInfo extends StatefulWidget { } class _ProfileInfoState extends State { + int? profileId; + String? token; @override Widget build(BuildContext context) { return Scaffold( @@ -47,8 +50,12 @@ class _ProfileInfoState extends State { title: const Text('Profile'), ), body: ProgressHUD( + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), child: BlocBuilder(builder: (context, state) { if (state is UserLoggedIn) { + profileId = state.userData!.user!.login!.user!.profileId; + token = state.userData!.user!.login!.token!; return BlocConsumer( listener: (context, state) { if (state is ProfileLoading) { @@ -57,7 +64,7 @@ class _ProfileInfoState extends State { 'Loading Profile', ); } - if (state is ProfileLoaded) { + if (state is ProfileLoaded || state is ProfileErrorState) { final progress = ProgressHUD.of(context); progress?.dismiss(); } @@ -141,12 +148,12 @@ class _ProfileInfoState extends State { icon: Elusive.group, title: "Family", onTap: () { - Navigator.push(context, MaterialPageRoute( - builder: (BuildContext context) { - return FamilyBackgroundScreen( - familyBackground: - state.profileInformation.families); - })); + // Navigator.push(context, MaterialPageRoute( + // builder: (BuildContext context) { + // return FamilyBackgroundScreen( + // familyBackground: + // state.profileInformation.families); + // })); }, ), const Divider(), @@ -154,13 +161,13 @@ class _ProfileInfoState extends State { icon: FontAwesome5.graduation_cap, title: "Education", onTap: () { - Navigator.push(context, MaterialPageRoute( - builder: (BuildContext context) { - return EducationScreen( - educationBackgrounds: state - .profileInformation - .educationalBackgrounds); - })); + // Navigator.push(context, MaterialPageRoute( + // builder: (BuildContext context) { + // return EducationScreen( + // educationBackgrounds: state + // .profileInformation + // .educationalBackgrounds); + // })); }, ), const Divider(), @@ -170,10 +177,11 @@ class _ProfileInfoState extends State { onTap: () { Navigator.push(context, MaterialPageRoute( builder: (BuildContext context) { - return BlocProvider.value(value: ProfileBloc()..add(LoadEligibility(eligibilities: state.profileInformation.eligibilities)), - - child: EligibiltyScreen( - ), + return BlocProvider.value( + value: ProfileBloc() + ..add(GetEligibilities( + profileId: profileId!, token: token!)), + child: const EligibiltyScreen(), ); })); }, @@ -183,12 +191,12 @@ class _ProfileInfoState extends State { icon: FontAwesome5.shopping_bag, title: "Work History", onTap: () { - Navigator.push(context, MaterialPageRoute( - builder: (BuildContext context) { - return WorkHistoryScreen( - workExperiences: state - .profileInformation.workExperiences); - })); + // Navigator.push(context, MaterialPageRoute( + // builder: (BuildContext context) { + // return WorkHistoryScreen( + // workExperiences: state + // .profileInformation.workExperiences); + // })); }, ), const Divider(), @@ -196,12 +204,12 @@ class _ProfileInfoState extends State { icon: FontAwesome5.walking, title: "Voluntary Work & Civic Services", onTap: () { - Navigator.push(context, MaterialPageRoute( - builder: (BuildContext context) { - return VolunataryWorkScreen( - voluntaryWorks: state - .profileInformation.voluntaryWorks); - })); + // Navigator.push(context, MaterialPageRoute( + // builder: (BuildContext context) { + // return VolunataryWorkScreen( + // voluntaryWorks: state + // .profileInformation.voluntaryWorks); + // })); }, ), const Divider(), @@ -209,13 +217,13 @@ class _ProfileInfoState extends State { icon: Elusive.lightbulb, title: "Learning & Development", onTap: () { - Navigator.push(context, MaterialPageRoute( - builder: (BuildContext context) { - return LearningAndDevelopmentScreen( - learningDevelopments: state - .profileInformation - .learningsAndDevelopment); - })); + // Navigator.push(context, MaterialPageRoute( + // builder: (BuildContext context) { + // return LearningAndDevelopmentScreen( + // learningDevelopments: state + // .profileInformation + // .learningsAndDevelopment); + // })); }, ), const Divider(), @@ -223,12 +231,12 @@ class _ProfileInfoState extends State { icon: Brandico.codepen, title: "Personal References", onTap: () { - Navigator.push(context, MaterialPageRoute( - builder: (BuildContext context) { - return ReferencesScreen( - references: - state.profileInformation.references); - })); + // Navigator.push(context, MaterialPageRoute( + // builder: (BuildContext context) { + // return ReferencesScreen( + // references: + // state.profileInformation.references); + // })); }, ), ExpandableGroup( @@ -249,32 +257,32 @@ class _ProfileInfoState extends State { subMenu( Icons.fitness_center, "Skills & Hobbies", () { - Navigator.push(context, MaterialPageRoute( - builder: (BuildContext context) { - return SkillHobbiesScreen( - skillsHobbies: state.profileInformation - .otherInformation.skillsAndHobbies); - })); + // Navigator.push(context, MaterialPageRoute( + // builder: (BuildContext context) { + // return SkillHobbiesScreen( + // skillsHobbies: state.profileInformation + // .otherInformation.skillsAndHobbies); + // })); }), subMenu(FontAwesome5.certificate, "Organization Memberships", () { - Navigator.push(context, MaterialPageRoute( - builder: (BuildContext context) { - return OrgMembershipsScreen( - orgMemberships: state.profileInformation - .otherInformation.orgMemberships); - })); + // Navigator.push(context, MaterialPageRoute( + // builder: (BuildContext context) { + // return OrgMembershipsScreen( + // orgMemberships: state.profileInformation + // .otherInformation.orgMemberships); + // })); }), subMenu(Entypo.doc_text, "Non-Academic Recognitions", () { - Navigator.push(context, MaterialPageRoute( - builder: (BuildContext context) { - return NonAcademicRecognitionScreen( - nonAcademicRecognitions: state - .profileInformation - .otherInformation - .nonAcademicRecognition); - })); + // Navigator.push(context, MaterialPageRoute( + // builder: (BuildContext context) { + // return NonAcademicRecognitionScreen( + // nonAcademicRecognitions: state + // .profileInformation + // .otherInformation + // .nonAcademicRecognition); + // })); }), ]), ExpandableGroup( @@ -302,6 +310,9 @@ class _ProfileInfoState extends State { if (state is ProfileLoading) { return const LoadingScreen(); } + if (state is ProfileErrorState) { + return Text(state.mesage); + } return Container(); }, diff --git a/lib/screens/unit2/login/login.dart b/lib/screens/unit2/login/login.dart index 317adbc..d59d3d7 100644 --- a/lib/screens/unit2/login/login.dart +++ b/lib/screens/unit2/login/login.dart @@ -1,6 +1,7 @@ import 'package:barcode_scan2/barcode_scan2.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; @@ -38,29 +39,21 @@ class _UniT2LoginState extends State { onWillPop: pressAgainToExit, child: Scaffold( body: ProgressHUD( + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), child: BlocConsumer(listener: (context, state) { - if (state is UserLoggedIn) { + if (state is UserLoggedIn || + state is UuidLoaded || + state is UserError || + state is InternetTimeout) { final progress = ProgressHUD.of(context); progress!.dismiss(); Navigator.pushReplacementNamed(context, '/module-screen'); } - if (state is UuidLoaded) { - final progress = ProgressHUD.of(context); - progress!.dismiss(); - Navigator.pushNamed(context, '/qr-login'); - } - if (state is UserError) { - final progress = ProgressHUD.of(context); - progress!.dismiss(); - } - if (state is InternetTimeout) { - final progress = ProgressHUD.of(context); - progress!.dismiss(); - } if (state is InvalidCredentials) { final progress = ProgressHUD.of(context); progress!.dismiss(); - errorAlert(context, "Error Login", state.message,(){}); + errorAlert(context, "Error Login", state.message, () {}); context.read().add(LoadVersion()); } }, builder: (context, state) { @@ -321,8 +314,8 @@ class _UniT2LoginState extends State { } if (state is UserError) { return SomethingWentWrong( - message:onError , - onpressed:(){} , + message: onError, + onpressed: () {}, ); } if (state is InternetTimeout) { diff --git a/lib/sevices/profile/eligibility_services.dart b/lib/sevices/profile/eligibility_services.dart index fa24736..1c3e183 100644 --- a/lib/sevices/profile/eligibility_services.dart +++ b/lib/sevices/profile/eligibility_services.dart @@ -8,6 +8,34 @@ import 'package:http/http.dart' as http; class EligibilityService { static final EligibilityService _instance = EligibilityService(); static EligibilityService get instance => _instance; + + + Future> getEligibilities(int profileId, String token)async{ + List eligibilities = []; + String authToken = "Token $token"; + String path = "${Url.instance.getEligibilities()}$profileId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + + }; + + // try{ + http.Response response = await Request.instance.getRequest(path: path,headers: headers,param: {}); + if(response.statusCode == 200){ + Map data = jsonDecode(response.body); + if (data['data']!= null) { + data['data'].forEach((var cert) { + EligibityCert eligibility = EligibityCert.fromJson(cert); + eligibilities.add(eligibility); + }); + } + } + // }catch(e){ + // throw e.toString(); + // } + return eligibilities; + } Future delete( {required int eligibilityId, @@ -22,7 +50,7 @@ class EligibilityService { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': authtoken }; - // try{ + try{ http.Response response = await Request.instance .deleteRequest(path: path, headers: headers, body: body, param: params); if (response.statusCode == 200) { @@ -32,9 +60,9 @@ class EligibilityService { success = false; } - // }catch(e){ - // throw(e.toString()); - // } + }catch(e){ + throw(e.toString()); + } return success!; } @@ -43,7 +71,7 @@ class EligibilityService { required String token, required int profileId}) async { Map? _response={}; - String authtoken = "Token $token+1"; + String authtoken = "Token $token"; String path = '${Url.instance.addEligibility()}$profileId/'; Map headers = { 'Content-Type': 'application/json; charset=UTF-8', @@ -74,4 +102,42 @@ class EligibilityService { throw e.toString(); } } + + Future> update( + {required EligibityCert eligibityCert, + required String token, + required int profileId, required int oldEligibility}) async { + Map? response={}; + String authtoken = "Token $token"; + String path = '${Url.instance.addEligibility()}$profileId/'; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authtoken + }; + + Map body = { + 'eligibility_id': eligibityCert.eligibility!.id, + 'license_number': eligibityCert.licenseNumber, + 'exam_date': eligibityCert.examDate?.toString(), + 'validity_date': eligibityCert.validityDate?.toString(), + 'rating': eligibityCert.rating, + '_citymunCode': eligibityCert.examAddress?.cityMunicipality?.code, + '_countryId': eligibityCert.examAddress?.country!.id, + '_oldEligibilityId': oldEligibility + }; + try { + http.Response res = await Request.instance + .putRequest(path: path, body: body, headers: headers, param: {}); + if (res.statusCode == 200) { + Map data = jsonDecode(res.body); + response = data; + } else { + response.addAll({'success':false}); + } + + return response; + } catch (e) { + throw e.toString(); + } + } } diff --git a/lib/sevices/profile/profile_service.dart b/lib/sevices/profile/profile_service.dart index af76c43..b38bf79 100644 --- a/lib/sevices/profile/profile_service.dart +++ b/lib/sevices/profile/profile_service.dart @@ -37,7 +37,7 @@ class ProfileService { List addresses = []; List identificationInformation = []; List contactInformation = []; - List eligibilities = []; + List families = []; List citizenships = []; List learningsDevelopments = []; @@ -50,9 +50,10 @@ class ProfileService { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': "Token $token" }; + Map param={"basic":"true"}; // try{ http.Response response = await Request.instance - .getRequest(path: path, param: {}, headers: headers); + .getRequest(path: path, param: param, headers: headers); if (response.statusCode == 200) { Map data = jsonDecode(response.body); @@ -92,100 +93,100 @@ class ProfileService { } // get all family background - if(data['data']['family_background'] != null){ - data['data']['family_background'].forEach((var family){ - FamilyBackground familyBackground = FamilyBackground.fromJson(family); - families.add(familyBackground); - }); - } + // if(data['data']['family_background'] != null){ + // data['data']['family_background'].forEach((var family){ + // FamilyBackground familyBackground = FamilyBackground.fromJson(family); + // families.add(familyBackground); + // }); + // } //get all eligibilities - if (data['data']['eligibilities'] != null) { - data['data']['eligibilities']!.forEach((var cert) { - EligibityCert eligibility = EligibityCert.fromJson(cert); - eligibilities.add(eligibility); - }); - } + // if (data['data']['eligibilities'] != null) { + // data['data']['eligibilities']!.forEach((var cert) { + // EligibityCert eligibility = EligibityCert.fromJson(cert); + // eligibilities.add(eligibility); + // }); + // } // get all citizenships - if (data['data']['citizenship'] != null) { - data['data']['citizenships']!.forEach((var citizenship) { - Citizenship person = Citizenship.fromJson(citizenship); - citizenships.add(person); - }); - } + // if (data['data']['citizenship'] != null) { + // data['data']['citizenships']!.forEach((var citizenship) { + // Citizenship person = Citizenship.fromJson(citizenship); + // citizenships.add(person); + // }); + // } // get all references; - if (data['data']['personal_references'] != null) { - data['data']['personal_references'].forEach((var person) { - PersonalReference reference = PersonalReference.fromJson(person); - references.add(reference); - }); - } + // if (data['data']['personal_references'] != null) { + // data['data']['personal_references'].forEach((var person) { + // PersonalReference reference = PersonalReference.fromJson(person); + // references.add(reference); + // }); + // } //get all learning and developments - if (data['data']['learning_development'] != null) { - data['data']['learning_development'].forEach((var training) { - LearningDevelopement learnings = - LearningDevelopement.fromJson(training); - learningsDevelopments.add(learnings); - }); - } + // if (data['data']['learning_development'] != null) { + // data['data']['learning_development'].forEach((var training) { + // LearningDevelopement learnings = + // LearningDevelopement.fromJson(training); + // learningsDevelopments.add(learnings); + // }); + // } //get all educational background - if (data['data']['education_background'] != null) { - data['data']['education_background'].forEach((var education) { - EducationalBackground educationalBackground = - EducationalBackground.fromJson(education); - educationalBackgrounds.add(educationalBackground); - }); - } + // if (data['data']['education_background'] != null) { + // data['data']['education_background'].forEach((var education) { + // EducationalBackground educationalBackground = + // EducationalBackground.fromJson(education); + // educationalBackgrounds.add(educationalBackground); + // }); + // } // get all work history - if (data['data']['work_experiences'] != null) { - data['data']['work_experiences'].forEach((var work) { - WorkHistory experience = WorkHistory.fromJson(work); - workExperiences.add(experience); - }); - } + // if (data['data']['work_experiences'] != null) { + // data['data']['work_experiences'].forEach((var work) { + // WorkHistory experience = WorkHistory.fromJson(work); + // workExperiences.add(experience); + // }); + // } // get all voluntary works - if (data['data']['voluntary_works'] != null) { - data['data']['voluntary_works'].forEach((var work) { - VoluntaryWork vwork = VoluntaryWork.fromJson(work); - voluntaryWorks.add(vwork); - }); - } + // if (data['data']['voluntary_works'] != null) { + // data['data']['voluntary_works'].forEach((var work) { + // VoluntaryWork vwork = VoluntaryWork.fromJson(work); + // voluntaryWorks.add(vwork); + // }); + // } // get all hobbies - if (data['data']['other_information']['skills_hobbies'] != null) { - data['data']['other_information']['skills_hobbies'] - .forEach((var skills_hobbies) { - SkillsHobbies skillsAndHobbies = - SkillsHobbies.fromJson(skills_hobbies); - skillsHobbies.add(skillsAndHobbies); - }); - } + // if (data['data']['other_information']['skills_hobbies'] != null) { + // data['data']['other_information']['skills_hobbies'] + // .forEach((var skills_hobbies) { + // SkillsHobbies skillsAndHobbies = + // SkillsHobbies.fromJson(skills_hobbies); + // skillsHobbies.add(skillsAndHobbies); + // }); + // } //get all organization memberships - if (data['data']['other_information']['organization_memberships'] != - null) { - data['data']['other_information']['organization_memberships'] - .forEach((var org) { - OrganizationMembership organization = - OrganizationMembership.fromJson(org); - orgMemberships.add(organization); - }); - } + // if (data['data']['other_information']['organization_memberships'] != + // null) { + // data['data']['other_information']['organization_memberships'] + // .forEach((var org) { + // OrganizationMembership organization = + // OrganizationMembership.fromJson(org); + // orgMemberships.add(organization); + // }); + // } //get all non academic recognition - if (data['data']['other_information']['non_academic_records'] != null) { - data['data']['other_information']['non_academic_records'] - .forEach((var recognition) { - NonAcademicRecognition nonAcademicRecognition = - NonAcademicRecognition.fromJson(recognition); - nonAcademicRecognitions.add(nonAcademicRecognition); - }); - } + // if (data['data']['other_information']['non_academic_records'] != null) { + // data['data']['other_information']['non_academic_records'] + // .forEach((var recognition) { + // NonAcademicRecognition nonAcademicRecognition = + // NonAcademicRecognition.fromJson(recognition); + // nonAcademicRecognitions.add(nonAcademicRecognition); + // }); + // } BasicInfo basicInfo = BasicInfo( contactInformation: contactInformation, @@ -193,20 +194,21 @@ class ProfileService { identifications: identificationInformation, citizenships: citizenships, addresses: addresses); - OtherInformation otherInformation = OtherInformation( - skillsAndHobbies: skillsHobbies, - orgMemberships: orgMemberships, - nonAcademicRecognition: nonAcademicRecognitions); + // OtherInformation otherInformation = OtherInformation( + // skillsAndHobbies: skillsHobbies, + // orgMemberships: orgMemberships, + // nonAcademicRecognition: nonAcademicRecognitions); ProfileInformation profileInformation = ProfileInformation( - families: families, - otherInformation: otherInformation, - workExperiences: workExperiences, + // families: families, + // otherInformation: otherInformation, + // workExperiences: workExperiences, basicInfo: basicInfo, - eligibilities: eligibilities, - references: references, - learningsAndDevelopment: learningsDevelopments, - educationalBackgrounds: educationalBackgrounds, - voluntaryWorks: voluntaryWorks); + // eligibilities: eligibilities, + // references: references, + // learningsAndDevelopment: learningsDevelopments, + // educationalBackgrounds: educationalBackgrounds, + // voluntaryWorks: voluntaryWorks + ); profileInformation0 = profileInformation; } // }catch(e){ @@ -215,3 +217,5 @@ class ProfileService { return profileInformation0; } } + + diff --git a/lib/utils/alerts.dart b/lib/utils/alerts.dart index 0994f39..3517fb7 100644 --- a/lib/utils/alerts.dart +++ b/lib/utils/alerts.dart @@ -58,6 +58,6 @@ successAlert(context, title, description,Function() func) { headerAnimationLoop: false, title: title, desc: description, - btnOk: SizedBox(height: 50,child: ElevatedButton(style: mainBtnStyle(primary, Colors.transparent, second), onPressed: func, child: const Text("OK")), ) + btnOk: SizedBox(height: 50,child: ElevatedButton(style: mainBtnStyle(success2, Colors.transparent, success), onPressed: func, child: const Text("OK")), ) ).show(); } diff --git a/lib/utils/request.dart b/lib/utils/request.dart index b99eabf..c224af0 100644 --- a/lib/utils/request.dart +++ b/lib/utils/request.dart @@ -97,46 +97,86 @@ class Request { return response; } + Future putRequest( + {required String path, + required Map? headers, + required Map? body, + required Map? param}) async { + Response response; + try { + response =await put(Uri.http(host,path,param),headers: headers,body: jsonEncode(body)); + } on TimeoutException catch (_) { + Fluttertoast.showToast( + msg: timeoutError, + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.black, + ); + throw (timeoutError); + } on SocketException catch (_) { + Fluttertoast.showToast( + msg: timeoutError, + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.black, + ); + throw (timeoutError); + } on FormatException catch (_) { + throw const FormatException(formatError); + } on HttpException catch (_) { + throw const HttpException(httpError); + } on Error catch (e) { + debugPrint("post request error: $e"); + Fluttertoast.showToast( + msg: onError, + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.black, + ); + throw (e.toString()); + } + return response; + } + Future deleteRequest( {required String path, required Map? headers, required Map? body, required Map? param}) async { Response response; - // try { + try { response = await delete(Uri.http(host, path, param), headers: headers, body: jsonEncode(body)) .timeout(Duration(seconds: requestTimeout)); - // } on TimeoutException catch (_) { - // Fluttertoast.showToast( - // msg: timeoutError, - // toastLength: Toast.LENGTH_LONG, - // gravity: ToastGravity.BOTTOM, - // backgroundColor: Colors.black, - // ); - // throw (timeoutError); - // } on SocketException catch (_) { - // Fluttertoast.showToast( - // msg: timeoutError, - // toastLength: Toast.LENGTH_LONG, - // gravity: ToastGravity.BOTTOM, - // backgroundColor: Colors.black, - // ); - // throw (timeoutError); - // } on FormatException catch (_) { - // throw const FormatException(formatError); - // } on HttpException catch (_) { - // throw const HttpException(httpError); - // } on Error catch (e) { - // debugPrint("post request error: $e"); - // Fluttertoast.showToast( - // msg: onError, - // toastLength: Toast.LENGTH_LONG, - // gravity: ToastGravity.BOTTOM, - // backgroundColor: Colors.black, - // ); - // throw (e.toString()); - // } + } on TimeoutException catch (_) { + Fluttertoast.showToast( + msg: timeoutError, + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.black, + ); + throw (timeoutError); + } on SocketException catch (_) { + Fluttertoast.showToast( + msg: timeoutError, + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.black, + ); + throw (timeoutError); + } on FormatException catch (_) { + throw const FormatException(formatError); + } on HttpException catch (_) { + throw const HttpException(httpError); + } on Error catch (e) { + Fluttertoast.showToast( + msg: onError, + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.black, + ); + throw (e.toString()); + } return response; } } diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index 953752f..6182792 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -3,10 +3,11 @@ class Url { static Url get instance => _instance; String host() { - // return '192.168.10.221:3003'; + // // return '192.168.10.221:3003'; // return 'agusandelnorte.gov.ph'; // return "192.168.10.219:3000"; - return 'devweb.agusandelnorte.gov.ph'; + return "devweb.agusandelnorte.gov.ph"; + // return 'devapi.agusandelnorte.gov.ph:3004'; } String authentication() { @@ -14,23 +15,33 @@ class Url { } String profileInformation(){ - return '/api/jobnet_app/profile/pds/'; + return 'api/jobnet_app/profile/pds/'; } String latestApk(){ return "/api/system_app/apk_version/latest"; } +////ELIGIBILITIES PATHS String eligibilities(){ return "/api/jobnet_app/eligibilities/"; } +String getEligibilities(){ + return "/api/jobnet_app/profile/pds/eligibility/"; +} + String addEligibility(){ return "/api/jobnet_app/profile/pds/eligibility/"; } String deleteEligibility(){ return "/api/jobnet_app/profile/pds/eligibility/"; } + +String updateEligibility(){ + return "/api/jobnet_app/profile/pds/eligibility/"; +} + // location utils path String getCounties(){ return "/api/jobnet_app/countries/"; diff --git a/lib/widgets/add_leading.dart b/lib/widgets/Leadings/add_leading.dart similarity index 100% rename from lib/widgets/add_leading.dart rename to lib/widgets/Leadings/add_leading.dart diff --git a/lib/widgets/Leadings/close_leading.dart b/lib/widgets/Leadings/close_leading.dart new file mode 100644 index 0000000..f4ea312 --- /dev/null +++ b/lib/widgets/Leadings/close_leading.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; + +class CloseLeading extends StatelessWidget { + final Function() onPressed; + const CloseLeading({super.key, required this.onPressed}); + + @override + Widget build(BuildContext context) { + return IconButton(onPressed: onPressed, icon: const Icon(Icons.close)); + } +} \ No newline at end of file diff --git a/macos/Podfile.lock b/macos/Podfile.lock index aa74d3f..42d76d9 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -3,6 +3,8 @@ PODS: - FMDB (2.7.5): - FMDB/standard (= 2.7.5) - FMDB/standard (2.7.5) + - modal_progress_hud_nsn (0.0.1): + - FlutterMacOS - package_info_plus (0.0.1): - FlutterMacOS - path_provider_foundation (0.0.1): @@ -17,6 +19,7 @@ PODS: DEPENDENCIES: - FlutterMacOS (from `Flutter/ephemeral`) + - modal_progress_hud_nsn (from `Flutter/ephemeral/.symlinks/plugins/modal_progress_hud_nsn/macos`) - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/macos`) - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos`) @@ -29,6 +32,8 @@ SPEC REPOS: EXTERNAL SOURCES: FlutterMacOS: :path: Flutter/ephemeral + modal_progress_hud_nsn: + :path: Flutter/ephemeral/.symlinks/plugins/modal_progress_hud_nsn/macos package_info_plus: :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos path_provider_foundation: @@ -41,6 +46,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a + modal_progress_hud_nsn: 8099d46c2cf9de7af8fe0a3f8f5d2aa32cf956c3 package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852 shared_preferences_foundation: 297b3ebca31b34ec92be11acd7fb0ba932c822ca From fba53ce2dc9103d4aa95d52f772d75c68f8da12a Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Thu, 2 Mar 2023 13:28:33 +0800 Subject: [PATCH 41/86] wotk history refactor and create its own Bloc --- lib/bloc/eligibility/eligibility_bloc.dart | 214 +++++ lib/bloc/eligibility/eligibility_event.dart | 72 ++ lib/bloc/eligibility/eligibility_state.dart | 96 ++ lib/bloc/profile/profile_bloc.dart | 206 ----- lib/bloc/profile/profile_event.dart | 60 -- lib/bloc/profile/profile_state.dart | 72 -- lib/bloc/workHistory/workHistory_bloc.dart | 23 + lib/bloc/workHistory/workHistory_event.dart | 20 + lib/bloc/workHistory/workHistory_state.dart | 28 + lib/main.dart | 4 + lib/model/profile/work_history.dart | 2 +- .../components/eligibility/add_modal.dart | 768 ++++++++-------- .../components/eligibility/edit_modal.dart | 863 +++++++++--------- .../components/eligibility_screen.dart | 529 ++++++----- .../non_academic_recognition_screen.dart | 2 - .../components/work_history_screen.dart | 202 ++-- lib/screens/profile/profile.dart | 30 +- lib/sevices/profile/education_services.dart | 0 lib/sevices/profile/eligibility_services.dart | 8 +- .../profile/work_history_services.dart | 39 + lib/utils/app_router.dart | 10 +- lib/utils/urls.dart | 4 + 22 files changed, 1797 insertions(+), 1455 deletions(-) create mode 100644 lib/bloc/eligibility/eligibility_bloc.dart create mode 100644 lib/bloc/eligibility/eligibility_event.dart create mode 100644 lib/bloc/eligibility/eligibility_state.dart create mode 100644 lib/bloc/workHistory/workHistory_bloc.dart create mode 100644 lib/bloc/workHistory/workHistory_event.dart create mode 100644 lib/bloc/workHistory/workHistory_state.dart create mode 100644 lib/sevices/profile/education_services.dart create mode 100644 lib/sevices/profile/work_history_services.dart diff --git a/lib/bloc/eligibility/eligibility_bloc.dart b/lib/bloc/eligibility/eligibility_bloc.dart new file mode 100644 index 0000000..98de16c --- /dev/null +++ b/lib/bloc/eligibility/eligibility_bloc.dart @@ -0,0 +1,214 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import '../../model/location/city.dart'; +import '../../model/location/country.dart'; +import '../../model/location/provinces.dart'; +import '../../model/location/region.dart'; +import '../../model/profile/eligibility.dart'; +import '../../model/utils/eligibility.dart'; +import '../../sevices/profile/eligibility_services.dart'; +import '../../utils/location_utilities.dart'; +import '../../utils/profile_utilities.dart'; +part 'eligibility_event.dart'; +part 'eligibility_state.dart'; + +class EligibilityBloc extends Bloc { + EligibilityBloc() : super(EligibilityInitial()) { + List? globalCountries; + List? globalRegions; + List? globalEligibilities; + List? eligibilities; +////===================================================================== + on((event, emit) { + emit(EligibilityLoadingState()); + eligibilities = event.eligibilities; + emit(EligibilityLoaded(eligibilities: event.eligibilities)); + }); +////==================================================================== + on((event, emit) async { + try { + if (eligibilities != null) { + emit(EligibilityLoaded(eligibilities: eligibilities!)); + } else { + emit(EligibilityLoadingState()); + eligibilities = await EligibilityService.instance + .getEligibilities(event.profileId, event.token); + emit(EligibilityLoaded(eligibilities: eligibilities!)); + } + } catch (e) { + emit(EligibilityErrorState(message: e.toString())); + } + }); +////==================================================================== + on((event, emit) async { + try { + emit(EligibilityLoadingState()); + if (globalCountries == null) { + List countries = await LocationUtils.instance.getCountries(); + globalCountries = countries; + } + if (globalRegions == null) { + List regions = await LocationUtils.instance.getRegions(); + globalRegions = regions; + } + if (globalEligibilities == null) { + List eligibilities = + await ProfileUtilities.instance.getEligibilities(); + globalEligibilities = eligibilities; + } + Eligibility currentEligibility = globalEligibilities!.firstWhere( + (Eligibility eligibility) => + event.eligibityCert.eligibility!.id == eligibility.id); + bool? isOverseas = event.eligibityCert.overseas; + Country currentCountry = globalCountries!.firstWhere( + (Country country) => + event.eligibityCert.examAddress!.country!.code == country.code); + if (event.eligibityCert.examAddress?.cityMunicipality?.province + ?.region != + null) { + Region currrentRegion = globalRegions!.firstWhere((Region region) => + event.eligibityCert.examAddress!.cityMunicipality!.province! + .region!.code == + region.code); + List provinces = await LocationUtils.instance + .getProvinces(regionCode: currrentRegion.code.toString()); + Province currentProvince = provinces.firstWhere((Province province) => + event.eligibityCert.examAddress!.cityMunicipality!.province! + .code == + province.code); + List cities = await LocationUtils.instance + .getCities(code: currentProvince.code.toString()); + CityMunicipality currentCity = cities.firstWhere( + (CityMunicipality cityMunicipality) => + event.eligibityCert.examAddress!.cityMunicipality!.code == + cityMunicipality.code); + + emit(EditEligibilityState( + currentCity: currentCity, + selectedCountry: currentCountry, + currentProvince: currentProvince, + currentRegion: currrentRegion, + currentEligibility: currentEligibility, + provinces: provinces, + cities: cities, + isOverseas: isOverseas!, + eligibityCert: event.eligibityCert, + countries: globalCountries!, + regions: globalRegions!, + eligibilities: globalEligibilities!)); + } else { + emit(EditEligibilityState( + selectedCountry: currentCountry, + currentCity: null, + currentProvince: null, + currentRegion: null, + provinces: null, + cities: null, + currentEligibility: currentEligibility, + isOverseas: isOverseas!, + eligibityCert: event.eligibityCert, + countries: globalCountries!, + regions: globalRegions!, + eligibilities: globalEligibilities!)); + } + } catch (e) { + emit(EligibilityErrorState(message: e.toString())); + } + }); + + ////==================================================================== + on((event, emit) async { + try { + emit(EligibilityLoadingState()); + Map status = await EligibilityService.instance.update( + eligibityCert: event.eligibityCert, + token: event.token, + profileId: int.parse(event.profileId), + oldEligibility: event.oldEligibility); + if (status['success']) { + EligibityCert newEligibility = EligibityCert.fromJson(status['data']); + eligibilities!.removeWhere( + (EligibityCert element) => element.id == event.eligibityCert.id); + eligibilities!.add(newEligibility); + emit(EligibilityEditedState( + eligibilities: eligibilities!, response: status)); + } else { + emit(EligibilityEditedState( + eligibilities: eligibilities!, response: status)); + } + } catch (e) { + emit(EligibilityErrorState(message: e.toString())); + } + }); + on((event, emit) async { + emit(EligibilityLoadingState()); + if (globalRegions == null) { + List regions = await LocationUtils.instance.getRegions(); + globalRegions = regions; + } + if (globalEligibilities == null) { + List eligibilities = + await ProfileUtilities.instance.getEligibilities(); + globalEligibilities = eligibilities; + } + if (globalCountries == null) { + List countries = await LocationUtils.instance.getCountries(); + globalCountries = countries; + } + + emit(AddEligibilityState( + eligibilities: globalEligibilities!, + regions: globalRegions!, + countries: globalCountries!)); + }); + ////==================================================================== + on((event, emit) async { + emit(EligibilityLoadingState()); + try { + final bool success = await EligibilityService.instance.delete( + eligibilityId: event.eligibilityId, + profileId: int.parse(event.profileId), + token: event.token); + if (success) { + event.eligibilities.removeWhere( + ((EligibityCert element) => element.id == event.eligibilityId)); + List eligibilities = event.eligibilities; + emit(DeletedState(success: success, eligibilities: eligibilities)); + } else { + emit(DeletedState( + success: success, eligibilities: event.eligibilities)); + } + } catch (e) { + emit(EligibilityErrorState(message: e.toString())); + } + }); + ////==================================================================== + on( + (event, emit) async { + try { + emit(EligibilityLoadingState()); + Map status = await EligibilityService.instance.add( + eligibityCert: event.eligibityCert, + token: event.token, + profileId: int.parse(event.profileId)); + if (status['success']) { + EligibityCert? eligibityCert = + EligibityCert.fromJson(status['data']); + eligibilities!.add(eligibityCert); + emit(EligibilityAddedState( + eligibilities: eligibilities!, response: status)); + } else { + emit(EligibilityAddedState( + eligibilities: eligibilities!, response: status)); + } + } catch (e) { + emit(EligibilityErrorState(message: e.toString())); + } + }, + ); + on((event, emit) { + emit(const EligibilityErrorState( + message: "Something went wrong. Please try again")); + }); + } +} diff --git a/lib/bloc/eligibility/eligibility_event.dart b/lib/bloc/eligibility/eligibility_event.dart new file mode 100644 index 0000000..381ffd8 --- /dev/null +++ b/lib/bloc/eligibility/eligibility_event.dart @@ -0,0 +1,72 @@ +part of 'eligibility_bloc.dart'; + +abstract class EligibilityEvent extends Equatable { + const EligibilityEvent(); + + @override + List get props => []; +} + +class ShowAddEligibilityForm extends EligibilityEvent { + +} + +class GetEligibilities extends EligibilityEvent{ + final int profileId; + final String token; + const GetEligibilities({required this.profileId, required this.token}); + @override + List get props => [profileId,token]; +} + +class AddEligibility extends EligibilityEvent{ + final EligibityCert eligibityCert; + final String profileId; + final String token; + const AddEligibility({required this.eligibityCert, required this.profileId, required this.token}); + @override + List get props => [eligibityCert, profileId, token]; +} +class UpdateEligibility extends EligibilityEvent{ + final EligibityCert eligibityCert; + final String profileId; + final String token; + final int oldEligibility; + const UpdateEligibility({required this.eligibityCert, required this.oldEligibility,required this.profileId, required this.token}); + + @override + List get props =>[eligibityCert,profileId,token,oldEligibility]; +} +class LoadEligibility extends EligibilityEvent { + final List eligibilities; + const LoadEligibility({required this.eligibilities}); + @override + List get props => []; +} + +class ShowEditEligibilityForm extends EligibilityEvent { + final EligibityCert eligibityCert; + const ShowEditEligibilityForm({required this.eligibityCert}); + @override + List get props => []; +} + +class DeleteEligibility extends EligibilityEvent { + final List eligibilities; + final String profileId; + final int eligibilityId; + final String token; + const DeleteEligibility( + {required this.eligibilities, + required this.eligibilityId, + required this.profileId, + required this.token}); + @override + List get props => [eligibilities, profileId, eligibilityId, token]; +} + +class CallErrorState extends EligibilityEvent{ + +} + + diff --git a/lib/bloc/eligibility/eligibility_state.dart b/lib/bloc/eligibility/eligibility_state.dart new file mode 100644 index 0000000..63421db --- /dev/null +++ b/lib/bloc/eligibility/eligibility_state.dart @@ -0,0 +1,96 @@ +part of 'eligibility_bloc.dart'; + +abstract class EligibilityState extends Equatable { + const EligibilityState(); + + @override + List get props => []; +} + +class EligibilityInitial extends EligibilityState {} + + +class EditEligibilityState extends EligibilityState { + final EligibityCert eligibityCert; + final List eligibilities; + final List countries; + final List regions; + final List? provinces; + final List? cities; + final bool isOverseas; + final Eligibility currentEligibility; + final Region? currentRegion; + final Province? currentProvince; + final CityMunicipality? currentCity; + final Country selectedCountry; + const EditEligibilityState({ + required this.provinces, + required this.cities, + required this.currentProvince, + required this.currentCity, + required this.currentRegion, + required this.currentEligibility, + required this.isOverseas, + required this.eligibityCert, + required this.eligibilities, + required this.countries, + required this.regions, + required this.selectedCountry, + }); + @override + List get props => + [isOverseas, eligibityCert, eligibilities, regions, countries]; +} + +class DeletedState extends EligibilityState { + final List eligibilities; + final bool success; + const DeletedState({required this.eligibilities, required this.success}); + @override + List get props => [success, eligibilities]; +} + +class AddEligibilityState extends EligibilityState { + final List eligibilities; + final List countries; + final List regions; + const AddEligibilityState({ + required this.eligibilities, + required this.countries, + required this.regions, + }); + @override + List get props => [eligibilities,countries,regions]; +} +class EligibilityEditedState extends EligibilityState{ + final List eligibilities; + final Map response; + const EligibilityEditedState({required this.eligibilities, required this.response}); + @override + List get props =>[eligibilities, response]; +} + +class EligibilityAddedState extends EligibilityState{ + final List eligibilities; + final Map response; + + const EligibilityAddedState({required this.eligibilities, required this.response}); + @override + List get props =>[eligibilities,response]; +} +class EligibilityLoadingState extends EligibilityState{ + +} +class EligibilityErrorState extends EligibilityState{ + final String message; + const EligibilityErrorState({required this.message}); + @override + List get props =>[message]; +} + +class EligibilityLoaded extends EligibilityState { + final List eligibilities; + const EligibilityLoaded({required this.eligibilities}); + @override + List get props => [eligibilities]; +} diff --git a/lib/bloc/profile/profile_bloc.dart b/lib/bloc/profile/profile_bloc.dart index d6c2c50..6316746 100644 --- a/lib/bloc/profile/profile_bloc.dart +++ b/lib/bloc/profile/profile_bloc.dart @@ -1,27 +1,13 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; -import 'package:unit2/model/profile/eligibility.dart'; import 'package:unit2/model/profile/profileInfomation.dart'; -import 'package:unit2/model/utils/eligibility.dart'; -import 'package:unit2/sevices/profile/eligibility_services.dart'; import 'package:unit2/sevices/profile/profile_service.dart'; -import 'package:unit2/utils/location_utilities.dart'; -import 'package:unit2/utils/profile_utilities.dart'; -import '../../model/location/country.dart'; -import '../../model/location/region.dart'; -import '../../model/location/provinces.dart'; -import '../../model/location/city.dart'; part 'profile_event.dart'; part 'profile_state.dart'; class ProfileBloc extends Bloc { ProfileBloc() : super(ProfileInitial()) { ProfileInformation? globalProfileInformation; - List? globalCountries; - List? globalRegions; - List? globalEligibilities; - List? eligibilities; - ////========================================================================= on((event, emit) async { try { emit(ProfileLoading()); @@ -34,198 +20,6 @@ class ProfileBloc extends Bloc { } }); - on((event, emit) { - emit(const ProfileErrorState( - mesage: "Something went wrong. Please try again")); - }); -////===================================================================== - on((event, emit) { - emit(ProfileLoading()); - eligibilities = event.eligibilities; - emit(EligibilityLoaded(eligibilities: event.eligibilities)); - }); -////==================================================================== -on((event,emit)async{ - - print(eligibilities?.length); - try{ - if(eligibilities != null){ - emit(EligibilityLoaded(eligibilities: eligibilities!)); - }else{ - emit(ProfileLoading()); - eligibilities = await EligibilityService.instance.getEligibilities(event.profileId, event.token); - emit(EligibilityLoaded(eligibilities: eligibilities!)); - } - }catch(e){ - emit(ProfileErrorState(mesage: e.toString())); - } -}); -////==================================================================== - on((event, emit) async { - try { - emit(ProfileLoading()); - if (globalCountries == null) { - List countries = await LocationUtils.instance.getCountries(); - globalCountries = countries; - } - if (globalRegions == null) { - List regions = await LocationUtils.instance.getRegions(); - globalRegions = regions; - } - if (globalEligibilities == null) { - List eligibilities = - await ProfileUtilities.instance.getEligibilities(); - globalEligibilities = eligibilities; - } - Eligibility currentEligibility = globalEligibilities!.firstWhere( - (Eligibility eligibility) => - event.eligibityCert.eligibility!.id == eligibility.id); - bool? isOverseas = event.eligibityCert.overseas; - Country currentCountry = globalCountries!.firstWhere( - (Country country) => - event.eligibityCert.examAddress!.country!.code == country.code); - if (event.eligibityCert.examAddress?.cityMunicipality?.province - ?.region != - null) { - Region currrentRegion = globalRegions!.firstWhere((Region region) => - event.eligibityCert.examAddress!.cityMunicipality!.province! - .region!.code == - region.code); - List provinces = await LocationUtils.instance - .getProvinces(regionCode: currrentRegion.code.toString()); - Province currentProvince = provinces.firstWhere((Province province) => - event.eligibityCert.examAddress!.cityMunicipality!.province! - .code == - province.code); - List cities = await LocationUtils.instance - .getCities(code: currentProvince.code.toString()); - CityMunicipality currentCity = cities.firstWhere( - (CityMunicipality cityMunicipality) => - event.eligibityCert.examAddress!.cityMunicipality!.code == - cityMunicipality.code); - - emit(EditEligibilityState( - currentCity: currentCity, - selectedCountry: currentCountry, - currentProvince: currentProvince, - currentRegion: currrentRegion, - currentEligibility: currentEligibility, - provinces: provinces, - cities: cities, - isOverseas: isOverseas!, - eligibityCert: event.eligibityCert, - countries: globalCountries!, - regions: globalRegions!, - eligibilities: globalEligibilities!)); - } else { - emit(EditEligibilityState( - selectedCountry: currentCountry, - currentCity: null, - currentProvince: null, - currentRegion: null, - provinces: null, - cities: null, - currentEligibility: currentEligibility, - isOverseas: isOverseas!, - eligibityCert: event.eligibityCert, - countries: globalCountries!, - regions: globalRegions!, - eligibilities: globalEligibilities!)); - } - } catch (e) { - emit(ProfileErrorState(mesage: e.toString())); - } - }); - ////==================================================================== - on((event, emit) async { - try { - emit(ProfileLoading()); - Map status = await EligibilityService.instance.update( - eligibityCert: event.eligibityCert, - token: event.token, - profileId: int.parse(event.profileId), - oldEligibility: event.oldEligibility); - if (status['success']) { - EligibityCert newEligibility = EligibityCert.fromJson(status['data']); - eligibilities!.removeWhere( - (EligibityCert element) => element.id == event.eligibityCert.id); - eligibilities!.add(newEligibility); - emit(EligibilityEditedState( - eligibilities: eligibilities!, response: status)); - } else { - emit(EligibilityEditedState( - eligibilities: eligibilities!, response: status)); - } - } catch (e) { - emit(ProfileErrorState(mesage: e.toString())); - } - }); - on((event, emit) async { - emit(ProfileLoading()); - if (globalRegions == null) { - List regions = await LocationUtils.instance.getRegions(); - globalRegions = regions; - } - if (globalEligibilities == null) { - List eligibilities = - await ProfileUtilities.instance.getEligibilities(); - globalEligibilities = eligibilities; - } - if (globalCountries == null) { - List countries = await LocationUtils.instance.getCountries(); - globalCountries = countries; - } - - emit(AddEligibilityState( - eligibilities: globalEligibilities!, - regions: globalRegions!, - countries: globalCountries!)); - }); - ////==================================================================== - on((event, emit) async { - emit(ProfileLoading()); - try { - final bool success = await EligibilityService.instance.delete( - eligibilityId: event.eligibilityId, - profileId: int.parse(event.profileId), - token: event.token); - if (success) { - event.eligibilities.removeWhere( - ((EligibityCert element) => element.id == event.eligibilityId)); - List eligibilities = event.eligibilities; - emit(DeletedState(success: success, eligibilities: eligibilities)); - } else { - emit(DeletedState( - success: success, eligibilities: event.eligibilities)); - } - } catch (e) { - emit(ProfileErrorState(mesage: e.toString())); - } - }); - ////==================================================================== - on( - (event, emit) async { - try { - emit(ProfileLoading()); - Map status = await EligibilityService.instance.add( - eligibityCert: event.eligibityCert, - token: event.token, - profileId: int.parse(event.profileId)); - if (status['success']) { - EligibityCert? eligibityCert = - EligibityCert.fromJson(status['data']); - eligibilities!.add(eligibityCert); - emit(EligibilityAddedState( - eligibilities: eligibilities!, response: status)); - } else { - emit(EligibilityAddedState( - eligibilities: eligibilities!, response: status)); - } - } catch (e) { - emit(ProfileErrorState(mesage: e.toString())); - } - }, - ); } } diff --git a/lib/bloc/profile/profile_event.dart b/lib/bloc/profile/profile_event.dart index 5951ff8..218cb3e 100644 --- a/lib/bloc/profile/profile_event.dart +++ b/lib/bloc/profile/profile_event.dart @@ -20,65 +20,5 @@ class LoadProfileInformation extends ProfileEvent { List get props => []; } -class LoadEligibility extends ProfileEvent { - final List eligibilities; - const LoadEligibility({required this.eligibilities}); - @override - List get props => []; -} -class ShowEditEligibilityForm extends ProfileEvent { - final EligibityCert eligibityCert; - const ShowEditEligibilityForm({required this.eligibityCert}); - @override - List get props => []; -} -class DeleteEligibility extends ProfileEvent { - final List eligibilities; - final String profileId; - final int eligibilityId; - final String token; - const DeleteEligibility( - {required this.eligibilities, - required this.eligibilityId, - required this.profileId, - required this.token}); - @override - List get props => [eligibilities, profileId, eligibilityId, token]; -} - -class ShowAddEligibilityForm extends ProfileEvent { - -} - -class GetEligibilities extends ProfileEvent{ - final int profileId; - final String token; - const GetEligibilities({required this.profileId, required this.token}); - @override - List get props => [profileId,token]; -} - -class AddEligibility extends ProfileEvent{ - final EligibityCert eligibityCert; - final String profileId; - final String token; - const AddEligibility({required this.eligibityCert, required this.profileId, required this.token}); - @override - List get props => [eligibityCert, profileId, token]; -} -class UpdateEligibility extends ProfileEvent{ - final EligibityCert eligibityCert; - final String profileId; - final String token; - final int oldEligibility; - const UpdateEligibility({required this.eligibityCert, required this.oldEligibility,required this.profileId, required this.token}); - - @override - List get props =>[eligibityCert,profileId,token,oldEligibility]; -} - -class CallErrorState extends ProfileEvent{ - -} diff --git a/lib/bloc/profile/profile_state.dart b/lib/bloc/profile/profile_state.dart index a311398..7c2bddb 100644 --- a/lib/bloc/profile/profile_state.dart +++ b/lib/bloc/profile/profile_state.dart @@ -25,77 +25,5 @@ class ProfileErrorState extends ProfileState { class ProfileLoading extends ProfileState {} -class EligibilityLoaded extends ProfileState { - final List eligibilities; - const EligibilityLoaded({required this.eligibilities}); - @override - List get props => [eligibilities]; -} -class EditEligibilityState extends ProfileState { - final EligibityCert eligibityCert; - final List eligibilities; - final List countries; - final List regions; - final List? provinces; - final List? cities; - final bool isOverseas; - final Eligibility currentEligibility; - final Region? currentRegion; - final Province? currentProvince; - final CityMunicipality? currentCity; - final Country selectedCountry; - const EditEligibilityState({ - required this.provinces, - required this.cities, - required this.currentProvince, - required this.currentCity, - required this.currentRegion, - required this.currentEligibility, - required this.isOverseas, - required this.eligibityCert, - required this.eligibilities, - required this.countries, - required this.regions, - required this.selectedCountry, - }); - @override - List get props => - [isOverseas, eligibityCert, eligibilities, regions, countries]; -} -class DeletedState extends ProfileState { - final List eligibilities; - final bool success; - const DeletedState({required this.eligibilities, required this.success}); - @override - List get props => [success, eligibilities]; -} -class AddEligibilityState extends ProfileState { - final List eligibilities; - final List countries; - final List regions; - const AddEligibilityState({ - required this.eligibilities, - required this.countries, - required this.regions, - }); - @override - List get props => [eligibilities,countries,regions]; -} -class EligibilityEditedState extends ProfileState{ - final List eligibilities; - final Map response; - const EligibilityEditedState({required this.eligibilities, required this.response}); - @override - List get props =>[eligibilities, response]; -} - -class EligibilityAddedState extends ProfileState{ - final List eligibilities; - final Map response; - - const EligibilityAddedState({required this.eligibilities, required this.response}); - @override - List get props =>[eligibilities,response]; -} diff --git a/lib/bloc/workHistory/workHistory_bloc.dart b/lib/bloc/workHistory/workHistory_bloc.dart new file mode 100644 index 0000000..3c8d4a5 --- /dev/null +++ b/lib/bloc/workHistory/workHistory_bloc.dart @@ -0,0 +1,23 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/model/profile/work_history.dart'; +import 'package:unit2/sevices/profile/work_history_services.dart'; + +part 'workHistory_event.dart'; +part 'workHistory_state.dart'; + +class WorkHistoryBloc extends Bloc { + List workExperiences = []; + WorkHistoryBloc() : super(EducationInitial()) { + on((event, emit)async { + emit(WorkHistoryLoadingState()); + // try{ + List works = await WorkHistoryService.instance.getWorkExperiences(event.profileId, event.token); + workExperiences = works; + emit(WorkHistoryLoaded(workExperiences: workExperiences)); + // }catch(e){ + // emit(WorkHistoryErrorState(message: e.toString())); + // } + }); + } +} diff --git a/lib/bloc/workHistory/workHistory_event.dart b/lib/bloc/workHistory/workHistory_event.dart new file mode 100644 index 0000000..7f3e066 --- /dev/null +++ b/lib/bloc/workHistory/workHistory_event.dart @@ -0,0 +1,20 @@ +part of 'workHistory_bloc.dart'; + +abstract class WorkHistorytEvent extends Equatable { + const WorkHistorytEvent(); + + @override + List get props => []; +} + +class GetWorkHistories extends WorkHistorytEvent{ + final int profileId; + final String token; + const GetWorkHistories({required this.profileId, required this.token}); + + + @override + List get props => [profileId, token]; +} + + diff --git a/lib/bloc/workHistory/workHistory_state.dart b/lib/bloc/workHistory/workHistory_state.dart new file mode 100644 index 0000000..21158a9 --- /dev/null +++ b/lib/bloc/workHistory/workHistory_state.dart @@ -0,0 +1,28 @@ +part of 'workHistory_bloc.dart'; + +abstract class WorkHistoryState extends Equatable { + const WorkHistoryState(); + + @override + List get props => []; +} + +class EducationInitial extends WorkHistoryState {} + +class WorkHistoryLoaded extends WorkHistoryState{ + final List workExperiences; + const WorkHistoryLoaded({required this.workExperiences}); + @override + List get props => [workExperiences]; +} + +class WorkHistoryLoadingState extends WorkHistoryState{ + +} +class WorkHistoryErrorState extends WorkHistoryState{ + final String message; + const WorkHistoryErrorState({required this.message}); + + @override + List get props => [message]; +} diff --git a/lib/main.dart b/lib/main.dart index d4dcacd..ce06e14 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:device_preview/device_preview.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/app_router.dart'; @@ -47,6 +48,9 @@ class MyApp extends StatelessWidget { BlocProvider( create: (_) => UserBloc(), ), + BlocProvider( + create: (_) => ProfileBloc(), + ), ], child: MaterialApp( navigatorKey: NavigationService.navigatorKey, diff --git a/lib/model/profile/work_history.dart b/lib/model/profile/work_history.dart index 687b1f7..821e603 100644 --- a/lib/model/profile/work_history.dart +++ b/lib/model/profile/work_history.dart @@ -35,7 +35,7 @@ class WorkHistory { final DateTime? fromDate; // final dynamic attachments; final int? salaryGrade; - final int? monthlySalary; + final double? monthlySalary; final String? appointmentStatus; factory WorkHistory.fromJson(Map json) => WorkHistory( diff --git a/lib/screens/profile/components/eligibility/add_modal.dart b/lib/screens/profile/components/eligibility/add_modal.dart index f57b2db..7e55df6 100644 --- a/lib/screens/profile/components/eligibility/add_modal.dart +++ b/lib/screens/profile/components/eligibility/add_modal.dart @@ -6,6 +6,7 @@ import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:intl/intl.dart'; import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; +import 'package:unit2/bloc/eligibility/eligibility_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/eligibility.dart'; @@ -60,403 +61,428 @@ class _AddEligibilityScreenState extends State { token = state.userData!.user!.login!.token; profileId = state.userData!.user!.login!.user!.profileId.toString(); return BlocBuilder( - buildWhen: (previous, current) { - if (state is EditEligibilityState) {} - return false; - }, builder: (context, state) { - //EDIT ELIGIBILITY STATE - if (state is AddEligibilityState) { - return ProgressHUD( - child: Center( - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 25, horizontal: 18), - child: FormBuilder( - key: formKey, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - //ELIGIBILITIES DROPDOWN - FormBuilderDropdown( - onChanged: (Eligibility? eligibility) { - selectedEligibility = eligibility; - }, - autovalidateMode: - AutovalidateMode.onUserInteraction, - validator: (value) => - value == null ? 'required' : null, - items: state.eligibilities - .map>( - (Eligibility eligibility) { - return DropdownMenuItem( - value: eligibility, - child: Text(eligibility.title)); - }).toList(), - name: "eligibility", - decoration: normalTextFieldStyle( - "Eligibility", "Eligibility")), - const SizedBox( - height: 20, - ), - - SizedBox( - width: screenWidth, - child: Row( - children: [ - //LICENSE NUMBER - Flexible( - flex: 1, - child: FormBuilderTextField( - onChanged: (value) { - license = value; - }, - name: 'license_number', - decoration: normalTextFieldStyle( - "license number", "license number"), - ), - ), - const SizedBox( - width: 12, - ), - //RATING - Flexible( - flex: 1, - child: FormBuilderTextField( - keyboardType: const TextInputType - .numberWithOptions(), - onChanged: (value) { - rating = value; - }, - name: 'rating', - decoration: normalTextFieldStyle( - 'rating %', 'rating'), - ), - ), - ], - ), - ), - const SizedBox( - height: 20, - ), - SizedBox( - width: screenWidth, - child: Row( - children: [ - //EXAM DATE - Flexible( - flex: 1, - child: DateTimePicker( - use24HourFormat: false, - icon: const Icon(Icons.date_range), - controller: examDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), - timeHintText: - "Date of Examination/Conferment", - decoration: normalTextFieldStyle( - "Exam date", "") - .copyWith( - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - )), - initialValue: null, - )), - const SizedBox( - width: 12, - ), - //VALIDITY DATE - Flexible( - flex: 1, - child: DateTimePicker( - controller: validityDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), - decoration: normalTextFieldStyle( - "Validity date", - "Validity date") - .copyWith( - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - )), - initialValue: null, - ), - ), - ], - ), - ), - const SizedBox( - height: 20, - ), - Text( - "Placement of Examination/Conferment", - style: Theme.of(context) - .textTheme - .displaySmall! - .copyWith(fontSize: blockSizeVertical * 2), - ), - const SizedBox( - height: 12, - ), - //OVERSEAS ADDRESS SWITCH - Column( + if(state is ProfileLoaded){ + return BlocBuilder( + buildWhen: (previous, current) { + if (state is EditEligibilityState) {} + return false; + }, + builder: (context, state) { + //EDIT ELIGIBILITY STATE + if (state is AddEligibilityState) { + return ProgressHUD( + child: Center( + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, horizontal: 18), + child: FormBuilder( + key: formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, children: [ - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - setState(() { - overseas = value; - }); - }, - decoration: normalTextFieldStyle("", ''), - name: 'overseas', - title: const Text("Overseas Address?"), + //ELIGIBILITIES DROPDOWN + FormBuilderDropdown( + onChanged: (Eligibility? eligibility) { + selectedEligibility = eligibility; + }, + autovalidateMode: + AutovalidateMode.onUserInteraction, + validator: (value) => + value == null ? 'required' : null, + items: state.eligibilities + .map>( + (Eligibility eligibility) { + return DropdownMenuItem( + value: eligibility, + child: Text(eligibility.title)); + }).toList(), + name: "eligibility", + decoration: normalTextFieldStyle( + "Eligibility", "Eligibility")), + const SizedBox( + height: 20, + ), + + SizedBox( + width: screenWidth, + child: Row( + children: [ + //LICENSE NUMBER + Flexible( + flex: 1, + child: FormBuilderTextField( + onChanged: (value) { + license = value; + }, + name: 'license_number', + decoration: normalTextFieldStyle( + "license number", + "license number"), + ), + ), + const SizedBox( + width: 12, + ), + //RATING + Flexible( + flex: 1, + child: FormBuilderTextField( + keyboardType: const TextInputType + .numberWithOptions(), + onChanged: (value) { + rating = value; + }, + name: 'rating', + decoration: normalTextFieldStyle( + 'rating %', 'rating'), + ), + ), + ], + ), ), const SizedBox( height: 20, ), - //COUNTRY DROPDOWN SizedBox( - child: overseas == true - ? FormBuilderDropdown( - initialValue: null, - validator: (value) => - value == null - ? 'required' - : null, - items: state.countries.map< - DropdownMenuItem< - Country>>( - (Country country) { - return DropdownMenuItem< - Country>( - value: country, - child: FittedBox( - child: Text( - country.name!))); - }).toList(), - name: 'country', + width: screenWidth, + child: Row( + children: [ + //EXAM DATE + Flexible( + flex: 1, + child: DateTimePicker( + use24HourFormat: false, + icon: + const Icon(Icons.date_range), + controller: examDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + timeHintText: + "Date of Examination/Conferment", decoration: normalTextFieldStyle( - "Country*", "Country"), - onChanged: (Country? value) { - selectedCountry = value; - }, - ) - : Column( - children: [ - //REGION DROPDOWN - FormBuilderDropdown( - autovalidateMode: - AutovalidateMode - .onUserInteraction, + "Exam date", "") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + initialValue: null, + )), + const SizedBox( + width: 12, + ), + //VALIDITY DATE + Flexible( + flex: 1, + child: DateTimePicker( + controller: validityDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + decoration: normalTextFieldStyle( + "Validity date", + "Validity date") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + initialValue: null, + ), + ), + ], + ), + ), + const SizedBox( + height: 20, + ), + Text( + "Placement of Examination/Conferment", + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith( + fontSize: blockSizeVertical * 2), + ), + const SizedBox( + height: 12, + ), + //OVERSEAS ADDRESS SWITCH + Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value; + }); + }, + decoration: + normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Overseas Address?"), + ), + const SizedBox( + height: 20, + ), + //COUNTRY DROPDOWN + SizedBox( + child: overseas == true + ? FormBuilderDropdown( + initialValue: null, validator: (value) => value == null ? 'required' : null, - onChanged: - (Region? region) async { - setState(() { - provinceCall = true; - }); - selectedRegion = region; - getProvinces(); - }, - initialValue: selectedRegion, + items: state.countries.map< + DropdownMenuItem< + Country>>( + (Country country) { + return DropdownMenuItem< + Country>( + value: country, + child: FittedBox( + child: Text(country + .name!))); + }).toList(), + name: 'country', decoration: normalTextFieldStyle( - "Region*", "Region"), - name: 'region', - items: state.regions.map< - DropdownMenuItem< - Region>>( - (Region region) { - return DropdownMenuItem< - Region>( - value: region, - child: Text(region - .description!)); - }).toList(), - ), - const SizedBox( - height: 20, - ), - //PROVINCE DROPDOWN - SizedBox( - height: 70, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: provinceCall, - child: DropdownButtonFormField< - Province?>( - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - value: selectedProvince, - onChanged: (Province? - province) { - setState(() { - cityCall = true; - }); - selectedProvince = - province; - getCities(); - }, - items: provinces == null - ? [] - : provinces!.map< - DropdownMenuItem< - Province>>( - (Province - province) { - return DropdownMenuItem( - value: - province, - child: - FittedBox( - child: Text( - province - .description!), - )); - }).toList(), - decoration: - normalTextFieldStyle( - "Province*", - "Province")), - ), - ), - - // CityMunicipalities dropdown - SizedBox( - height: 70, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: cityCall, - child: - DropdownButtonFormField< - CityMunicipality>( + "Country*", + "Country"), + onChanged: (Country? value) { + selectedCountry = value; + }, + ) + : Column( + children: [ + //REGION DROPDOWN + FormBuilderDropdown< + Region?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, validator: (value) => value == null ? 'required' : null, - isExpanded: true, - onChanged: - (CityMunicipality? - city) { - selectedMunicipality = - city; + onChanged: (Region? + region) async { + setState(() { + provinceCall = true; + }); + selectedRegion = region; + getProvinces(); }, + initialValue: + selectedRegion, decoration: normalTextFieldStyle( - "Municipality*", - "Municipality"), - value: - selectedMunicipality, - items: citymuns == null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality - c) { - return DropdownMenuItem( - value: c, - child: Text(c - .description!)); - }).toList(), + "Region*", + "Region"), + name: 'region', + items: state.regions.map< + DropdownMenuItem< + Region>>( + (Region region) { + return DropdownMenuItem< + Region>( + value: region, + child: Text(region + .description!)); + }).toList(), + ), + const SizedBox( + height: 20, + ), + //PROVINCE DROPDOWN + SizedBox( + height: 70, + child: ModalProgressHUD( + color: + Colors.transparent, + inAsyncCall: + provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + value: + selectedProvince, + onChanged: + (Province? + province) { + setState(() { + cityCall = true; + }); + selectedProvince = + province; + getCities(); + }, + items: provinces == + null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>((Province + province) { + return DropdownMenuItem( + value: + province, + child: + FittedBox( + child: + Text(province.description!), + )); + }).toList(), + decoration: + normalTextFieldStyle( + "Province*", + "Province")), + ), ), - ), - ), - const SizedBox( - height: 20, - ), - ], - )), - ], - ), - const Expanded( - child: SizedBox(), - ), + // CityMunicipalities dropdown + SizedBox( + height: 70, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: + DropdownButtonFormField< + CityMunicipality>( + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + onChanged: + (CityMunicipality? + city) { + selectedMunicipality = + city; + }, + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: + selectedMunicipality, + items: citymuns == + null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality + c) { + return DropdownMenuItem( + value: c, + child: Text( + c.description!)); + }).toList(), + ), + ), + ), + const SizedBox( + height: 20, + ), + ], + )), + ], + ), - SizedBox( - width: screenWidth, - height: 60, - child: ElevatedButton( - style: mainBtnStyle( - primary, Colors.transparent, second), - onPressed: () { - //rating - double? rate = rating == null - ? null - : double.parse(rating!); + const Expanded( + child: SizedBox(), + ), + + SizedBox( + width: screenWidth, + height: 60, + child: ElevatedButton( + style: mainBtnStyle(primary, + Colors.transparent, second), + onPressed: () { + //rating + double? rate = rating == null + ? null + : double.parse(rating!); //lisence - String? licenseNumber = license; - CityMunicipality? cityMunicipality = - selectedMunicipality; - DateTime? examDate = - examDateController.text.isEmpty - ? null - : DateTime.parse( - examDateController.text); - DateTime? validityDate = - validityDateController.text.isEmpty - ? null - : DateTime.parse( - validityDateController.text); + String? licenseNumber = license; + CityMunicipality? cityMunicipality = + selectedMunicipality; + DateTime? examDate = + examDateController.text.isEmpty + ? null + : DateTime.parse( + examDateController.text); + DateTime? validityDate = + validityDateController + .text.isEmpty + ? null + : DateTime.parse( + validityDateController + .text); - ExamAddress examAddress = ExamAddress( - barangay: null, - id: null, - addressCategory: null, - examAddressClass: null, - country: selectedCountry ?? - Country( - id: 175, - name: 'Philippines', - code: 'PH'), - cityMunicipality: cityMunicipality); - EligibityCert eligibityCert = - EligibityCert( + ExamAddress examAddress = ExamAddress( + barangay: null, id: null, - rating: rate, - examDate: examDate, - attachments: null, - eligibility: selectedEligibility, - examAddress: examAddress, - validityDate: validityDate, - licenseNumber: licenseNumber, - overseas: overseas); - if (formKey.currentState! - .saveAndValidate()) { - context.read().add( - AddEligibility( - eligibityCert: eligibityCert, - profileId: profileId!, - token: token!)); - } - // context.read().add(AddEligibility(eligibityCert: eligibityCert, profileId: profileId, token: token)) - }, - child: const Text(submit)), - ), - const SizedBox( - height: 20, - ), - ]), + addressCategory: null, + examAddressClass: null, + country: selectedCountry ?? + Country( + id: 175, + name: 'Philippines', + code: 'PH'), + cityMunicipality: + cityMunicipality); + EligibityCert eligibityCert = + EligibityCert( + id: null, + rating: rate, + examDate: examDate, + attachments: null, + eligibility: + selectedEligibility, + examAddress: examAddress, + validityDate: validityDate, + licenseNumber: licenseNumber, + overseas: overseas); + if (formKey.currentState! + .saveAndValidate()) { + context.read().add( + AddEligibility( + eligibityCert: + eligibityCert, + profileId: profileId!, + token: token!)); + } + // context.read().add(AddEligibility(eligibityCert: eligibityCert, profileId: profileId, token: token)) + }, + child: const Text(submit)), + ), + const SizedBox( + height: 20, + ), + ]), + ), + ), ), - ), - ), - ); - } - return Container(); + ); + } + return Container(); + }, + ); + } + return Container(); }, ); } @@ -477,7 +503,7 @@ class _AddEligibilityScreenState extends State { getCities(); }); } catch (e) { - context.read().add(CallErrorState()); + context.read().add(CallErrorState()); } } @@ -491,7 +517,7 @@ class _AddEligibilityScreenState extends State { cityCall = false; }); } catch (e) { - context.read().add(CallErrorState()); + context.read().add(CallErrorState()); } } } diff --git a/lib/screens/profile/components/eligibility/edit_modal.dart b/lib/screens/profile/components/eligibility/edit_modal.dart index 6f32e97..0241a5f 100644 --- a/lib/screens/profile/components/eligibility/edit_modal.dart +++ b/lib/screens/profile/components/eligibility/edit_modal.dart @@ -4,6 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:intl/intl.dart'; import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; +import 'package:unit2/bloc/eligibility/eligibility_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/location/city.dart'; @@ -59,446 +60,486 @@ class _EditEligibilityScreenState extends State { token = state.userData!.user!.login!.token; profileId = state.userData!.user!.login!.user!.profileId.toString(); return BlocBuilder( - buildWhen: (previous, current) { - if (state is EditEligibilityState) {} - return false; - }, builder: (context, state) { - //EDIT ELIGIBILITY STATE - if (state is EditEligibilityState) { - examDateController.text = state.eligibityCert.examDate == null?'': state.eligibityCert.examDate.toString(); - validityDateController.text = state.eligibityCert.validityDate == null?'': state.eligibityCert.validityDate.toString(); - - provinces = state.provinces; - citymuns = state.cities; - regions = state.regions; - overseas = state.isOverseas; - selectedRegion = state.currentRegion; - selectedProvince = state.currentProvince; - selectedMunicipality = state.currentCity; - selectedEligibility= state.currentEligibility; - rating = state.eligibityCert.rating?.toString(); - license = state.eligibityCert.licenseNumber; - return Center( - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 25, horizontal: 18), - child: FormBuilder( - key: formKey, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - //ELIGIBILITIES DROPDOWN - DropdownButtonFormField( - validator: (value) => - value == null ? 'required' : null, - isExpanded: true, - onChanged: (Eligibility? eligibility) { - selectedEligibility = eligibility; - }, - value: selectedEligibility, - items: state.eligibilities - .map>( - (Eligibility eligibility) { - return DropdownMenuItem( - value: eligibility, - child: Text(eligibility.title)); - }).toList(), - decoration: - normalTextFieldStyle("Eligibility", "")), - const SizedBox( - height: 20, - ), + if(state is ProfileLoaded){ + return BlocBuilder( + buildWhen: (previous, current) { + if (state is EditEligibilityState) {} + return false; + }, + builder: (context, state) { + //EDIT ELIGIBILITY STATE + if (state is EditEligibilityState) { + examDateController.text = + state.eligibityCert.examDate == null + ? '' + : state.eligibityCert.examDate.toString(); + validityDateController.text = + state.eligibityCert.validityDate == null + ? '' + : state.eligibityCert.validityDate.toString(); - SizedBox( - width: screenWidth, - child: Row( - children: [ - //LICENSE NUMBER - Flexible( - flex: 1, - child: FormBuilderTextField( - onChanged: (value) { - license = value; - }, - name: 'license_number', - initialValue: - license, - decoration: normalTextFieldStyle( - "license number", "license number"), - ), - ), - const SizedBox( - width: 12, - ), - //RATING - Flexible( - flex: 1, - child: FormBuilderTextField( - keyboardType: const TextInputType - .numberWithOptions(), - onChanged: (value) { - rating = value; - }, - name: 'rating', - initialValue: rating == null - ? 'N/A' - : rating.toString(), - decoration: normalTextFieldStyle( - 'rating', 'rating'), - ), - ), - ], - ), - ), - const SizedBox( - height: 20, - ), - SizedBox( - width: screenWidth, - child: Row( - children: [ - //EXAM DATE - Flexible( - flex: 1, - child: DateTimePicker( - use24HourFormat: false, - controller: examDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), - decoration: normalTextFieldStyle( - "Exam date", "") - .copyWith( - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - )), - )), - const SizedBox( - width: 12, - ), - //VALIDITY DATE - Flexible( - flex: 1, - child: DateTimePicker( - use24HourFormat: false, - controller: validityDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), - decoration: normalTextFieldStyle( - "validity date", "") - .copyWith( - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - )), - ), - ), - ], - ), - ), - const SizedBox( - height: 20, - ), - Text( - "Placement of Examination/Confinement", - style: Theme.of(context) - .textTheme - .displaySmall! - .copyWith(fontSize: blockSizeVertical * 2), - ), - const SizedBox( - height: 12, - ), - //OVERSEAS ADDRESS SWITCH - StatefulBuilder( - builder: (context, StateSetter setState) { - return Column( - children: [ - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - setState(() { - overseas = value; - }); + provinces = state.provinces; + citymuns = state.cities; + regions = state.regions; + overseas = state.isOverseas; + selectedRegion = state.currentRegion; + selectedProvince = state.currentProvince; + selectedMunicipality = state.currentCity; + selectedEligibility = state.currentEligibility; + rating = state.eligibityCert.rating?.toString(); + license = state.eligibityCert.licenseNumber; + return Center( + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, horizontal: 18), + child: FormBuilder( + key: formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + //ELIGIBILITIES DROPDOWN + DropdownButtonFormField( + validator: (value) => + value == null ? 'required' : null, + isExpanded: true, + onChanged: (Eligibility? eligibility) { + selectedEligibility = eligibility; }, - decoration: normalTextFieldStyle("", ''), - name: 'overseas', - title: const Text("Overseas Address?"), + value: selectedEligibility, + items: state.eligibilities + .map>( + (Eligibility eligibility) { + return DropdownMenuItem( + value: eligibility, + child: Text(eligibility.title)); + }).toList(), + decoration: normalTextFieldStyle( + "Eligibility", "")), + const SizedBox( + height: 20, + ), + + SizedBox( + width: screenWidth, + child: Row( + children: [ + //LICENSE NUMBER + Flexible( + flex: 1, + child: FormBuilderTextField( + onChanged: (value) { + license = value; + }, + name: 'license_number', + initialValue: license, + decoration: normalTextFieldStyle( + "license number", + "license number"), + ), + ), + const SizedBox( + width: 12, + ), + //RATING + Flexible( + flex: 1, + child: FormBuilderTextField( + keyboardType: const TextInputType + .numberWithOptions(), + onChanged: (value) { + rating = value; + }, + name: 'rating', + initialValue: rating == null + ? 'N/A' + : rating.toString(), + decoration: normalTextFieldStyle( + 'rating', 'rating'), + ), + ), + ], ), - const SizedBox( - height: 20, + ), + const SizedBox( + height: 20, + ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + //EXAM DATE + Flexible( + flex: 1, + child: DateTimePicker( + use24HourFormat: false, + controller: examDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + decoration: normalTextFieldStyle( + "Exam date", "") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + )), + const SizedBox( + width: 12, + ), + //VALIDITY DATE + Flexible( + flex: 1, + child: DateTimePicker( + use24HourFormat: false, + controller: validityDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + decoration: normalTextFieldStyle( + "validity date", "") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + ), + ), + ], ), - //COUNTRY DROPDOWN - SizedBox( - child: overseas == true - ? FormBuilderDropdown( - validator: (value) => - value == null - ? 'required' - : null, - initialValue: - state.selectedCountry, - items: state.countries.map< - DropdownMenuItem< - Country>>( - (Country country) { - return DropdownMenuItem< - Country>( - value: country, - child: FittedBox( - child: Text( - country.name!))); - }).toList(), - name: 'country', - decoration: normalTextFieldStyle( - "Country*", "Country"), - onChanged: (Country? value) { - selectedCountry = value; - }, - ) - : Column( - children: [ - //REGION DROPDOWN - DropdownButtonFormField< - Region?>( + ), + const SizedBox( + height: 20, + ), + Text( + "Placement of Examination/Confinement", + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith( + fontSize: blockSizeVertical * 2), + ), + const SizedBox( + height: 12, + ), + //OVERSEAS ADDRESS SWITCH + StatefulBuilder( + builder: (context, StateSetter setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value; + }); + }, + decoration: + normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Overseas Address?"), + ), + const SizedBox( + height: 20, + ), + //COUNTRY DROPDOWN + SizedBox( + child: overseas == true + ? FormBuilderDropdown( validator: (value) => value == null ? 'required' : null, - isExpanded: true, - onChanged: - (Region? region) async { - setState(() { - provinceCall = true; - }); - selectedRegion = region; - provinces = await LocationUtils - .instance - .getProvinces( - regionCode: - selectedRegion! - .code - .toString()); - selectedProvince = - provinces![0]; - setState(() { - provinceCall = false; - cityCall = true; - }); - citymuns = await LocationUtils - .instance - .getCities( - code: - selectedProvince! - .code!); - selectedMunicipality = - citymuns![0]; - setState(() { - cityCall = false; - }); - }, - value: selectedRegion, + initialValue: + state.selectedCountry, + items: state.countries.map< + DropdownMenuItem< + Country>>( + (Country country) { + return DropdownMenuItem< + Country>( + value: country, + child: FittedBox( + child: Text(country + .name!))); + }).toList(), + name: 'country', decoration: normalTextFieldStyle( - "Region*", "Region"), - items: regions == null - ? [] - : regions!.map< - DropdownMenuItem< - Region>>( - (Region region) { - return DropdownMenuItem< - Region>( - value: region, - child: Text(region - .description!)); - }).toList(), - ), - const SizedBox( - height: 20, - ), - //PROVINCE DROPDOWN - SizedBox( - height: 70, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: provinceCall, - child: DropdownButtonFormField< - Province?>( - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - value: selectedProvince, - onChanged: (Province? - province) async { - setState(() { - cityCall = true; - }); - selectedProvince = - province; - citymuns = await LocationUtils - .instance - .getCities( - code: selectedProvince! - .code - .toString()); - selectedMunicipality = - citymuns![0]; - setState(() { - cityCall = false; - }); - }, - items: provinces == null - ? [] - : provinces!.map< - DropdownMenuItem< - Province>>( - (Province - province) { - return DropdownMenuItem( - value: - province, - child: - FittedBox( - child: Text( - province - .description!), - )); - }).toList(), - decoration: - normalTextFieldStyle( - "Province*", - "Province")), - ), - ), - - // City municipality - SizedBox( - height: 70, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: cityCall, - child: - DropdownButtonFormField< - CityMunicipality>( + "Country*", + "Country"), + onChanged: (Country? value) { + selectedCountry = value; + }, + ) + : Column( + children: [ + //REGION DROPDOWN + DropdownButtonFormField< + Region?>( validator: (value) => value == null ? 'required' : null, isExpanded: true, - onChanged: - (CityMunicipality? - city) { + onChanged: (Region? + region) async { + setState(() { + provinceCall = true; + }); + selectedRegion = region; + provinces = await LocationUtils + .instance + .getProvinces( + regionCode: + selectedRegion! + .code + .toString()); + selectedProvince = + provinces![0]; + setState(() { + provinceCall = false; + cityCall = true; + }); + citymuns = await LocationUtils + .instance + .getCities( + code: + selectedProvince! + .code!); selectedMunicipality = - city; + citymuns![0]; + setState(() { + cityCall = false; + }); }, + value: selectedRegion, decoration: normalTextFieldStyle( - "Municipality*", - "Municipality"), - value: - selectedMunicipality, - items: citymuns == null + "Region*", + "Region"), + items: regions == null ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality - c) { - return DropdownMenuItem( - value: c, - child: Text(c + : regions!.map< + DropdownMenuItem< + Region>>((Region + region) { + return DropdownMenuItem< + Region>( + value: region, + child: Text(region .description!)); }).toList(), ), - ), - ), - const SizedBox( - height: 20, - ), - ], - )), - ], - ); - }), + const SizedBox( + height: 20, + ), + //PROVINCE DROPDOWN + SizedBox( + height: 70, + child: ModalProgressHUD( + color: + Colors.transparent, + inAsyncCall: + provinceCall, + child: DropdownButtonFormField< + Province?>( + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + value: + selectedProvince, + onChanged: (Province? + province) async { + setState(() { + cityCall = true; + }); + selectedProvince = + province; + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code + .toString()); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = + false; + }); + }, + items: provinces == + null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>((Province + province) { + return DropdownMenuItem( + value: + province, + child: + FittedBox( + child: + Text(province.description!), + )); + }).toList(), + decoration: + normalTextFieldStyle( + "Province*", + "Province")), + ), + ), - const Expanded( - child: SizedBox(), - ), + // City municipality + SizedBox( + height: 70, + child: ModalProgressHUD( + color: + Colors.transparent, + inAsyncCall: cityCall, + child: + DropdownButtonFormField< + CityMunicipality>( + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + onChanged: + (CityMunicipality? + city) { + selectedMunicipality = + city; + }, + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: + selectedMunicipality, + items: citymuns == + null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality + c) { + return DropdownMenuItem( + value: c, + child: Text( + c.description!)); + }).toList(), + ), + ), + ), + const SizedBox( + height: 20, + ), + ], + )), + ], + ); + }), - SizedBox( - width: screenWidth, - height: 60, - child: ElevatedButton( - style: mainBtnStyle( - primary, Colors.transparent, second), - onPressed: () { - - //rating - double? rate = rating == null - ? null - : double.parse(rating!); - //license - String? newLicense = license; - //city municipality - CityMunicipality? cityMunicipality = - selectedMunicipality; - //exam date - DateTime? examDate = - examDateController.text.isEmpty - ? null - : DateTime.parse( - examDateController.text); - // validity date - DateTime? validityDate = - validityDateController.text.isEmpty - ? null - : DateTime.parse( - validityDateController.text); - // exam address - ExamAddress examAddress = ExamAddress( - barangay: state.eligibityCert.examAddress?.barangay, - id: state.eligibityCert.examAddress?.id, - addressCategory: state.eligibityCert.examAddress?.addressCategory, - examAddressClass: state.eligibityCert.examAddress?.examAddressClass, - country: selectedCountry ??= Country( - id: 175, - name: 'Philippines', - code: 'PH'), - cityMunicipality: cityMunicipality); - EligibityCert eligibityCert = - EligibityCert( - id: state.eligibityCert.id, - rating: rate, - examDate: examDate, - attachments: null, - eligibility: selectedEligibility, - examAddress: examAddress, - validityDate: validityDate, - licenseNumber: newLicense, - overseas: overseas); - if (formKey.currentState! - .saveAndValidate()) { - context.read().add(UpdateEligibility(eligibityCert: eligibityCert, oldEligibility: state.eligibityCert.eligibility!.id, profileId: profileId!, token: token!)); - } - }, - child: const Text(submit)), - ), - const SizedBox( - height: 20, - ), - ]), - ), - ), - ); + const Expanded( + child: SizedBox(), + ), + + SizedBox( + width: screenWidth, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + //rating + double? rate = rating == null + ? null + : double.parse(rating!); + //license + String? newLicense = license; + //city municipality + CityMunicipality? cityMunicipality = + selectedMunicipality; + //exam date + DateTime? examDate = + examDateController.text.isEmpty + ? null + : DateTime.parse( + examDateController.text); + // validity date + DateTime? validityDate = + validityDateController.text.isEmpty + ? null + : DateTime.parse( + validityDateController + .text); + // exam address + ExamAddress examAddress = ExamAddress( + barangay: state.eligibityCert + .examAddress?.barangay, + id: state + .eligibityCert.examAddress?.id, + addressCategory: state.eligibityCert + .examAddress?.addressCategory, + examAddressClass: state + .eligibityCert + .examAddress + ?.examAddressClass, + country: selectedCountry ??= + Country( + id: 175, + name: 'Philippines', + code: 'PH'), + cityMunicipality: cityMunicipality); + EligibityCert eligibityCert = + EligibityCert( + id: state.eligibityCert.id, + rating: rate, + examDate: examDate, + attachments: null, + eligibility: + selectedEligibility, + examAddress: examAddress, + validityDate: validityDate, + licenseNumber: newLicense, + overseas: overseas); + if (formKey.currentState! + .saveAndValidate()) { + context.read().add( + UpdateEligibility( + eligibityCert: eligibityCert, + oldEligibility: state + .eligibityCert + .eligibility! + .id, + profileId: profileId!, + token: token!)); + } + }, + child: const Text(submit)), + ), + const SizedBox( + height: 20, + ), + ]), + ), + ), + ); + } + return Container(); + }, + ); } return Container(); }, + ); } return Container(); diff --git a/lib/screens/profile/components/eligibility_screen.dart b/lib/screens/profile/components/eligibility_screen.dart index 3e3623a..2d3a75d 100644 --- a/lib/screens/profile/components/eligibility_screen.dart +++ b/lib/screens/profile/components/eligibility_screen.dart @@ -4,6 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:unit2/bloc/eligibility/eligibility_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/eligibility.dart'; @@ -25,32 +26,33 @@ class EligibiltyScreen extends StatelessWidget { Widget build(BuildContext context) { String? token; String? profileId; - List? eligibilities; + return WillPopScope( onWillPop: () async { return true; }, child: Scaffold( appBar: AppBar( - title: context.watch().state is AddEligibilityState + title: context.watch().state is AddEligibilityState ? const Text("Add Eligiblity") - : context.watch().state is EditEligibilityState + : context.watch().state is EditEligibilityState ? const Text("Edit Eligibilty") : const Text(elibilityScreenTitle), centerTitle: true, backgroundColor: primary, - actions: (context.watch().state is EligibilityLoaded || - context.watch().state is ProfileLoading) + actions: (context.watch().state is EligibilityLoaded || + context.watch().state is ProfileLoading) ? [ AddLeading(onPressed: () { - context.read().add(ShowAddEligibilityForm()); + context + .read() + .add(ShowAddEligibilityForm()); }) ] : [ CloseLeading(onPressed: () { - context - .read() - .add(GetEligibilities(profileId: int.parse(profileId!), token: token!)); + context.read().add(GetEligibilities( + profileId: int.parse(profileId!), token: token!)); }) ], ), @@ -60,257 +62,278 @@ class EligibiltyScreen extends StatelessWidget { token = state.userData!.user!.login!.token; profileId = state.userData!.user!.login!.user!.profileId.toString(); - return ProgressHUD( - padding: const EdgeInsets.all(24), - indicatorWidget: const SpinKitFadingCircle( - color: Colors.white, - ), - backgroundColor: Colors.black87, - child: BlocConsumer( - listener: (context, state) { - if (state is ProfileLoading) { - final progress = ProgressHUD.of(context); - progress!.showWithText("Loading"); - } - if (state is EligibilityLoaded || - state is AddEligibilityState || - state is ProfileErrorState || - state is EditEligibilityState || - state is DeletedState || - state is EligibilityAddedState || - state is EligibilityEditedState) { - final progress = ProgressHUD.of(context); - progress!.dismiss(); - } - //DELETED STATE - if (state is DeletedState) { - if (state.success) { - successAlert(context, "Deletion Successfull", - "Eligibility has been deleted successfully", () { - Navigator.of(context).pop(); - context.read().add(LoadEligibility( - eligibilities: state.eligibilities)); - }); - } else { - errorAlert(context, "Deletion Failed", - "Error deleting eligibility", () { - Navigator.of(context).pop(); - context.read().add(LoadEligibility( - eligibilities: state.eligibilities)); - }); - } - } - //ADDED STATE - if (state is EligibilityAddedState) { - if (state.response['success']) { - successAlert(context, "Adding Successfull!", - state.response['message'], () { - Navigator.of(context).pop(); - context.read().add(LoadEligibility( - eligibilities: state.eligibilities)); - }); - } else { - errorAlert(context, "Adding Failed", - "Something went wrong. Please try again.", () { - Navigator.of(context).pop(); - context.read().add(LoadEligibility( - eligibilities: state.eligibilities)); - }); - } - } - //UPDATED STATE - if (state is EligibilityEditedState) { - if (state.response['success']) { - successAlert(context, "Update Successfull!", - state.response['message'], () { - Navigator.of(context).pop(); - context.read().add(LoadEligibility( - eligibilities: state.eligibilities)); - }); - } else { - errorAlert(context, "Update Failed", - "Something went wrong. Please try again.", () { - Navigator.of(context).pop(); - context.read().add(LoadEligibility( - eligibilities: state.eligibilities)); - }); - } - } - }, - builder: (context, state) { - return BlocBuilder( - builder: (context, state) { - if (state is EligibilityLoaded) { - eligibilities = state.eligibilities; - if (state.eligibilities.isNotEmpty) { - return ListView.builder( - padding: const EdgeInsets.symmetric( - vertical: 8, horizontal: 10), - itemCount: state.eligibilities.length, - itemBuilder: - (BuildContext context, int index) { - String title = state.eligibilities[index] - .eligibility!.title; - return Column( - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Container( - width: screenWidth, - padding: const EdgeInsets.symmetric( - horizontal: 12, vertical: 8), - decoration: box1(), - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - Text( - title, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith( - fontWeight: - FontWeight - .w500), - ), - const Divider(), - const SizedBox( - height: 5, - ), - Text( - "$licenseNumber: ${state.eligibilities[index].licenseNumber == null ? 'N/A' : state.eligibilities[index].licenseNumber.toString()}", - style: - Theme.of(context) - .textTheme - .titleSmall), - const SizedBox( - height: 3, - ), - Text( - "Rating : ${state.eligibilities[index].rating ?? 'N/A'}.", - style: - Theme.of(context) - .textTheme - .titleSmall) - ]), - ), - AppPopupMenu( - offset: const Offset(-10, -10), - elevation: 3, - onSelected: (value) { - final progress = - ProgressHUD.of(context); - progress!.showWithText( - "Loading..."); - ////delete eligibilty-= = = = = = = = =>> - if (value == 2) { - confirmAlert(context, () { - BlocProvider.of< - ProfileBloc>( - context) - .add(DeleteEligibility( - eligibilities: - state - .eligibilities, - eligibilityId: state - .eligibilities[ - index] - .id!, - profileId: - profileId!, - token: token!)); - }, "Delete?", - "Confirm Delete?"); - } - if (value == 1) { - ////edit eligibilty-= = = = = = = = =>> - EligibityCert - eligibityCert = - state.eligibilities[ - index]; - bool overseas = eligibityCert - .examAddress! - .country! - .id - .toString() == - '175' - ? false - : true; - eligibityCert.overseas = - overseas; - - eligibityCert.overseas = - overseas; - - context.read().add( - ShowEditEligibilityForm( - eligibityCert: - eligibityCert)); - } - }, - menuItems: [ - popMenuItem( - text: "Edit", - value: 1, - icon: Icons.edit), - popMenuItem( - text: "Delete", - value: 2, - icon: Icons.delete), - popMenuItem( - text: "Attachment", - value: 3, - icon: FontAwesome.attach) - ], - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - ), - tooltip: "Options", - ) - ], - ), - ), - const SizedBox( - height: 5, - ) - ], - ); - }); + return BlocBuilder( + builder: (context, state) { + if(state is ProfileLoaded){ + return ProgressHUD( + padding: const EdgeInsets.all(24), + indicatorWidget: const SpinKitFadingCircle( + color: Colors.white, + ), + backgroundColor: Colors.black87, + child: BlocConsumer( + listener: (context, state) { + if (state is EligibilityLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading"); + } + if (state is EligibilityLoaded || + state is AddEligibilityState || + state is EditEligibilityState || + state is DeletedState || + state is EligibilityAddedState || + state is EligibilityEditedState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + //DELETED STATE + if (state is DeletedState) { + if (state.success) { + successAlert(context, "Deletion Successfull", + "Eligibility has been deleted successfully", + () { + Navigator.of(context).pop(); + context.read().add(LoadEligibility( + eligibilities: state.eligibilities)); + }); } else { - return const EmptyData( - message: - "You don't have any eligibilities added. Please click + to add"); + errorAlert(context, "Deletion Failed", + "Error deleting eligibility", () { + Navigator.of(context).pop(); + context.read().add(LoadEligibility( + eligibilities: state.eligibilities)); + }); } } - if (state is EditEligibilityState) { - return EditEligibilityScreen( - eligibityCert: state.eligibityCert); + //ADDED STATE + if (state is EligibilityAddedState) { + if (state.response['success']) { + successAlert(context, "Adding Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context.read().add(LoadEligibility( + eligibilities: state.eligibilities)); + }); + } else { + errorAlert(context, "Adding Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context.read().add(LoadEligibility( + eligibilities: state.eligibilities)); + }); + } } - if (state is AddEligibilityState) { - return const AddEligibilityScreen(); + //UPDATED STATE + if (state is EligibilityEditedState) { + if (state.response['success']) { + successAlert(context, "Update Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context.read().add(LoadEligibility( + eligibilities: state.eligibilities)); + }); + } else { + errorAlert(context, "Update Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context.read().add(LoadEligibility( + eligibilities: state.eligibilities)); + }); + } } - if (state is ProfileErrorState) { - return Center( - child: Text(state.mesage), - ); - } - return Container( - color: Colors.grey.shade200, + }, + builder: (context, state) { + return BlocBuilder( + builder: (context, state) { + if (state is EligibilityLoaded) { + + if (state.eligibilities.isNotEmpty) { + return ListView.builder( + padding: const EdgeInsets.symmetric( + vertical: 8, horizontal: 10), + itemCount: state.eligibilities.length, + itemBuilder: + (BuildContext context, int index) { + String title = state + .eligibilities[index] + .eligibility! + .title; + return Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Container( + width: screenWidth, + padding: + const EdgeInsets.symmetric( + horizontal: 12, + vertical: 8), + decoration: box1(), + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + Text( + title, + style: Theme.of( + context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight + .w500), + ), + const Divider(), + const SizedBox( + height: 5, + ), + Text( + "$licenseNumber: ${state.eligibilities[index].licenseNumber == null ? 'N/A' : state.eligibilities[index].licenseNumber.toString()}", + style: Theme.of( + context) + .textTheme + .titleSmall), + const SizedBox( + height: 3, + ), + Text( + "Rating : ${state.eligibilities[index].rating ?? 'N/A'}.", + style: Theme.of( + context) + .textTheme + .titleSmall) + ]), + ), + AppPopupMenu( + offset: + const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + final progress = + ProgressHUD.of( + context); + progress!.showWithText( + "Loading..."); + ////delete eligibilty-= = = = = = = = =>> + if (value == 2) { + confirmAlert(context, + () { + BlocProvider.of< + EligibilityBloc>( + context) + .add(DeleteEligibility( + eligibilities: state + .eligibilities, + eligibilityId: state + .eligibilities[ + index] + .id!, + profileId: + profileId!, + token: + token!)); + }, "Delete?", + "Confirm Delete?"); + } + if (value == 1) { + ////edit eligibilty-= = = = = = = = =>> + EligibityCert + eligibityCert = + state.eligibilities[ + index]; + bool overseas = eligibityCert + .examAddress! + .country! + .id + .toString() == + '175' + ? false + : true; + eligibityCert.overseas = + overseas; + + eligibityCert.overseas = + overseas; + + context + .read() + .add(ShowEditEligibilityForm( + eligibityCert: + eligibityCert)); + } + }, + menuItems: [ + popMenuItem( + text: "Edit", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Delete", + value: 2, + icon: Icons.delete), + popMenuItem( + text: "Attachment", + value: 3, + icon: FontAwesome + .attach) + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ) + ], + ), + ), + const SizedBox( + height: 5, + ) + ], + ); + }); + } else { + return const EmptyData( + message: + "You don't have any eligibilities added. Please click + to add"); + } + } + if (state is EditEligibilityState) { + return EditEligibilityScreen( + eligibityCert: state.eligibityCert); + } + if (state is AddEligibilityState) { + return const AddEligibilityScreen(); + } + if (state is EligibilityErrorState) { + return Center( + child: Text(state.message), + ); + } + return Container( + color: Colors.grey.shade200, + ); + }, ); }, - ); - }, - ), + ), + ); + + } + return Container(); + } ); } return Container(); diff --git a/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart b/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart index 6aa6465..4af8aa7 100644 --- a/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart +++ b/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart @@ -1,6 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; import 'package:unit2/model/profile/other_information/non_acedimic_recognition.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; diff --git a/lib/screens/profile/components/work_history_screen.dart b/lib/screens/profile/components/work_history_screen.dart index 71deb9f..74d9a0d 100644 --- a/lib/screens/profile/components/work_history_screen.dart +++ b/lib/screens/profile/components/work_history_screen.dart @@ -1,71 +1,167 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:intl/intl.dart'; +import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/user/user_bloc.dart'; +import 'package:unit2/bloc/workHistory/workHistory_bloc.dart'; import 'package:unit2/model/profile/work_history.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; +import 'package:unit2/widgets/error_state.dart'; import '../../../utils/global.dart'; class WorkHistoryScreen extends StatelessWidget { - final List workExperiences; - const WorkHistoryScreen({super.key, required this.workExperiences}); - - + const WorkHistoryScreen({super.key}); @override Widget build(BuildContext context) { - DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); + DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); return Scaffold( - appBar: AppBar( - title: const Text(workHistoryScreenTitle), - backgroundColor: primary, - centerTitle: true, - actions: [AddLeading(onPressed: (){})], - ), - body: workExperiences.isNotEmpty? ListView.builder( - padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 10), - itemCount: workExperiences.length, - itemBuilder: (BuildContext context, int index) { - String position = workExperiences[index].position!.title!; - String agency = workExperiences[index].agency!.name!; - String from = - dteFormat2.format(workExperiences[index].fromDate!); - String? to = workExperiences[index].toDate == null - ? present.toUpperCase() - : dteFormat2.format(workExperiences[index].toDate!); - return Column( - - children: [ - Container( - width: screenWidth, - decoration: box1(), - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), - child: Row(children: [ - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(position,style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w600),), - const Divider(), - const SizedBox(height: 8,), - Text(agency,style: Theme.of(context).textTheme.titleSmall!.copyWith(fontWeight: FontWeight.w500),), - const SizedBox(height: 5,), - Text("$from to $to",style: Theme.of(context).textTheme.bodyMedium,), - ], - )), - IconButton(onPressed: () {}, icon: const Icon(Icons.more_vert,color: Colors.grey,)) - ]), - ), - const SizedBox( - height: 5, - ), - ], - ); - }):const EmptyData(message: "You don't have any Work History added. Please click + to add ."), - ); + appBar: AppBar( + title: const Text(workHistoryScreenTitle), + backgroundColor: primary, + centerTitle: true, + actions: [AddLeading(onPressed: () {})], + ), + body: + //UserBloc + ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle( + color: Colors.white, + ), + child: BlocBuilder( + builder: (context, state) { + if (state is UserLoggedIn) { + return BlocBuilder( + builder: (context, state) { + //ProfileBloc + if (state is ProfileLoaded) { + //WorkHistoryBloc + return BlocConsumer( + listener: (context, state) { + if (state is WorkHistoryLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is WorkHistoryLoaded || state is WorkHistoryErrorState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + }, + builder: (context, state) { + if (state is WorkHistoryLoaded) { + if (state.workExperiences.isNotEmpty) { + return ListView.builder( + padding: const EdgeInsets.symmetric( + vertical: 8, horizontal: 10), + itemCount: state.workExperiences.length, + itemBuilder: + (BuildContext context, int index) { + String position = state + .workExperiences[index] + .position! + .title!; + String agency = state + .workExperiences[index].agency!.name!; + String from = dteFormat2.format( + state.workExperiences[index].fromDate!); + String? to = + state.workExperiences[index].toDate == + null + ? present.toUpperCase() + : dteFormat2.format( + state.workExperiences[index].toDate!); + return Column( + children: [ + Container( + width: screenWidth, + decoration: box1(), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + child: Row(children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + position, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight.w600), + ), + const Divider(), + const SizedBox( + height: 8, + ), + Text( + agency, + style: Theme.of(context) + .textTheme + .titleSmall! + .copyWith( + fontWeight: + FontWeight.w500), + ), + const SizedBox( + height: 5, + ), + Text( + "$from $to ", + style: Theme.of(context) + .textTheme + .bodyMedium, + ), + ], + )), + IconButton( + onPressed: () {}, + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + )) + ]), + ), + const SizedBox( + height: 5, + ), + ], + ); + }); + } else { + return const EmptyData( + message: + "You don't have any work experience added. Please click + to add"); + } + } + if (state is WorkHistoryErrorState) { + return SomethingWentWrong( + message: state.message, onpressed: () {}); + } + return Container(); + }, + ); + } + return Container(); + }, + ); + } + return Container(); + }, + ), + )); } } diff --git a/lib/screens/profile/profile.dart b/lib/screens/profile/profile.dart index 2d305ed..91c203b 100644 --- a/lib/screens/profile/profile.dart +++ b/lib/screens/profile/profile.dart @@ -8,14 +8,15 @@ import 'package:fluttericon/elusive_icons.dart'; import 'package:fluttericon/entypo_icons.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:fluttericon/modern_pictograms_icons.dart'; +import 'package:unit2/bloc/eligibility/eligibility_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/workHistory/workHistory_bloc.dart'; import 'package:unit2/model/login_data/employee_info/employee_info.dart'; import 'package:unit2/screens/profile/components/basic_information/address_screen.dart'; import 'package:unit2/screens/profile/components/basic_information/citizenship_screen.dart'; import 'package:unit2/screens/profile/components/basic_information/contact_information_screen.dart'; import 'package:unit2/screens/profile/components/basic_information/identification_information_screen.dart'; import 'package:unit2/screens/profile/components/basic_information/primary_information_screen.dart'; -import 'package:unit2/screens/profile/components/education_screen.dart'; import 'package:unit2/screens/profile/components/eligibility_screen.dart'; import 'package:unit2/screens/profile/components/family_background_screen.dart'; import 'package:unit2/screens/profile/components/learning_and_development_screen.dart'; @@ -148,12 +149,7 @@ class _ProfileInfoState extends State { icon: Elusive.group, title: "Family", onTap: () { - // Navigator.push(context, MaterialPageRoute( - // builder: (BuildContext context) { - // return FamilyBackgroundScreen( - // familyBackground: - // state.profileInformation.families); - // })); + }, ), const Divider(), @@ -177,10 +173,8 @@ class _ProfileInfoState extends State { onTap: () { Navigator.push(context, MaterialPageRoute( builder: (BuildContext context) { - return BlocProvider.value( - value: ProfileBloc() - ..add(GetEligibilities( - profileId: profileId!, token: token!)), + return BlocProvider( + create: (context) => EligibilityBloc()..add(GetEligibilities(profileId: profileId!, token: token!)), child: const EligibiltyScreen(), ); })); @@ -191,12 +185,14 @@ class _ProfileInfoState extends State { icon: FontAwesome5.shopping_bag, title: "Work History", onTap: () { - // Navigator.push(context, MaterialPageRoute( - // builder: (BuildContext context) { - // return WorkHistoryScreen( - // workExperiences: state - // .profileInformation.workExperiences); - // })); + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return BlocProvider( + create: (context) => WorkHistoryBloc()..add(GetWorkHistories(profileId: profileId!, token: token!)), + child: const WorkHistoryScreen(), + ); + })); + }, ), const Divider(), diff --git a/lib/sevices/profile/education_services.dart b/lib/sevices/profile/education_services.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/sevices/profile/eligibility_services.dart b/lib/sevices/profile/eligibility_services.dart index 1c3e183..adfdb8d 100644 --- a/lib/sevices/profile/eligibility_services.dart +++ b/lib/sevices/profile/eligibility_services.dart @@ -20,7 +20,7 @@ class EligibilityService { }; - // try{ + try{ http.Response response = await Request.instance.getRequest(path: path,headers: headers,param: {}); if(response.statusCode == 200){ Map data = jsonDecode(response.body); @@ -31,9 +31,9 @@ class EligibilityService { }); } } - // }catch(e){ - // throw e.toString(); - // } + }catch(e){ + throw e.toString(); + } return eligibilities; } diff --git a/lib/sevices/profile/work_history_services.dart b/lib/sevices/profile/work_history_services.dart new file mode 100644 index 0000000..b42de20 --- /dev/null +++ b/lib/sevices/profile/work_history_services.dart @@ -0,0 +1,39 @@ +import 'dart:convert'; + +import 'package:unit2/model/profile/work_history.dart'; + + +import 'package:http/http.dart' as http; +import 'package:unit2/utils/request.dart'; + +import '../../utils/urls.dart'; +class WorkHistoryService{ + static final WorkHistoryService _instance = WorkHistoryService(); + static WorkHistoryService get instance => _instance; + + Future > getWorkExperiences( int profileId, String token)async{ + List workExperiences = []; + String authToken = "Token $token"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + String path= Url.instance.workHistories()+profileId.toString(); + // try{ + http.Response response =await Request.instance.getRequest(path: path, headers: headers, param: {}); + if(response.statusCode == 200){ + Map data = jsonDecode(response.body); + if(data['data'] != null){ + data['data'].forEach((var workHistory){ + workExperiences.add(WorkHistory.fromJson(workHistory)); + }); + } + + } + // }catch(e){ + // throw e.toString(); + // } + return workExperiences; + + } +} \ No newline at end of file diff --git a/lib/utils/app_router.dart b/lib/utils/app_router.dart index 16eea91..7e6d33c 100644 --- a/lib/utils/app_router.dart +++ b/lib/utils/app_router.dart @@ -34,12 +34,12 @@ class AppRouter { return const QRLogin(); }); case '/profile': + ProfileArguments arguments = routeSettings.arguments as ProfileArguments; + BlocProvider.of( + NavigationService.navigatorKey.currentContext!) + .add(LoadProfile(token:arguments.token ,userID:arguments.userID )); return MaterialPageRoute(builder: (_) { - ProfileArguments arguments = routeSettings.arguments as ProfileArguments; - return BlocProvider( - create: (context) => ProfileBloc()..add(LoadProfile(token: arguments.token,userID: arguments.userID)), - child: const ProfileInfo(), - ); + return const ProfileInfo(); }); default: return MaterialPageRoute(builder: (context) { diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index 6182792..f0d11fe 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -41,6 +41,10 @@ String deleteEligibility(){ String updateEligibility(){ return "/api/jobnet_app/profile/pds/eligibility/"; } +//// work history paths +String workHistories(){ + return "/api/jobnet_app/profile/pds/work/"; +} // location utils path String getCounties(){ From 61646bdcaae4c9f592f65125e2bc0f4bc57c6823 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Thu, 2 Mar 2023 14:59:40 +0800 Subject: [PATCH 42/86] educational background refactor and create its own bloc --- lib/bloc/education/education_bloc.dart | 25 ++ lib/bloc/education/education_event.dart | 15 + lib/bloc/education/education_state.dart | 29 ++ .../profile/components/education_screen.dart | 302 ++++++++++++------ .../components/eligibility_screen.dart | 2 +- lib/screens/profile/profile.dart | 16 +- lib/sevices/profile/education_services.dart | 41 +++ .../profile/work_history_services.dart | 2 +- lib/utils/urls.dart | 7 +- 9 files changed, 323 insertions(+), 116 deletions(-) create mode 100644 lib/bloc/education/education_bloc.dart create mode 100644 lib/bloc/education/education_event.dart create mode 100644 lib/bloc/education/education_state.dart diff --git a/lib/bloc/education/education_bloc.dart b/lib/bloc/education/education_bloc.dart new file mode 100644 index 0000000..bb19420 --- /dev/null +++ b/lib/bloc/education/education_bloc.dart @@ -0,0 +1,25 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/model/profile/educational_background.dart'; +import 'package:unit2/sevices/profile/education_services.dart'; + +part 'education_event.dart'; +part 'education_state.dart'; + +class EducationBloc extends Bloc { + List educationalBackgrounds = []; + EducationBloc() : super(EducationInitial()) { + on((event, emit) async { + emit(EducationalBackgroundLoadingState()); + try { + List educations = await EducationService.instace + .getEducationalBackground(event.profileId, event.token); + educationalBackgrounds = educations; + emit(EducationalBackgroundLoadedState( + educationalBackground: educationalBackgrounds)); + } catch (e) { + emit(EducationalBackgroundErrorState(message: e.toString())); + } + }); + } +} diff --git a/lib/bloc/education/education_event.dart b/lib/bloc/education/education_event.dart new file mode 100644 index 0000000..cfe1db7 --- /dev/null +++ b/lib/bloc/education/education_event.dart @@ -0,0 +1,15 @@ +part of 'education_bloc.dart'; + +abstract class EducationEvent extends Equatable { + const EducationEvent(); + + @override + List get props => []; +} +class GetEducationalBackground extends EducationEvent{ + final int profileId; + final String token; + const GetEducationalBackground({required this.profileId, required this.token}); + @override + List get props => [profileId,token]; +} diff --git a/lib/bloc/education/education_state.dart b/lib/bloc/education/education_state.dart new file mode 100644 index 0000000..cc75963 --- /dev/null +++ b/lib/bloc/education/education_state.dart @@ -0,0 +1,29 @@ +part of 'education_bloc.dart'; + +abstract class EducationState extends Equatable { + const EducationState(); + + @override + List get props => []; +} + +class EducationInitial extends EducationState {} + + + +class EducationalBackgroundLoadedState extends EducationState{ + final List educationalBackground; + const EducationalBackgroundLoadedState({required this.educationalBackground}); + @override + List get props => [educationalBackground]; +} + +class EducationalBackgroundErrorState extends EducationState{ + final String message; + const EducationalBackgroundErrorState({required this.message}); + @override + List get props => [message]; +} +class EducationalBackgroundLoadingState extends EducationState{ + +} diff --git a/lib/screens/profile/components/education_screen.dart b/lib/screens/profile/components/education_screen.dart index 6602b57..67cd194 100644 --- a/lib/screens/profile/components/education_screen.dart +++ b/lib/screens/profile/components/education_screen.dart @@ -1,4 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:unit2/bloc/education/education_bloc.dart'; +import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/educational_background.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; @@ -6,122 +12,206 @@ import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; +import 'package:unit2/widgets/error_state.dart'; class EducationScreen extends StatelessWidget { - final List educationBackgrounds; - const EducationScreen({super.key, required this.educationBackgrounds}); + + const EducationScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: const Text(educationScreenTitle), - centerTitle: true, - backgroundColor: primary, - actions: [AddLeading(onPressed: (){})], - ), - body: educationBackgrounds.isNotEmpty ? ListView.builder( - padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 10), - itemCount: educationBackgrounds.length, - itemBuilder: (BuildContext context, int index) { - String level = educationBackgrounds[index].education!.level!; - String periodFrom = educationBackgrounds[index].periodFrom!; - String periodTo = educationBackgrounds[index].periodTo!; - String? program = - educationBackgrounds[index].education!.course == null - ? null - : educationBackgrounds[index].education!.course! - .program!; - List? honors = - educationBackgrounds[index].honors!.toList(); - String school = - educationBackgrounds[index].education!.school!.name!; - return Column( - children: [ - Container( - decoration: box1(), - padding: - const EdgeInsets.symmetric(horizontal: 12, vertical: 8), - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Expanded( - child: Text( - level, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith(fontWeight: FontWeight.w500), - )), - Text( - "$periodFrom - $periodTo", - style: - Theme.of(context).textTheme.bodyMedium, - ), - ], - ), - - const SizedBox(height: 5,), - Text( - school, - style: Theme.of(context).textTheme.titleSmall, - ), - Container( - padding: const EdgeInsets.only(top: 8), - child: honors.isNotEmpty - ? Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text("$honorsText : ",style: TextStyle(fontWeight: FontWeight.w600),), - Column( - children: - - honors - .map((Honor honor) => - Text(" - ${honor.name!}")) - .toList(), - ), - ], - ) - : const SizedBox()), - program == null - ? const SizedBox() - : Column( - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, + appBar: AppBar( + title: const Text(educationScreenTitle), + centerTitle: true, + backgroundColor: primary, + actions: [AddLeading(onPressed: () {})], + ), + //userbloc + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle( + color: Colors.white), + child: BlocBuilder( + builder: (context, state) { + if (state is UserLoggedIn) { + //profilebloc + return BlocBuilder( + builder: (context, state) { + if (state is ProfileLoaded) { + //education bloc + return BlocConsumer( + listener: (context, state) { + if (state is EducationalBackgroundLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if(state is EducationalBackgroundLoadedState || state is EducationalBackgroundErrorState){ + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + }, + builder: (context, state) { + if (state is EducationalBackgroundLoadedState) { + if (state.educationalBackground.isNotEmpty) { + return ListView.builder( + padding: const EdgeInsets.symmetric( + vertical: 8, horizontal: 10), + itemCount: state.educationalBackground.length, + itemBuilder: (BuildContext context, int index) { + String level = state + .educationalBackground[index] + .education! + .level!; + String periodFrom = state + .educationalBackground[index].periodFrom!; + String periodTo = state + .educationalBackground[index].periodTo!; + String? program = state + .educationalBackground[index] + .education! + .course == + null + ? null + : state.educationalBackground[index] + .education!.course!.program!; + List? honors = state + .educationalBackground[index].honors! + .toList(); + String school = state + .educationalBackground[index] + .education! + .school! + .name!; + return Column( children: [ + Container( + decoration: box1(), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: Text( + level, + style: Theme.of( + context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight + .w500), + )), + Text( + "$periodFrom - ", + style: + Theme.of(context) + .textTheme + .bodyMedium, + ), + ], + ), + const SizedBox( + height: 5, + ), + Text( + school, + style: Theme.of(context) + .textTheme + .titleSmall, + ), + Container( + padding: + const EdgeInsets + .only(top: 8), + child: honors.isNotEmpty + ? Column( + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + const Text( + " : ", + style: TextStyle( + fontWeight: + FontWeight.w600), + ), + Column( + children: honors + .map((Honor + honor) => + Text(" - ${honor.name!}")) + .toList(), + ), + ], + ) + : const SizedBox()), + program == null + ? const SizedBox() + : Column( + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + const SizedBox( + height: 5, + ), + Text(program), + ], + ), + ]), + ), + IconButton( + onPressed: () {}, + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + )) + ], + ), + ), const SizedBox( height: 5, ), - Text(program), ], - ), - ]), - ), - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - )) - ], - ), - ), - const SizedBox( - height: 5, - ), - ], - ); - }):const EmptyData(message: "You don't have any Educational Background added. Please click + to add."), - ); + ); + }); + } else { + const EmptyData( + message: + "You don't have any Educational Background added. Please click + to add."); + } + }if(state is EducationalBackgroundErrorState){ + return SomethingWentWrong(message: state.message, onpressed: (){}); + } + return Container(); + }, + ); + } + return Container(); + }, + ); + } + return Container(); + }, + ), + )); } } diff --git a/lib/screens/profile/components/eligibility_screen.dart b/lib/screens/profile/components/eligibility_screen.dart index 2d3a75d..7fc3356 100644 --- a/lib/screens/profile/components/eligibility_screen.dart +++ b/lib/screens/profile/components/eligibility_screen.dart @@ -75,7 +75,7 @@ class EligibiltyScreen extends StatelessWidget { listener: (context, state) { if (state is EligibilityLoadingState) { final progress = ProgressHUD.of(context); - progress!.showWithText("Loading"); + progress!.showWithText("Please wait..."); } if (state is EligibilityLoaded || state is AddEligibilityState || diff --git a/lib/screens/profile/profile.dart b/lib/screens/profile/profile.dart index 91c203b..e6ed37b 100644 --- a/lib/screens/profile/profile.dart +++ b/lib/screens/profile/profile.dart @@ -8,6 +8,7 @@ import 'package:fluttericon/elusive_icons.dart'; import 'package:fluttericon/entypo_icons.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:fluttericon/modern_pictograms_icons.dart'; +import 'package:unit2/bloc/education/education_bloc.dart'; import 'package:unit2/bloc/eligibility/eligibility_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/workHistory/workHistory_bloc.dart'; @@ -17,6 +18,7 @@ import 'package:unit2/screens/profile/components/basic_information/citizenship_s import 'package:unit2/screens/profile/components/basic_information/contact_information_screen.dart'; import 'package:unit2/screens/profile/components/basic_information/identification_information_screen.dart'; import 'package:unit2/screens/profile/components/basic_information/primary_information_screen.dart'; +import 'package:unit2/screens/profile/components/education_screen.dart'; import 'package:unit2/screens/profile/components/eligibility_screen.dart'; import 'package:unit2/screens/profile/components/family_background_screen.dart'; import 'package:unit2/screens/profile/components/learning_and_development_screen.dart'; @@ -157,13 +159,13 @@ class _ProfileInfoState extends State { icon: FontAwesome5.graduation_cap, title: "Education", onTap: () { - // Navigator.push(context, MaterialPageRoute( - // builder: (BuildContext context) { - // return EducationScreen( - // educationBackgrounds: state - // .profileInformation - // .educationalBackgrounds); - // })); + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return BlocProvider( + create: (context) => EducationBloc()..add(GetEducationalBackground(profileId: profileId!, token: token!)), + child: const EducationScreen(), + ); + })); }, ), const Divider(), diff --git a/lib/sevices/profile/education_services.dart b/lib/sevices/profile/education_services.dart index e69de29..1d6d93e 100644 --- a/lib/sevices/profile/education_services.dart +++ b/lib/sevices/profile/education_services.dart @@ -0,0 +1,41 @@ +import 'dart:convert'; + +import 'package:unit2/model/profile/educational_background.dart'; +import 'package:unit2/utils/request.dart'; + +import '../../utils/urls.dart'; +import 'package:http/http.dart' as http; + +class EducationService { + static final EducationService _instance = EducationService(); + static EducationService get instace => _instance; + + Future> getEducationalBackground( + int profileId, String token) async { + List educationalBackgrounds = []; + String authToken = "Token $token"; + String path = "${Url.instance.getEducationalBackgrounds()}$profileId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + try { + http.Response response = await Request.instance + .getRequest(path: path, param: {}, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((var education) { + EducationalBackground educationalBackground = + EducationalBackground.fromJson(education); + educationalBackgrounds.add(educationalBackground); + }); + } + } + } catch (e) { + throw e.toString(); + } + + return educationalBackgrounds; + } +} diff --git a/lib/sevices/profile/work_history_services.dart b/lib/sevices/profile/work_history_services.dart index b42de20..b62f526 100644 --- a/lib/sevices/profile/work_history_services.dart +++ b/lib/sevices/profile/work_history_services.dart @@ -18,7 +18,7 @@ class WorkHistoryService{ 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': authToken }; - String path= Url.instance.workHistories()+profileId.toString(); + String path= Url.instance.getWorkHistories()+profileId.toString(); // try{ http.Response response =await Request.instance.getRequest(path: path, headers: headers, param: {}); if(response.statusCode == 200){ diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index f0d11fe..b3506fb 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -42,10 +42,15 @@ String updateEligibility(){ return "/api/jobnet_app/profile/pds/eligibility/"; } //// work history paths -String workHistories(){ +String getWorkHistories(){ return "/api/jobnet_app/profile/pds/work/"; } +////educational background paths +String getEducationalBackgrounds(){ + return "/api/jobnet_app/profile/pds/education/"; +} + // location utils path String getCounties(){ return "/api/jobnet_app/countries/"; From 74c2c7ef5944902e478d51a87dba03352f0c1038 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Thu, 2 Mar 2023 16:22:31 +0800 Subject: [PATCH 43/86] Learning and development refactor and create its own bloc --- .../learning_development_bloc.dart | 28 +++ .../learning_development_event.dart | 17 ++ .../learning_development_state.dart | 28 +++ lib/model/profile/learning_development.dart | 2 +- .../learning_and_development_screen.dart | 216 +++++++++++++----- lib/screens/profile/profile.dart | 15 +- .../profile/learningDevelopment_service.dart | 41 ++++ lib/utils/urls.dart | 10 +- 8 files changed, 295 insertions(+), 62 deletions(-) create mode 100644 lib/bloc/learningDevelopment/learning_development_bloc.dart create mode 100644 lib/bloc/learningDevelopment/learning_development_event.dart create mode 100644 lib/bloc/learningDevelopment/learning_development_state.dart create mode 100644 lib/sevices/profile/learningDevelopment_service.dart diff --git a/lib/bloc/learningDevelopment/learning_development_bloc.dart b/lib/bloc/learningDevelopment/learning_development_bloc.dart new file mode 100644 index 0000000..4457b4d --- /dev/null +++ b/lib/bloc/learningDevelopment/learning_development_bloc.dart @@ -0,0 +1,28 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/sevices/profile/learningDevelopment_service.dart'; + +import '../../model/profile/learning_development.dart'; + +part 'learning_development_event.dart'; +part 'learning_development_state.dart'; + +class LearningDevelopmentBloc + extends Bloc { + LearningDevelopmentBloc() : super(LearningDevelopmentInitial()) { + List learningsAndDevelopments; + on((event, emit) async { + // try { + emit(LearningDevelopmentLoadingState()); + List learnings = await LearningDevelopmentServices + .instance + .getLearningDevelopments(event.profileId, event.token); + learningsAndDevelopments = learnings; + emit(LearningDevelopmentLoadedState( + learningsAndDevelopment: learningsAndDevelopments)); + // } catch (e) { + // emit(LeaningDevelopmentErrorState(message: e.toString())); + // } + }); + } +} diff --git a/lib/bloc/learningDevelopment/learning_development_event.dart b/lib/bloc/learningDevelopment/learning_development_event.dart new file mode 100644 index 0000000..9aedc7d --- /dev/null +++ b/lib/bloc/learningDevelopment/learning_development_event.dart @@ -0,0 +1,17 @@ +part of 'learning_development_bloc.dart'; + +abstract class LearningDevelopmentEvent extends Equatable { + const LearningDevelopmentEvent(); + + @override + List get props => []; +} + +class GetLearningDevelopments extends LearningDevelopmentEvent { + final int profileId; + final String token; + const GetLearningDevelopments({required this.profileId, required this.token}); + + @override + List get props => [profileId, token]; +} diff --git a/lib/bloc/learningDevelopment/learning_development_state.dart b/lib/bloc/learningDevelopment/learning_development_state.dart new file mode 100644 index 0000000..c4b2337 --- /dev/null +++ b/lib/bloc/learningDevelopment/learning_development_state.dart @@ -0,0 +1,28 @@ +part of 'learning_development_bloc.dart'; + +abstract class LearningDevelopmentState extends Equatable { + const LearningDevelopmentState(); + + @override + List get props => []; +} + +class LearningDevelopmentInitial extends LearningDevelopmentState {} + +class LearningDevelopmentLoadedState extends LearningDevelopmentState{ + final List learningsAndDevelopment; + const LearningDevelopmentLoadedState({required this.learningsAndDevelopment}); + @override + List get props => [learningsAndDevelopment]; +} + +class LeaningDevelopmentErrorState extends LearningDevelopmentState{ + final String message; + const LeaningDevelopmentErrorState({required this.message}); + @override + List get props => [message]; +} + +class LearningDevelopmentLoadingState extends LearningDevelopmentState{ + +} diff --git a/lib/model/profile/learning_development.dart b/lib/model/profile/learning_development.dart index 11bb21b..ce7a045 100644 --- a/lib/model/profile/learning_development.dart +++ b/lib/model/profile/learning_development.dart @@ -23,7 +23,7 @@ class LearningDevelopement { final dynamic attachments; final EdBy? sponsoredBy; final ConductedTraining? conductedTraining; - final int? totalHoursAttended; + final double? totalHoursAttended; factory LearningDevelopement.fromJson(Map json) => LearningDevelopement( attachments: json["attachments"], diff --git a/lib/screens/profile/components/learning_and_development_screen.dart b/lib/screens/profile/components/learning_and_development_screen.dart index e7c9a4a..d77859c 100644 --- a/lib/screens/profile/components/learning_and_development_screen.dart +++ b/lib/screens/profile/components/learning_and_development_screen.dart @@ -1,7 +1,13 @@ import 'package:flutter/material.dart'; import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:intl/intl.dart'; +import 'package:unit2/bloc/learningDevelopment/learning_development_bloc.dart'; +import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/learning_development.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; @@ -9,62 +15,168 @@ import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; +import 'package:unit2/widgets/error_state.dart'; class LearningAndDevelopmentScreen extends StatelessWidget { - final List learningDevelopments; - const LearningAndDevelopmentScreen({super.key, required this.learningDevelopments}); - + const LearningAndDevelopmentScreen( + {super.key,}); @override Widget build(BuildContext context) { - DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); - return Scaffold( - appBar: AppBar(title: const Text(learningAndDevelopmentScreenTitle), - centerTitle: true, - backgroundColor: primary, - actions: [AddLeading(onPressed: (){})], - ), - body: learningDevelopments.isNotEmpty? ListView.builder( - padding: const EdgeInsets.symmetric(vertical: 8,horizontal: 10), - itemCount: learningDevelopments.length, - itemBuilder: (BuildContext context, int index){ - String training = learningDevelopments[index].conductedTraining!.title!.title!; - String provider = learningDevelopments[index].conductedTraining!.conductedBy!.name!; - String start = dteFormat2.format(learningDevelopments[index].conductedTraining!.fromDate!); - String end = dteFormat2.format(learningDevelopments[index].conductedTraining!.toDate!); - String type = learningDevelopments[index].conductedTraining!.learningDevelopmentType!.title!; - return Column( - children: [ - Container( - decoration: box1(), - padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), - width: screenWidth, - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(training,style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500),), - const Divider(), - const SizedBox(height: 5,), - Text(provider,style: Theme.of(context).textTheme.titleSmall,), - const SizedBox(height: 5,), - Text("$duration : $start TO $end",style: Theme.of(context).textTheme.labelMedium,), - const SizedBox(height: 5,), - Text("$type : $type",style: Theme.of(context).textTheme.labelMedium,), - ]), - ), - IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert)), - ], - ), - ), - const SizedBox(height: 8,), - ], - ); - }):const EmptyData(message: "You don't have any Learning and Development added. Please click + to add."), - ); + DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); + return Scaffold( + appBar: AppBar( + title: const Text(learningAndDevelopmentScreenTitle), + centerTitle: true, + backgroundColor: primary, + actions: [AddLeading(onPressed: () {})], + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + indicatorWidget: const SpinKitFadingCircle( + color: Colors.white, + ), + backgroundColor: Colors.black87, + child: BlocBuilder( + builder: (context, state) { + if (state is UserLoggedIn) { + return BlocBuilder( + builder: (context, state) { + if (state is ProfileLoaded) { + return BlocConsumer( + listener: (context, state) { + if (state is LearningDevelopmentLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is LearningDevelopmentLoadedState || + state is LeaningDevelopmentErrorState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + // TODO: implement listener + }, + builder: (context, state) { + if (state is LearningDevelopmentLoadedState) { + if (state.learningsAndDevelopment.isNotEmpty) { + return ListView.builder( + padding: const EdgeInsets.symmetric( + vertical: 8, horizontal: 10), + itemCount: + state.learningsAndDevelopment.length, + itemBuilder: + (BuildContext context, int index) { + String training = state + .learningsAndDevelopment[index] + .conductedTraining! + .title! + .title!; + String provider = state + .learningsAndDevelopment[index] + .conductedTraining! + .conductedBy! + .name!; + String start = dteFormat2.format(state + .learningsAndDevelopment[index] + .conductedTraining! + .fromDate!); + String end = dteFormat2.format(state + .learningsAndDevelopment[index] + .conductedTraining! + .toDate!); + String type = state + .learningsAndDevelopment[index] + .conductedTraining! + .learningDevelopmentType! + .title!; + return Column( + children: [ + Container( + decoration: box1(), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + width: screenWidth, + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + Text( + training, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight + .w500), + ), + const Divider(), + const SizedBox( + height: 5, + ), + Text( + provider, + style: Theme.of(context) + .textTheme + .titleSmall, + ), + const SizedBox( + height: 5, + ), + Text( + "$duration : $start to $end'", + style: Theme.of(context) + .textTheme + .labelMedium, + ), + const SizedBox( + height: 5, + ), + + ]), + ), + IconButton( + onPressed: () {}, + icon: const Icon( + Icons.more_vert)), + ], + ), + ), + const SizedBox( + height: 8, + ), + ], + ); + }); + } else { + const EmptyData( + message: + "You don't have any Learning and Development added. Please click + to add."); + } + } + if (state is LeaningDevelopmentErrorState) { + return (SomethingWentWrong( + message: state.message, onpressed: () {})); + } + return Container(); + }, + ); + } + return Container(); + }, + ); + } + return Container(); + }, + ), + )); } -} \ No newline at end of file +} diff --git a/lib/screens/profile/profile.dart b/lib/screens/profile/profile.dart index e6ed37b..7441db5 100644 --- a/lib/screens/profile/profile.dart +++ b/lib/screens/profile/profile.dart @@ -10,6 +10,7 @@ import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:fluttericon/modern_pictograms_icons.dart'; import 'package:unit2/bloc/education/education_bloc.dart'; import 'package:unit2/bloc/eligibility/eligibility_bloc.dart'; +import 'package:unit2/bloc/learningDevelopment/learning_development_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/workHistory/workHistory_bloc.dart'; import 'package:unit2/model/login_data/employee_info/employee_info.dart'; @@ -215,13 +216,13 @@ class _ProfileInfoState extends State { icon: Elusive.lightbulb, title: "Learning & Development", onTap: () { - // Navigator.push(context, MaterialPageRoute( - // builder: (BuildContext context) { - // return LearningAndDevelopmentScreen( - // learningDevelopments: state - // .profileInformation - // .learningsAndDevelopment); - // })); + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return BlocProvider( + create: (context) => LearningDevelopmentBloc()..add(GetLearningDevelopments(profileId: profileId!, token: token!)), + child: const LearningAndDevelopmentScreen(), + ); + })); }, ), const Divider(), diff --git a/lib/sevices/profile/learningDevelopment_service.dart b/lib/sevices/profile/learningDevelopment_service.dart new file mode 100644 index 0000000..9cf8154 --- /dev/null +++ b/lib/sevices/profile/learningDevelopment_service.dart @@ -0,0 +1,41 @@ +import 'dart:convert'; + +import 'package:unit2/utils/request.dart'; + +import '../../model/profile/learning_development.dart'; +import '../../utils/urls.dart'; +import 'package:http/http.dart' as http; + +class LearningDevelopmentServices { + static final LearningDevelopmentServices _instance = + LearningDevelopmentServices(); + static LearningDevelopmentServices get instance => _instance; + + Future> getLearningDevelopments( + int profileId, String token) async { + List learningsAndDevelopments = []; + String authToken = "Token $token"; + String path = "${Url.instance.getLearningAndDevelopments()}$profileId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + // try { + http.Response response = await Request.instance + .getRequest(path: path, param: {}, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((var learnings) { + LearningDevelopement learningDevelopement = + LearningDevelopement.fromJson(learnings); + learningsAndDevelopments.add(learningDevelopement); + }); + } + } + // } catch (e) { + // throw e.toString(); + // } + return learningsAndDevelopments; + } +} diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index b3506fb..b5bd7db 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -5,8 +5,8 @@ class Url { String host() { // // return '192.168.10.221:3003'; // return 'agusandelnorte.gov.ph'; - // return "192.168.10.219:3000"; - return "devweb.agusandelnorte.gov.ph"; + return "192.168.10.219:3000"; + // return "devweb.agusandelnorte.gov.ph"; // return 'devapi.agusandelnorte.gov.ph:3004'; } @@ -51,6 +51,12 @@ String getEducationalBackgrounds(){ return "/api/jobnet_app/profile/pds/education/"; } +//// learning and development paths + +String getLearningAndDevelopments(){ + return "api/jobnet_app/profile/pds/learning_development/"; +} + // location utils path String getCounties(){ return "/api/jobnet_app/countries/"; From c83572cfb1a5a6767ae70193fd78579cf9bc1df3 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Mon, 6 Mar 2023 08:57:39 +0800 Subject: [PATCH 44/86] Personal refences refactor and created its own bloc --- lib/bloc/references/references_bloc.dart | 25 +++ lib/bloc/references/references_event.dart | 16 ++ lib/bloc/references/references_state.dart | 29 +++ .../learning_and_development_screen.dart | 2 +- .../profile/components/references_screen.dart | 188 +++++++++++------- lib/screens/profile/profile.dart | 14 +- lib/sevices/profile/references_services.dart | 40 ++++ lib/utils/urls.dart | 9 +- 8 files changed, 246 insertions(+), 77 deletions(-) create mode 100644 lib/bloc/references/references_bloc.dart create mode 100644 lib/bloc/references/references_event.dart create mode 100644 lib/bloc/references/references_state.dart create mode 100644 lib/sevices/profile/references_services.dart diff --git a/lib/bloc/references/references_bloc.dart b/lib/bloc/references/references_bloc.dart new file mode 100644 index 0000000..569f774 --- /dev/null +++ b/lib/bloc/references/references_bloc.dart @@ -0,0 +1,25 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/sevices/profile/references_services.dart'; +import '../../model/profile/references.dart'; + +part 'references_event.dart'; +part 'references_state.dart'; + + +class ReferencesBloc extends Bloc { + List references = []; + ReferencesBloc() : super(ReferencesInitial()) { + on((event, emit) async{ + emit(ReferencesLoadingState()); + try{ + List refs = await ReferencesServices.instace.getRefences(event.profileId, event.token); + references = refs; + emit(ReferencesLoadedState(references: references)); + }catch(e){ + ReferencesErrorState(message: e.toString()); + } + + }); + } +} diff --git a/lib/bloc/references/references_event.dart b/lib/bloc/references/references_event.dart new file mode 100644 index 0000000..4b6737a --- /dev/null +++ b/lib/bloc/references/references_event.dart @@ -0,0 +1,16 @@ +part of 'references_bloc.dart'; + +abstract class ReferencesEvent extends Equatable { + const ReferencesEvent(); + + @override + List get props => []; +} + +class GetReferences extends ReferencesEvent{ + final int profileId; + final String token; + const GetReferences({required this.profileId, required this.token}); + @override + List get props => [profileId,token]; +} diff --git a/lib/bloc/references/references_state.dart b/lib/bloc/references/references_state.dart new file mode 100644 index 0000000..13c2284 --- /dev/null +++ b/lib/bloc/references/references_state.dart @@ -0,0 +1,29 @@ +part of 'references_bloc.dart'; + +abstract class ReferencesState extends Equatable { + const ReferencesState(); + + @override + List get props => []; +} + +class ReferencesInitial extends ReferencesState {} + +class ReferencesLoadedState extends ReferencesState{ + final List references; + const ReferencesLoadedState({required this.references}); + + @override + List get props => [references]; + +} +class ReferencesErrorState extends ReferencesState{ + final String message; + const ReferencesErrorState({required this.message}); + + @override + List get props => [message]; +} +class ReferencesLoadingState extends ReferencesState{ + +} diff --git a/lib/screens/profile/components/learning_and_development_screen.dart b/lib/screens/profile/components/learning_and_development_screen.dart index d77859c..7f9f7cd 100644 --- a/lib/screens/profile/components/learning_and_development_screen.dart +++ b/lib/screens/profile/components/learning_and_development_screen.dart @@ -132,7 +132,7 @@ class LearningAndDevelopmentScreen extends StatelessWidget { height: 5, ), Text( - "$duration : $start to $end'", + "$duration: $start to $end", style: Theme.of(context) .textTheme .labelMedium, diff --git a/lib/screens/profile/components/references_screen.dart b/lib/screens/profile/components/references_screen.dart index b02608e..cd9b6ae 100644 --- a/lib/screens/profile/components/references_screen.dart +++ b/lib/screens/profile/components/references_screen.dart @@ -1,6 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/references/references_bloc.dart'; +import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/references.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; @@ -9,78 +15,124 @@ import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; class ReferencesScreen extends StatelessWidget { - final List references; - const ReferencesScreen({super.key, required this.references}); - + const ReferencesScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: const Text(referencesScreenTitle), - centerTitle: true, - backgroundColor: primary, - actions: [AddLeading(onPressed: (){})], - ), - body: references.isNotEmpty? ListView.builder( - padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 10), - itemCount: references.length, - itemBuilder: (BuildContext context, int index) { - String fullname = - "${references[index].firstName} ${references[index].middleName} ${references[index].lastName}"; - String addres = - "${references[index].address!.cityMunicipality!.description}, ${references[index].address!.cityMunicipality!.province!.description}, ${references[0].address!.cityMunicipality!.province!.region!.description}"; - String mobile = references[index].contactNo.toString(); - return Column( - children: [ - Container( + appBar: AppBar( + title: const Text(referencesScreenTitle), + centerTitle: true, + backgroundColor: primary, + actions: [AddLeading(onPressed: () {})], + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle( + color: Colors.white), + child: BlocBuilder( + builder: (context, state) { + //userbloc + if (state is UserLoggedIn) { + return BlocBuilder( + builder: (context, state) { + //profilebloc + if (state is ProfileLoaded) { + return BlocConsumer( + //listener + listener: (context, state) { + if(state is ReferencesLoadingState){ + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if(state is ReferencesLoadedState || state is ReferencesErrorState){ + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + }, + //builder + builder: (context, state) { + //references bloc + if (state is ReferencesLoadedState) { + if (state.references.isNotEmpty) { + return ListView.builder( padding: - const EdgeInsets.symmetric(horizontal: 12, vertical: 8), - decoration: box1(), - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(fullname, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith(fontWeight: FontWeight.w500)), - const Divider(), - const SizedBox( - height: 5, - ), - Text(addres, - style: Theme.of(context) - .textTheme - .titleSmall! - .copyWith(fontWeight: FontWeight.w500)), - const SizedBox( - height: 8, - ), - Text("${mobileOrPhone.toUpperCase()} : $mobile", - style: - Theme.of(context).textTheme.labelMedium!), - ], + const EdgeInsets.symmetric(vertical: 8, horizontal: 10), + itemCount: state.references.length, + itemBuilder: (BuildContext context, int index) { + String fullname = + "${state.references[index].firstName} ${state.references[index].middleName} ${state.references[index].lastName}"; + String addres = + "${state.references[index].address!.cityMunicipality!.description}, ${state.references[index].address!.cityMunicipality!.province!.description}, ${state.references[0].address!.cityMunicipality!.province!.region!.description}"; + String mobile = state.references[index].contactNo.toString(); + return Column( + children: [ + Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + decoration: box1(), + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(fullname, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: FontWeight.w500)), + const Divider(), + const SizedBox( + height: 5, + ), + Text(addres, + style: Theme.of(context) + .textTheme + .titleSmall! + .copyWith( + fontWeight: FontWeight.w500)), + const SizedBox( + height: 8, + ), + Text("${mobileOrPhone.toUpperCase()} : $mobile", + style: Theme.of(context) + .textTheme + .labelMedium!), + ], + ), + ), + IconButton( + onPressed: () {}, + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + )) + ], + ), ), - ), - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - )) - ], - ), - ), - const SizedBox( - height: 5, - ), - ], - ); - }):const EmptyData(message: "You don't have any References added. Please click + to add."), - ); + const SizedBox( + height: 5, + ), + ], + ); + }); + } + } + return Container(); + }, + ); + } + return Container(); + }, + ); + } + return Container(); + }, + ), + )); } } + diff --git a/lib/screens/profile/profile.dart b/lib/screens/profile/profile.dart index 7441db5..951b2e7 100644 --- a/lib/screens/profile/profile.dart +++ b/lib/screens/profile/profile.dart @@ -12,6 +12,7 @@ import 'package:unit2/bloc/education/education_bloc.dart'; import 'package:unit2/bloc/eligibility/eligibility_bloc.dart'; import 'package:unit2/bloc/learningDevelopment/learning_development_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/references/references_bloc.dart'; import 'package:unit2/bloc/workHistory/workHistory_bloc.dart'; import 'package:unit2/model/login_data/employee_info/employee_info.dart'; import 'package:unit2/screens/profile/components/basic_information/address_screen.dart'; @@ -230,12 +231,13 @@ class _ProfileInfoState extends State { icon: Brandico.codepen, title: "Personal References", onTap: () { - // Navigator.push(context, MaterialPageRoute( - // builder: (BuildContext context) { - // return ReferencesScreen( - // references: - // state.profileInformation.references); - // })); + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return BlocProvider( + create: (context) => ReferencesBloc()..add(GetReferences(profileId: profileId!, token: token!)), + child: const ReferencesScreen(), + ); + })); }, ), ExpandableGroup( diff --git a/lib/sevices/profile/references_services.dart b/lib/sevices/profile/references_services.dart new file mode 100644 index 0000000..f425f0d --- /dev/null +++ b/lib/sevices/profile/references_services.dart @@ -0,0 +1,40 @@ + + + +import 'dart:convert'; + +import 'package:unit2/utils/request.dart'; + +import '../../model/profile/references.dart'; +import '../../utils/urls.dart'; +import 'package:http/http.dart' as http; +class ReferencesServices{ + static final ReferencesServices _instance = ReferencesServices(); + static ReferencesServices get instace => _instance; + + Future> getRefences(int profileId, String token)async{ + +List references = []; + String authToken = "Token $token"; + String path = "${Url.instance.getRefences()}$profileId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + try{ + http.Response response = await Request.instance.getRequest(path: path,param: {},headers: headers); + if(response.statusCode == 200){ + Map data = jsonDecode(response.body); + if(data['data'] != null){ + data['data'].forEach((var ref){ + PersonalReference reference = PersonalReference.fromJson(ref); + references.add(reference); + }); + } + } + }catch(e){ + throw e.toString(); + } + return references; + } +} \ No newline at end of file diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index b5bd7db..412a4b2 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -5,9 +5,9 @@ class Url { String host() { // // return '192.168.10.221:3003'; // return 'agusandelnorte.gov.ph'; - return "192.168.10.219:3000"; + // return "192.168.10.219:3000"; // return "devweb.agusandelnorte.gov.ph"; - // return 'devapi.agusandelnorte.gov.ph:3004'; + return 'devapi.agusandelnorte.gov.ph:3004'; } String authentication() { @@ -57,6 +57,11 @@ String getLearningAndDevelopments(){ return "api/jobnet_app/profile/pds/learning_development/"; } +//// references paths +String getRefences(){ + return "/api/jobnet_app/profile/pds/personal_reference/"; +} + // location utils path String getCounties(){ return "/api/jobnet_app/countries/"; From 4151038ff3de96b040e91dbd862b56c53b452252 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Mon, 6 Mar 2023 09:50:20 +0800 Subject: [PATCH 45/86] volunaty work & civic service refactor and created its own bloc --- .../voluntary_works/voluntary_work_bloc.dart | 25 +++ .../voluntary_works/voluntary_work_event.dart | 16 ++ .../voluntary_works/voluntary_work_state.dart | 27 +++ .../components/voluntary_works_screen.dart | 197 ++++++++++++------ lib/screens/profile/profile.dart | 14 +- lib/sevices/profile/volunatary_services.dart | 39 ++++ lib/utils/urls.dart | 5 + 7 files changed, 253 insertions(+), 70 deletions(-) create mode 100644 lib/bloc/voluntary_works/voluntary_work_bloc.dart create mode 100644 lib/bloc/voluntary_works/voluntary_work_event.dart create mode 100644 lib/bloc/voluntary_works/voluntary_work_state.dart create mode 100644 lib/sevices/profile/volunatary_services.dart diff --git a/lib/bloc/voluntary_works/voluntary_work_bloc.dart b/lib/bloc/voluntary_works/voluntary_work_bloc.dart new file mode 100644 index 0000000..f12ce40 --- /dev/null +++ b/lib/bloc/voluntary_works/voluntary_work_bloc.dart @@ -0,0 +1,25 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/sevices/profile/volunatary_services.dart'; +import 'package:unit2/sevices/profile/work_history_services.dart'; + +import '../../model/profile/voluntary_works.dart'; + +part 'voluntary_work_event.dart'; +part 'voluntary_work_state.dart'; + +class VoluntaryWorkBloc extends Bloc { + VoluntaryWorkBloc() : super(VoluntaryWorkInitial()) { + List voluntaryWorks = []; + on((event, emit) async{ + emit(VoluntaryWorkLoadingState()); + try{ + List works = await VoluntaryService.instance.getVoluntaryWorks(event.profileId, event.token); + voluntaryWorks = works; + emit(VoluntaryWorkLoadedState(voluntaryWorks: voluntaryWorks)); + }catch(e){ + emit(VoluntaryWorkErrorState(message: e.toString())); + } + }); + } +} diff --git a/lib/bloc/voluntary_works/voluntary_work_event.dart b/lib/bloc/voluntary_works/voluntary_work_event.dart new file mode 100644 index 0000000..0e62010 --- /dev/null +++ b/lib/bloc/voluntary_works/voluntary_work_event.dart @@ -0,0 +1,16 @@ +part of 'voluntary_work_bloc.dart'; + +abstract class VoluntaryWorkEvent extends Equatable { + const VoluntaryWorkEvent(); + + @override + List get props => []; +} + +class GetVoluntarWorks extends VoluntaryWorkEvent{ + final int profileId; + final String token; + const GetVoluntarWorks({required this.profileId, required this.token}); + @override + List get props => [profileId,token]; +} diff --git a/lib/bloc/voluntary_works/voluntary_work_state.dart b/lib/bloc/voluntary_works/voluntary_work_state.dart new file mode 100644 index 0000000..8c2c912 --- /dev/null +++ b/lib/bloc/voluntary_works/voluntary_work_state.dart @@ -0,0 +1,27 @@ +part of 'voluntary_work_bloc.dart'; + +abstract class VoluntaryWorkState extends Equatable { + const VoluntaryWorkState(); + + @override + List get props => []; +} + +class VoluntaryWorkInitial extends VoluntaryWorkState {} +class VoluntaryWorkLoadedState extends VoluntaryWorkState{ + final List voluntaryWorks; + const VoluntaryWorkLoadedState({required this.voluntaryWorks}); + @override + List get props => [voluntaryWorks]; +} + +class VoluntaryWorkErrorState extends VoluntaryWorkState{ + final String message; + const VoluntaryWorkErrorState({required this.message}); + @override + List get props => [message]; +} + +class VoluntaryWorkLoadingState extends VoluntaryWorkState{ + +} diff --git a/lib/screens/profile/components/voluntary_works_screen.dart b/lib/screens/profile/components/voluntary_works_screen.dart index 17c33b8..18b89f5 100644 --- a/lib/screens/profile/components/voluntary_works_screen.dart +++ b/lib/screens/profile/components/voluntary_works_screen.dart @@ -1,6 +1,11 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:intl/intl.dart'; -import 'package:unit2/model/profile/voluntary_works.dart'; +import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/user/user_bloc.dart'; +import 'package:unit2/bloc/voluntary_works/voluntary_work_bloc.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; @@ -8,8 +13,7 @@ import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; class VolunataryWorkScreen extends StatelessWidget { - final List voluntaryWorks; - const VolunataryWorkScreen({super.key, required this.voluntaryWorks}); + const VolunataryWorkScreen({super.key}); @override Widget build(BuildContext context) { @@ -18,68 +22,133 @@ class VolunataryWorkScreen extends StatelessWidget { appBar: AppBar( title: const Text(voluntaryScreenTitle), backgroundColor: primary, - actions: [AddLeading(onPressed: (){})], + actions: [AddLeading(onPressed: () {})], ), - body: voluntaryWorks.isNotEmpty? ListView.builder( - itemCount: voluntaryWorks.length, - padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 10), - itemBuilder: (BuildContext context, int index) { - String position = voluntaryWorks[index].position!.title!; - String agency = voluntaryWorks[index].agency!.name!; - String from = dteFormat2.format(voluntaryWorks[index].fromDate!); - String hours = voluntaryWorks[index].totalHours.toString(); - String? to = voluntaryWorks[index].toDate == null - ? "Present" - : dteFormat2.format(voluntaryWorks[index].toDate!); - return Column( - children: [ - Container( - decoration: box1(), - padding: - const EdgeInsets.symmetric(horizontal: 12, vertical: 8), - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - position, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith(fontWeight: FontWeight.w500), - ), - const SizedBox( - height: 5, - ), - Text( - agency, - style: Theme.of(context).textTheme.titleSmall, - ), - const Divider(), - const SizedBox( - height: 3, - ), - Text("$duration : $from to $to"), - const SizedBox( - height: 5, - ), - Text("$numberOfHours : $hours hours"), - ]), - ), - IconButton( - onPressed: () {}, icon: const Icon(Icons.more_vert)) - ], - ), - ), - const SizedBox( - height: 5, - ), - ], - ); - }):const EmptyData(message: "You don't have any Voluntary Works added. Please click + to add."), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocBuilder( + builder: (context, state) { + if (state is UserLoggedIn) { + return BlocBuilder( + builder: (context, state) { + if (state is ProfileLoaded) { + return BlocConsumer( + listener: (context, state) { + if (state is VoluntaryWorkLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is VoluntaryWorkLoadedState || + state is VoluntaryWorkErrorState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + }, + builder: (context, state) { + if (state is VoluntaryWorkLoadedState) { + if (state.voluntaryWorks.isNotEmpty) { + return ListView.builder( + itemCount: state.voluntaryWorks.length, + padding: const EdgeInsets.symmetric( + vertical: 8, horizontal: 10), + itemBuilder: + (BuildContext context, int index) { + String position = state + .voluntaryWorks[index].position!.title!; + String agency = state + .voluntaryWorks[index].agency!.name!; + String from = dteFormat2.format( + state.voluntaryWorks[index].fromDate!); + String hours = state + .voluntaryWorks[index].totalHours + .toString(); + String? to = + state.voluntaryWorks[index].toDate == + null + ? "Present" + : dteFormat2.format(state + .voluntaryWorks[index].toDate!); + return Column( + children: [ + Container( + decoration: box1(), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + Text( + position, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight + .w500), + ), + const SizedBox( + height: 5, + ), + Text( + agency, + style: Theme.of(context) + .textTheme + .titleSmall, + ), + const Divider(), + const SizedBox( + height: 3, + ), + Text( + "$duration : $from to $to"), + const SizedBox( + height: 5, + ), + Text( + "$numberOfHours : $hours hours"), + ]), + ), + IconButton( + onPressed: () {}, + icon: const Icon( + Icons.more_vert)) + ], + ), + ), + const SizedBox( + height: 5, + ), + ], + ); + }); + } else { + return const EmptyData( + message: + "You don't have any Voluntary Works added. Please click + to add."); + } + } + return Container(); + }, + ); + } + return Container(); + }, + ); + } + return Container(); + }, + )), ); } } diff --git a/lib/screens/profile/profile.dart b/lib/screens/profile/profile.dart index 951b2e7..f6256d2 100644 --- a/lib/screens/profile/profile.dart +++ b/lib/screens/profile/profile.dart @@ -13,6 +13,7 @@ import 'package:unit2/bloc/eligibility/eligibility_bloc.dart'; import 'package:unit2/bloc/learningDevelopment/learning_development_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/references/references_bloc.dart'; +import 'package:unit2/bloc/voluntary_works/voluntary_work_bloc.dart'; import 'package:unit2/bloc/workHistory/workHistory_bloc.dart'; import 'package:unit2/model/login_data/employee_info/employee_info.dart'; import 'package:unit2/screens/profile/components/basic_information/address_screen.dart'; @@ -204,12 +205,13 @@ class _ProfileInfoState extends State { icon: FontAwesome5.walking, title: "Voluntary Work & Civic Services", onTap: () { - // Navigator.push(context, MaterialPageRoute( - // builder: (BuildContext context) { - // return VolunataryWorkScreen( - // voluntaryWorks: state - // .profileInformation.voluntaryWorks); - // })); + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return BlocProvider( + create: (context) => VoluntaryWorkBloc()..add(GetVoluntarWorks(profileId: profileId!, token: token!)), + child: const VolunataryWorkScreen(), + ); + })); }, ), const Divider(), diff --git a/lib/sevices/profile/volunatary_services.dart b/lib/sevices/profile/volunatary_services.dart new file mode 100644 index 0000000..41d4974 --- /dev/null +++ b/lib/sevices/profile/volunatary_services.dart @@ -0,0 +1,39 @@ + +import 'dart:convert'; + +import 'package:http/http.dart' as http; +import 'package:unit2/utils/request.dart'; +import '../../model/profile/voluntary_works.dart'; +import '../../utils/urls.dart'; + +class VoluntaryService{ + static final VoluntaryService _instance = VoluntaryService(); + static VoluntaryService get instance => _instance; + + Future< List> getVoluntaryWorks(int profileId, String token)async{ + List voluntaryWorks = []; + String authToken = "Token $token"; + String path = "${Url.instance.getVoluntaryWorks()}$profileId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + + try{ + http.Response response = await Request.instance.getRequest(path: path,param: {},headers: headers); + if(response.statusCode == 200){ + Map data = jsonDecode(response.body); + if(data['data'] != null){ + data['data'].forEach((var work){ + VoluntaryWork voluntaryWork = VoluntaryWork.fromJson(work); + voluntaryWorks.add(voluntaryWork); + }); + } + } + }catch(e){ + throw(e.toString()); + } + +return voluntaryWorks; + } +} \ No newline at end of file diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index 412a4b2..1908f93 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -62,6 +62,11 @@ String getRefences(){ return "/api/jobnet_app/profile/pds/personal_reference/"; } + +////voluntary works +String getVoluntaryWorks(){ + return "/api/jobnet_app/profile/pds/voluntary_work/"; +} // location utils path String getCounties(){ return "/api/jobnet_app/countries/"; From acc74653d18b0658f6ff22b414504ae78da61d78 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Mon, 6 Mar 2023 13:29:38 +0800 Subject: [PATCH 46/86] Organization membership refactor and created its own bloc --- .../bloc/organization_membership_bloc.dart | 24 +++ .../bloc/organization_membership_event.dart | 16 ++ .../bloc/organization_membership_state.dart | 28 ++++ lib/bloc/hobbies/hoobies_bloc.dart | 24 +++ lib/bloc/hobbies/hoobies_event.dart | 16 ++ lib/bloc/hobbies/hoobies_state.dart | 29 ++++ .../profile/components/education_screen.dart | 54 +++--- .../org_membership_screen.dart | 157 +++++++++++++----- .../skills_and_hobbies_screen.dart | 115 +++++++++---- lib/screens/profile/profile.dart | 72 +++++--- .../profile/orgmembership_services.dart | 42 +++++ lib/sevices/skillshobbies_services.dart | 40 +++++ lib/utils/urls.dart | 10 ++ 13 files changed, 496 insertions(+), 131 deletions(-) create mode 100644 lib/bloc/bloc/organization_membership_bloc.dart create mode 100644 lib/bloc/bloc/organization_membership_event.dart create mode 100644 lib/bloc/bloc/organization_membership_state.dart create mode 100644 lib/bloc/hobbies/hoobies_bloc.dart create mode 100644 lib/bloc/hobbies/hoobies_event.dart create mode 100644 lib/bloc/hobbies/hoobies_state.dart create mode 100644 lib/sevices/profile/orgmembership_services.dart create mode 100644 lib/sevices/skillshobbies_services.dart diff --git a/lib/bloc/bloc/organization_membership_bloc.dart b/lib/bloc/bloc/organization_membership_bloc.dart new file mode 100644 index 0000000..83e0260 --- /dev/null +++ b/lib/bloc/bloc/organization_membership_bloc.dart @@ -0,0 +1,24 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/sevices/profile/orgmembership_services.dart'; + +import '../../model/profile/other_information/organization_memberships.dart'; + +part 'organization_membership_event.dart'; +part 'organization_membership_state.dart'; + +class OrganizationMembershipBloc extends Bloc { + OrganizationMembershipBloc() : super(OrganizationMembershipInitial()) { + List organizationMemberships=[]; + on((event, emit) async{ + emit(OrgmembershipLoadingState()); + try{ + List orgs = await OrganizationMembershipServices.instance.getOrgMemberships(event.profileId, event.token); + organizationMemberships = orgs; + emit(OrganizationMembershipLoaded(orgMemberships: organizationMemberships)); + }catch(e){ + OrganizationMembershipErrorState(message: e.toString()); + } + }); + } +} diff --git a/lib/bloc/bloc/organization_membership_event.dart b/lib/bloc/bloc/organization_membership_event.dart new file mode 100644 index 0000000..9375837 --- /dev/null +++ b/lib/bloc/bloc/organization_membership_event.dart @@ -0,0 +1,16 @@ +part of 'organization_membership_bloc.dart'; + +abstract class OrganizationMembershipEvent extends Equatable { + const OrganizationMembershipEvent(); + + @override + List get props => []; +} + +class GetOrganizationMembership extends OrganizationMembershipEvent{ + final int profileId; + final String token; + const GetOrganizationMembership({required this.profileId, required this.token}); + @override + List get props => [profileId,token]; +} diff --git a/lib/bloc/bloc/organization_membership_state.dart b/lib/bloc/bloc/organization_membership_state.dart new file mode 100644 index 0000000..c922624 --- /dev/null +++ b/lib/bloc/bloc/organization_membership_state.dart @@ -0,0 +1,28 @@ +part of 'organization_membership_bloc.dart'; + +abstract class OrganizationMembershipState extends Equatable { + const OrganizationMembershipState(); + + @override + List get props => []; +} + +class OrganizationMembershipInitial extends OrganizationMembershipState {} + +class OrganizationMembershipLoaded extends OrganizationMembershipState{ + final ListorgMemberships; + const OrganizationMembershipLoaded({required this.orgMemberships}); + @override + List get props => [orgMemberships]; +} + +class OrganizationMembershipErrorState extends OrganizationMembershipState{ + final String message; + const OrganizationMembershipErrorState({required this.message}); + @override + List get props => [message]; +} + +class OrgmembershipLoadingState extends OrganizationMembershipState{ + +} diff --git a/lib/bloc/hobbies/hoobies_bloc.dart b/lib/bloc/hobbies/hoobies_bloc.dart new file mode 100644 index 0000000..6453222 --- /dev/null +++ b/lib/bloc/hobbies/hoobies_bloc.dart @@ -0,0 +1,24 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/sevices/skillshobbies_services.dart'; + +import '../../model/profile/other_information/skills_and_hobbies.dart'; + +part 'hoobies_event.dart'; +part 'hoobies_state.dart'; + +class HoobiesBloc extends Bloc { + HoobiesBloc() : super(HoobiesInitial()) { + List skillsAndHobbies = []; + on((event, emit)async { + emit(HobbiesLoadingState()); + try{ + List hobbies = await SkillsHobbiesServices.instance.getSkillsHobbies(event.profileId, event.token); + skillsAndHobbies = hobbies; + emit(HobbiesLoadedState(skillsAndHobbies: skillsAndHobbies)); + }catch(e){ + emit(HobbiesErrorState(message: e.toString())); + } + }); + } +} diff --git a/lib/bloc/hobbies/hoobies_event.dart b/lib/bloc/hobbies/hoobies_event.dart new file mode 100644 index 0000000..a78f0f9 --- /dev/null +++ b/lib/bloc/hobbies/hoobies_event.dart @@ -0,0 +1,16 @@ +part of 'hoobies_bloc.dart'; + +abstract class HobbiesEvent extends Equatable { + const HobbiesEvent(); + + @override + List get props => []; +} + +class GetSkillsHobbies extends HobbiesEvent{ + final int profileId; + final String token; + const GetSkillsHobbies({required this.profileId, required this.token}); + @override + List get props => [profileId,token]; +} \ No newline at end of file diff --git a/lib/bloc/hobbies/hoobies_state.dart b/lib/bloc/hobbies/hoobies_state.dart new file mode 100644 index 0000000..c374892 --- /dev/null +++ b/lib/bloc/hobbies/hoobies_state.dart @@ -0,0 +1,29 @@ +part of 'hoobies_bloc.dart'; + +abstract class HobbiesState extends Equatable { + const HobbiesState(); + + @override + List get props => []; +} + +class HoobiesInitial extends HobbiesState {} + +class HobbiesLoadedState extends HobbiesState{ + final List skillsAndHobbies; + const HobbiesLoadedState({required this.skillsAndHobbies}); + @override + List get props => [skillsAndHobbies]; +} +class HobbiesErrorState extends HobbiesState{ + final String message; + const HobbiesErrorState({required this.message}); + + @override + List get props => [message]; + +} + +class HobbiesLoadingState extends HobbiesState{ + +} diff --git a/lib/screens/profile/components/education_screen.dart b/lib/screens/profile/components/education_screen.dart index 67cd194..0e9860f 100644 --- a/lib/screens/profile/components/education_screen.dart +++ b/lib/screens/profile/components/education_screen.dart @@ -15,7 +15,6 @@ import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; class EducationScreen extends StatelessWidget { - const EducationScreen({super.key}); @override @@ -29,10 +28,9 @@ class EducationScreen extends StatelessWidget { ), //userbloc body: ProgressHUD( - padding: const EdgeInsets.all(24), - backgroundColor: Colors.black87, - indicatorWidget: const SpinKitFadingCircle( - color: Colors.white), + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), child: BlocBuilder( builder: (context, state) { if (state is UserLoggedIn) { @@ -43,14 +41,15 @@ class EducationScreen extends StatelessWidget { //education bloc return BlocConsumer( listener: (context, state) { - if (state is EducationalBackgroundLoadingState) { - final progress = ProgressHUD.of(context); - progress!.showWithText("Please wait..."); - } - if(state is EducationalBackgroundLoadedState || state is EducationalBackgroundErrorState){ - final progress = ProgressHUD.of(context); - progress!.dismiss(); - } + if (state is EducationalBackgroundLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is EducationalBackgroundLoadedState || + state is EducationalBackgroundErrorState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } }, builder: (context, state) { if (state is EducationalBackgroundLoadedState) { @@ -59,13 +58,15 @@ class EducationScreen extends StatelessWidget { padding: const EdgeInsets.symmetric( vertical: 8, horizontal: 10), itemCount: state.educationalBackground.length, - itemBuilder: (BuildContext context, int index) { + itemBuilder: + (BuildContext context, int index) { String level = state .educationalBackground[index] .education! .level!; String periodFrom = state - .educationalBackground[index].periodFrom!; + .educationalBackground[index] + .periodFrom!; String periodTo = state .educationalBackground[index].periodTo!; String? program = state @@ -97,7 +98,8 @@ class EducationScreen extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: - CrossAxisAlignment.start, + CrossAxisAlignment + .start, children: [ Row( children: [ @@ -115,10 +117,10 @@ class EducationScreen extends StatelessWidget { )), Text( "$periodFrom - ", - style: - Theme.of(context) - .textTheme - .bodyMedium, + style: Theme.of( + context) + .textTheme + .bodyMedium, ), ], ), @@ -135,7 +137,8 @@ class EducationScreen extends StatelessWidget { padding: const EdgeInsets .only(top: 8), - child: honors.isNotEmpty + child: honors + .isNotEmpty ? Column( mainAxisAlignment: MainAxisAlignment @@ -152,8 +155,7 @@ class EducationScreen extends StatelessWidget { ), Column( children: honors - .map((Honor - honor) => + .map((Honor honor) => Text(" - ${honor.name!}")) .toList(), ), @@ -198,8 +200,10 @@ class EducationScreen extends StatelessWidget { message: "You don't have any Educational Background added. Please click + to add."); } - }if(state is EducationalBackgroundErrorState){ - return SomethingWentWrong(message: state.message, onpressed: (){}); + } + if (state is EducationalBackgroundErrorState) { + return SomethingWentWrong( + message: state.message, onpressed: () {}); } return Container(); }, diff --git a/lib/screens/profile/components/other_information/org_membership_screen.dart b/lib/screens/profile/components/other_information/org_membership_screen.dart index 01b870f..8cb6c45 100644 --- a/lib/screens/profile/components/other_information/org_membership_screen.dart +++ b/lib/screens/profile/components/other_information/org_membership_screen.dart @@ -1,4 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:unit2/bloc/bloc/organization_membership_bloc.dart'; +import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/other_information/organization_memberships.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; @@ -9,54 +15,115 @@ import 'package:unit2/widgets/empty_data.dart'; import '../../../../utils/global.dart'; class OrgMembershipsScreen extends StatelessWidget { - final List orgMemberships; - const OrgMembershipsScreen({super.key, required this.orgMemberships}); + const OrgMembershipsScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: const Text(orgMembershipTitle), - backgroundColor: primary, - centerTitle: true, - actions: [AddLeading(onPressed: (){})], - ), - body: orgMemberships.isNotEmpty? ListView.builder( - itemCount: orgMemberships.length, - padding: const EdgeInsets.symmetric(vertical: 8,horizontal: 10), - itemBuilder: (BuildContext context, int index) { - String entity = - orgMemberships[index].agency!.privateEntity == false - ? governmentText.toUpperCase() - : privateText.toUpperCase(); - String agencyName = orgMemberships[index].agency!.name!; - return Column( - children: [ - Container( - width: screenWidth, - decoration: box1(), - padding: - const EdgeInsets.symmetric(horizontal: 12, vertical: 8), - child: Row(children: [ - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - - Text(agencyName,style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500),), - const Divider(), - Text(entity,style: Theme.of(context).textTheme.labelLarge,), - ], - )), - IconButton( - onPressed: () {}, icon: const Icon(Icons.more_vert,color: Colors.grey,)) - ]), - ), - const SizedBox(height: 5,), - ], - ); - }):const EmptyData(message: "You don't have any Organization Membership added. Please click + to add."), - ); + appBar: AppBar( + title: const Text(orgMembershipTitle), + backgroundColor: primary, + centerTitle: true, + actions: [AddLeading(onPressed: () {})], + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocBuilder( + builder: (context, state) { + if (state is UserLoggedIn) { + return BlocBuilder( + builder: (context, state) { + if (state is ProfileLoaded) { + return BlocConsumer( + listener: (context, state) { + if(state is OrgmembershipLoadingState){ + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if(state is OrganizationMembershipLoaded || state is OrganizationMembershipErrorState){ + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + }, + builder: (context, state) { + if (state is OrganizationMembershipLoaded) { + return ListView.builder( + itemCount: state.orgMemberships.length, + padding: const EdgeInsets.symmetric( + vertical: 8, horizontal: 10), + itemBuilder: (BuildContext context, int index) { + String entity = state.orgMemberships[index] + .agency! + .privateEntity == + false + ? governmentText.toUpperCase() + : privateText.toUpperCase(); + String agencyName = + state.orgMemberships[index].agency!.name!; + return Column( + children: [ + Container( + width: screenWidth, + decoration: box1(), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + child: Row(children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + agencyName, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight.w500), + ), + const Divider(), + Text( + entity, + style: Theme.of(context) + .textTheme + .labelLarge, + ), + ], + )), + IconButton( + onPressed: () {}, + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + )) + ]), + ), + const SizedBox( + height: 5, + ), + ], + ); + }); + } + return Container(); + }, + ); + } + return Container(); + }, + ); + } + return Container(); + }, + ), + )); } } + +// const EmptyData(message: "You don't have any Organization Membership added. Please click + to add."), diff --git a/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart b/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart index 07f6455..dad7d6d 100644 --- a/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart +++ b/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart @@ -1,6 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:unit2/bloc/hobbies/hoobies_bloc.dart'; +import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/other_information/skills_and_hobbies.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; @@ -9,43 +15,82 @@ import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; class SkillHobbiesScreen extends StatelessWidget { - final ListskillsHobbies; - const SkillHobbiesScreen({super.key,required this.skillsHobbies}); + const SkillHobbiesScreen({super.key}); @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: const Text(skillAndHobbiesTitle), - backgroundColor: primary, - centerTitle: true, - actions: [AddLeading(onPressed: (){})], - - ), - body: skillsHobbies.isNotEmpty? Padding( - padding: const EdgeInsets.all(24), - child: Wrap( - spacing: 8, - runSpacing: 8, - alignment: WrapAlignment.start, - clipBehavior: Clip.none, - verticalDirection: VerticalDirection.up, - crossAxisAlignment: WrapCrossAlignment.start, - direction: Axis.horizontal, - children:skillsHobbies.map((SkillsHobbies sh){ - return FittedBox( - child: Row( - children: [ - Text(sh.name!), - IconButton( - onPressed: () {}, - icon: const Icon(Icons.close)), - ], - ), - ); - }).toList() - + return Scaffold( + appBar: AppBar( + title: const Text(skillAndHobbiesTitle), + backgroundColor: primary, + centerTitle: true, + actions: [AddLeading(onPressed: () {})], ), - ):const EmptyData(message: "You don't have any Skills and Hobbies added. Please click + to add"), - ); + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocBuilder( + builder: (context, state) { + if (state is UserLoggedIn) { + return BlocBuilder( + builder: (context, state) { + if (state is ProfileLoaded) { + return BlocConsumer( + listener: (context, state) { + if (state is HobbiesLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + }if(state is HobbiesLoadedState || state is HobbiesErrorState){ + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + }, + builder: (context, state) { + if (state is HobbiesLoadedState) { + if (state.skillsAndHobbies.isNotEmpty) { + return Padding( + padding: const EdgeInsets.all(24), + child: Wrap( + spacing: 8, + runSpacing: 8, + alignment: WrapAlignment.start, + clipBehavior: Clip.none, + verticalDirection: VerticalDirection.up, + crossAxisAlignment: + WrapCrossAlignment.start, + direction: Axis.horizontal, + children: + state.skillsAndHobbies.map((SkillsHobbies sh) { + return FittedBox( + child: Row( + children: [ + Text(sh.name!), + IconButton( + onPressed: () {}, + icon: const Icon(Icons.close)), + ], + ), + ); + }).toList()), + ); + } else { + const EmptyData( + message: + "You don't have any Skills and Hobbies added. Please click + to add"); + } + } + return Container(); + }, + ); + } + return Container(); + }, + ); + } + return Container(); + }, + ), + )); } -} \ No newline at end of file +} diff --git a/lib/screens/profile/profile.dart b/lib/screens/profile/profile.dart index f6256d2..2d46664 100644 --- a/lib/screens/profile/profile.dart +++ b/lib/screens/profile/profile.dart @@ -8,8 +8,10 @@ import 'package:fluttericon/elusive_icons.dart'; import 'package:fluttericon/entypo_icons.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:fluttericon/modern_pictograms_icons.dart'; +import 'package:unit2/bloc/bloc/organization_membership_bloc.dart'; import 'package:unit2/bloc/education/education_bloc.dart'; import 'package:unit2/bloc/eligibility/eligibility_bloc.dart'; +import 'package:unit2/bloc/hobbies/hoobies_bloc.dart'; import 'package:unit2/bloc/learningDevelopment/learning_development_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/references/references_bloc.dart'; @@ -153,19 +155,19 @@ class _ProfileInfoState extends State { MainMenu( icon: Elusive.group, title: "Family", - onTap: () { - - }, + onTap: () {}, ), const Divider(), MainMenu( icon: FontAwesome5.graduation_cap, title: "Education", onTap: () { - Navigator.push(context, MaterialPageRoute( + Navigator.push(context, MaterialPageRoute( builder: (BuildContext context) { return BlocProvider( - create: (context) => EducationBloc()..add(GetEducationalBackground(profileId: profileId!, token: token!)), + create: (context) => EducationBloc() + ..add(GetEducationalBackground( + profileId: profileId!, token: token!)), child: const EducationScreen(), ); })); @@ -179,7 +181,9 @@ class _ProfileInfoState extends State { Navigator.push(context, MaterialPageRoute( builder: (BuildContext context) { return BlocProvider( - create: (context) => EligibilityBloc()..add(GetEligibilities(profileId: profileId!, token: token!)), + create: (context) => EligibilityBloc() + ..add(GetEligibilities( + profileId: profileId!, token: token!)), child: const EligibiltyScreen(), ); })); @@ -193,11 +197,12 @@ class _ProfileInfoState extends State { Navigator.push(context, MaterialPageRoute( builder: (BuildContext context) { return BlocProvider( - create: (context) => WorkHistoryBloc()..add(GetWorkHistories(profileId: profileId!, token: token!)), + create: (context) => WorkHistoryBloc() + ..add(GetWorkHistories( + profileId: profileId!, token: token!)), child: const WorkHistoryScreen(), ); })); - }, ), const Divider(), @@ -205,10 +210,12 @@ class _ProfileInfoState extends State { icon: FontAwesome5.walking, title: "Voluntary Work & Civic Services", onTap: () { - Navigator.push(context, MaterialPageRoute( + Navigator.push(context, MaterialPageRoute( builder: (BuildContext context) { return BlocProvider( - create: (context) => VoluntaryWorkBloc()..add(GetVoluntarWorks(profileId: profileId!, token: token!)), + create: (context) => VoluntaryWorkBloc() + ..add(GetVoluntarWorks( + profileId: profileId!, token: token!)), child: const VolunataryWorkScreen(), ); })); @@ -219,10 +226,12 @@ class _ProfileInfoState extends State { icon: Elusive.lightbulb, title: "Learning & Development", onTap: () { - Navigator.push(context, MaterialPageRoute( + Navigator.push(context, MaterialPageRoute( builder: (BuildContext context) { return BlocProvider( - create: (context) => LearningDevelopmentBloc()..add(GetLearningDevelopments(profileId: profileId!, token: token!)), + create: (context) => LearningDevelopmentBloc() + ..add(GetLearningDevelopments( + profileId: profileId!, token: token!)), child: const LearningAndDevelopmentScreen(), ); })); @@ -233,10 +242,12 @@ class _ProfileInfoState extends State { icon: Brandico.codepen, title: "Personal References", onTap: () { - Navigator.push(context, MaterialPageRoute( + Navigator.push(context, MaterialPageRoute( builder: (BuildContext context) { return BlocProvider( - create: (context) => ReferencesBloc()..add(GetReferences(profileId: profileId!, token: token!)), + create: (context) => ReferencesBloc() + ..add(GetReferences( + profileId: profileId!, token: token!)), child: const ReferencesScreen(), ); })); @@ -260,24 +271,33 @@ class _ProfileInfoState extends State { subMenu( Icons.fitness_center, "Skills & Hobbies", () { - // Navigator.push(context, MaterialPageRoute( - // builder: (BuildContext context) { - // return SkillHobbiesScreen( - // skillsHobbies: state.profileInformation - // .otherInformation.skillsAndHobbies); - // })); + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return BlocProvider( + create: (context) => HoobiesBloc() + ..add(GetSkillsHobbies( + profileId: profileId!, + token: token!)), + child: const SkillHobbiesScreen(), + ); + })); }), subMenu(FontAwesome5.certificate, "Organization Memberships", () { - // Navigator.push(context, MaterialPageRoute( - // builder: (BuildContext context) { - // return OrgMembershipsScreen( - // orgMemberships: state.profileInformation - // .otherInformation.orgMemberships); - // })); + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return BlocProvider( + create: (context) => OrganizationMembershipBloc() + ..add(GetOrganizationMembership( + profileId: profileId!, + token: token!)), + child: const OrgMembershipsScreen(), + ); + })); }), subMenu(Entypo.doc_text, "Non-Academic Recognitions", () { + // Navigator.push(context, MaterialPageRoute( // builder: (BuildContext context) { // return NonAcademicRecognitionScreen( diff --git a/lib/sevices/profile/orgmembership_services.dart b/lib/sevices/profile/orgmembership_services.dart new file mode 100644 index 0000000..6bf8fd4 --- /dev/null +++ b/lib/sevices/profile/orgmembership_services.dart @@ -0,0 +1,42 @@ +import 'dart:convert'; + +import 'package:unit2/utils/request.dart'; + +import '../../model/profile/other_information/organization_memberships.dart'; +import 'package:http/http.dart' as http; + +import '../../utils/urls.dart'; + +class OrganizationMembershipServices { + static final OrganizationMembershipServices _instance = + OrganizationMembershipServices(); + static OrganizationMembershipServices get instance => _instance; + + Future> getOrgMemberships( + int profileId, String token) async { + List orgMemberships = []; + String authToken = "Token $token"; + String path = "${Url.instance.getOrgMemberShips()}$profileId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + try { + http.Response response = await Request.instance + .getRequest(path: path, param: {}, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((var org) { + OrganizationMembership organizationMembership = + OrganizationMembership.fromJson(org); + orgMemberships.add(organizationMembership); + }); + } + } + } catch (e) { + throw e.toString(); + } + return orgMemberships; + } +} diff --git a/lib/sevices/skillshobbies_services.dart b/lib/sevices/skillshobbies_services.dart new file mode 100644 index 0000000..2666033 --- /dev/null +++ b/lib/sevices/skillshobbies_services.dart @@ -0,0 +1,40 @@ + + +import 'dart:convert'; + +import 'package:unit2/utils/request.dart'; + +import '../model/profile/other_information/skills_and_hobbies.dart'; +import '../utils/urls.dart'; +import 'package:http/http.dart' as http; +class SkillsHobbiesServices{ + static final SkillsHobbiesServices _instance = SkillsHobbiesServices(); + static SkillsHobbiesServices get instance => _instance; + + Future> getSkillsHobbies(int profileId, String token)async{ + + List skillsAndHobbies = []; + String authToken = "Token $token"; + String path = "${Url.instance.getSkillsHobbies()}$profileId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + try{ + http.Response response = await Request.instance.getRequest(path: path,param: {},headers: headers); + if(response.statusCode == 200){ + Map data = jsonDecode(response.body); + if(data['data']['skill_hobby'] != null){ + data['data']['skill_hobby'].forEach((var hobby){ + SkillsHobbies skillsHobby = SkillsHobbies.fromJson(hobby); + skillsAndHobbies.add(skillsHobby); + }); + } + } + }catch(e){ + throw e.toString(); + } + return skillsAndHobbies; + } +} + diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index 1908f93..788b37f 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -67,6 +67,16 @@ String getRefences(){ String getVoluntaryWorks(){ return "/api/jobnet_app/profile/pds/voluntary_work/"; } + +//// skills hobbies +String getSkillsHobbies(){ + return "/api/jobnet_app/profile/pds/other/skill_hobby/"; +} +//// orgmemberships +String getOrgMemberShips(){ + return "/api/jobnet_app/profile/pds/other/org_membership/"; +} + // location utils path String getCounties(){ return "/api/jobnet_app/countries/"; From 2a57e7453b6972879118db7d9422ba9490da97ce Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Mon, 6 Mar 2023 14:16:49 +0800 Subject: [PATCH 47/86] non academic recognition refactor created its own bloc --- .../non_academic_recognition_bloc.dart | 24 +++ .../non_academic_recognition_event.dart | 16 ++ .../non_academic_recognition_state.dart | 28 ++++ .../organization_membership_bloc.dart | 0 .../organization_membership_event.dart | 0 .../organization_membership_state.dart | 0 .../non_academic_recognition_screen.dart | 157 +++++++++++++----- .../org_membership_screen.dart | 2 +- lib/screens/profile/profile.dart | 21 ++- .../profile/non_academic_services.dart | 41 +++++ lib/utils/urls.dart | 5 + 11 files changed, 246 insertions(+), 48 deletions(-) create mode 100644 lib/bloc/non_academic_recognition.dart/non_academic_recognition_bloc.dart create mode 100644 lib/bloc/non_academic_recognition.dart/non_academic_recognition_event.dart create mode 100644 lib/bloc/non_academic_recognition.dart/non_academic_recognition_state.dart rename lib/bloc/{bloc => org_membership}/organization_membership_bloc.dart (100%) rename lib/bloc/{bloc => org_membership}/organization_membership_event.dart (100%) rename lib/bloc/{bloc => org_membership}/organization_membership_state.dart (100%) create mode 100644 lib/sevices/profile/non_academic_services.dart diff --git a/lib/bloc/non_academic_recognition.dart/non_academic_recognition_bloc.dart b/lib/bloc/non_academic_recognition.dart/non_academic_recognition_bloc.dart new file mode 100644 index 0000000..8a7cdf7 --- /dev/null +++ b/lib/bloc/non_academic_recognition.dart/non_academic_recognition_bloc.dart @@ -0,0 +1,24 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/sevices/profile/non_academic_services.dart'; + +import '../../model/profile/other_information/non_acedimic_recognition.dart'; + +part 'non_academic_recognition_event.dart'; +part 'non_academic_recognition_state.dart'; + +class NonAcademicRecognitionBloc extends Bloc { + NonAcademicRecognitionBloc() : super(NonAcademicRecognitionInitial()) { + List nonAcademicRecognitions = []; + on((event, emit)async { + emit(NonAcademicRecognitionLoadingState()); + try{ + List recognitions = await NonAcademicRecognitionServices.instance.getNonAcademicRecognition(event.profileId, event.token); + nonAcademicRecognitions = recognitions; + emit(NonAcademicRecognitionLoadedState(nonAcademicRecognition: nonAcademicRecognitions)); + }catch(e){ + emit(NonAcademicRecognitionErrorState(message: e.toString())); + } + }); + } +} diff --git a/lib/bloc/non_academic_recognition.dart/non_academic_recognition_event.dart b/lib/bloc/non_academic_recognition.dart/non_academic_recognition_event.dart new file mode 100644 index 0000000..c262400 --- /dev/null +++ b/lib/bloc/non_academic_recognition.dart/non_academic_recognition_event.dart @@ -0,0 +1,16 @@ +part of 'non_academic_recognition_bloc.dart'; + +abstract class NonAcademicRecognitionEvent extends Equatable { + const NonAcademicRecognitionEvent(); + + @override + List get props => []; +} + +class GetNonAcademicRecognition extends NonAcademicRecognitionEvent{ + final int profileId; + final String token; + const GetNonAcademicRecognition({required this.profileId, required this.token}); + @override + List get props => [profileId,token]; +} diff --git a/lib/bloc/non_academic_recognition.dart/non_academic_recognition_state.dart b/lib/bloc/non_academic_recognition.dart/non_academic_recognition_state.dart new file mode 100644 index 0000000..7d4ada6 --- /dev/null +++ b/lib/bloc/non_academic_recognition.dart/non_academic_recognition_state.dart @@ -0,0 +1,28 @@ +part of 'non_academic_recognition_bloc.dart'; + +abstract class NonAcademicRecognitionState extends Equatable { + const NonAcademicRecognitionState(); + + @override + List get props => []; +} + +class NonAcademicRecognitionInitial extends NonAcademicRecognitionState {} + +class NonAcademicRecognitionLoadingState extends NonAcademicRecognitionState{ + +} +class NonAcademicRecognitionLoadedState extends NonAcademicRecognitionState{ + final List nonAcademicRecognition; + const NonAcademicRecognitionLoadedState({required this.nonAcademicRecognition}); + @override + List get props => []; +} + +class NonAcademicRecognitionErrorState extends NonAcademicRecognitionState{ + final String message; + const NonAcademicRecognitionErrorState({required this.message}); + + @override + List get props => []; +} diff --git a/lib/bloc/bloc/organization_membership_bloc.dart b/lib/bloc/org_membership/organization_membership_bloc.dart similarity index 100% rename from lib/bloc/bloc/organization_membership_bloc.dart rename to lib/bloc/org_membership/organization_membership_bloc.dart diff --git a/lib/bloc/bloc/organization_membership_event.dart b/lib/bloc/org_membership/organization_membership_event.dart similarity index 100% rename from lib/bloc/bloc/organization_membership_event.dart rename to lib/bloc/org_membership/organization_membership_event.dart diff --git a/lib/bloc/bloc/organization_membership_state.dart b/lib/bloc/org_membership/organization_membership_state.dart similarity index 100% rename from lib/bloc/bloc/organization_membership_state.dart rename to lib/bloc/org_membership/organization_membership_state.dart diff --git a/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart b/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart index 4af8aa7..385ae45 100644 --- a/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart +++ b/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart @@ -1,5 +1,10 @@ import 'package:flutter/material.dart'; -import 'package:unit2/model/profile/other_information/non_acedimic_recognition.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:unit2/bloc/non_academic_recognition.dart/non_academic_recognition_bloc.dart'; +import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; @@ -8,44 +13,120 @@ import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; class NonAcademicRecognitionScreen extends StatelessWidget { - final List nonAcademicRecognitions; - const NonAcademicRecognitionScreen({super.key, required this.nonAcademicRecognitions}); + const NonAcademicRecognitionScreen({ + super.key, + }); @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: const Text(nonAcademicRecTitle), centerTitle: true, backgroundColor: primary,actions: [AddLeading(onPressed: (){})],), - body: nonAcademicRecognitions.isNotEmpty?ListView.builder( - padding: const EdgeInsets.symmetric(vertical: 8,horizontal: 10), - itemCount: nonAcademicRecognitions.length, - itemBuilder: (BuildContext context, int index){ - String award = nonAcademicRecognitions[index].title!; - String presenter = nonAcademicRecognitions[index].presenter!.name!; - return Column( - children: [ - Container( - width: screenWidth, - decoration: box1(), - padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), - child: Row( - children: [ - Expanded(child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(award,style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500),), - const Divider(), - Text(presenter), - ],)), - - IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert,color: Colors.grey,)) - ], - ), - ), - const SizedBox(height: 5,), - ], - ); - }): const EmptyData(message: "You don't have any Non Academic Recognition added. Please click + to add"), - ); + return Scaffold( + appBar: AppBar( + title: const Text(nonAcademicRecTitle), + centerTitle: true, + backgroundColor: primary, + actions: [AddLeading(onPressed: () {})], + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocBuilder( + builder: (context, state) { + if (state is UserLoggedIn) { + return BlocBuilder( + builder: (context, state) { + if (state is ProfileLoaded) { + return BlocConsumer( + listener: (context, state) { + if (state is NonAcademicRecognitionLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is NonAcademicRecognitionLoadedState || + state is NonAcademicRecognitionErrorState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + }, + builder: (context, state) { + if (state is NonAcademicRecognitionLoadedState) { + if (state.nonAcademicRecognition.isNotEmpty) { + return ListView.builder( + padding: const EdgeInsets.symmetric( + vertical: 8, horizontal: 10), + itemCount: + state.nonAcademicRecognition.length, + itemBuilder: + (BuildContext context, int index) { + String award = state + .nonAcademicRecognition[index].title!; + String presenter = state + .nonAcademicRecognition[index] + .presenter! + .name!; + return Column( + children: [ + Container( + width: screenWidth, + decoration: box1(), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + award, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight + .w500), + ), + const Divider(), + Text(presenter), + ], + )), + IconButton( + onPressed: () {}, + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + )) + ], + ), + ), + const SizedBox( + height: 5, + ), + ], + ); + }); + } else { + const EmptyData( + message: + "You don't have any Non Academic Recognition added. Please click + to add"); + } + } + return Container(); + }, + ); + } + return Container(); + }, + ); + } + return Container(); + }, + ), + )); } -} \ No newline at end of file +} diff --git a/lib/screens/profile/components/other_information/org_membership_screen.dart b/lib/screens/profile/components/other_information/org_membership_screen.dart index 8cb6c45..480f01e 100644 --- a/lib/screens/profile/components/other_information/org_membership_screen.dart +++ b/lib/screens/profile/components/other_information/org_membership_screen.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; -import 'package:unit2/bloc/bloc/organization_membership_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/other_information/organization_memberships.dart'; @@ -12,6 +11,7 @@ import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; +import '../../../../bloc/org_membership/organization_membership_bloc.dart'; import '../../../../utils/global.dart'; class OrgMembershipsScreen extends StatelessWidget { diff --git a/lib/screens/profile/profile.dart b/lib/screens/profile/profile.dart index 2d46664..88daa42 100644 --- a/lib/screens/profile/profile.dart +++ b/lib/screens/profile/profile.dart @@ -8,11 +8,11 @@ import 'package:fluttericon/elusive_icons.dart'; import 'package:fluttericon/entypo_icons.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:fluttericon/modern_pictograms_icons.dart'; -import 'package:unit2/bloc/bloc/organization_membership_bloc.dart'; import 'package:unit2/bloc/education/education_bloc.dart'; import 'package:unit2/bloc/eligibility/eligibility_bloc.dart'; import 'package:unit2/bloc/hobbies/hoobies_bloc.dart'; import 'package:unit2/bloc/learningDevelopment/learning_development_bloc.dart'; +import 'package:unit2/bloc/non_academic_recognition.dart/non_academic_recognition_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/references/references_bloc.dart'; import 'package:unit2/bloc/voluntary_works/voluntary_work_bloc.dart'; @@ -35,6 +35,7 @@ import 'package:unit2/screens/profile/components/references_screen.dart'; import 'package:unit2/screens/profile/components/work_history_screen.dart'; import 'package:unit2/screens/profile/components/voluntary_works_screen.dart'; import 'package:unit2/theme-data.dart/colors.dart'; +import '../../bloc/org_membership/organization_membership_bloc.dart'; import '../../bloc/user/user_bloc.dart'; import 'components/main_menu.dart'; import 'components/submenu.dart'; @@ -298,14 +299,16 @@ class _ProfileInfoState extends State { subMenu(Entypo.doc_text, "Non-Academic Recognitions", () { - // Navigator.push(context, MaterialPageRoute( - // builder: (BuildContext context) { - // return NonAcademicRecognitionScreen( - // nonAcademicRecognitions: state - // .profileInformation - // .otherInformation - // .nonAcademicRecognition); - // })); + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return BlocProvider( + create: (context) => NonAcademicRecognitionBloc() + ..add(GetNonAcademicRecognition( + profileId: profileId!, + token: token!)), + child: const NonAcademicRecognitionScreen(), + ); + })); }), ]), ExpandableGroup( diff --git a/lib/sevices/profile/non_academic_services.dart b/lib/sevices/profile/non_academic_services.dart new file mode 100644 index 0000000..88812c7 --- /dev/null +++ b/lib/sevices/profile/non_academic_services.dart @@ -0,0 +1,41 @@ +import 'dart:convert'; + +import 'package:http/http.dart' as http; +import 'package:unit2/utils/request.dart'; + +import '../../model/profile/other_information/non_acedimic_recognition.dart'; +import '../../utils/urls.dart'; + +class NonAcademicRecognitionServices { + static final NonAcademicRecognitionServices _instance = + NonAcademicRecognitionServices(); + static NonAcademicRecognitionServices get instance => _instance; + + Future> getNonAcademicRecognition( + int profileId, String token) async { + List nonAcademicRecognitions = []; + String authToken = "Token $token"; + String path = "${Url.instance.getNonAcademicRecognition()}$profileId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + try { + http.Response response = await Request.instance + .getRequest(path: path, param: {}, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((var recognition) { + NonAcademicRecognition nonAcademicRecognition = + NonAcademicRecognition.fromJson(recognition); + nonAcademicRecognitions.add(nonAcademicRecognition); + }); + } + } + } catch (e) { + throw e.toString(); + } + return nonAcademicRecognitions; + } +} diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index 788b37f..a1603c5 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -77,6 +77,11 @@ String getOrgMemberShips(){ return "/api/jobnet_app/profile/pds/other/org_membership/"; } +////non academic recognition +String getNonAcademicRecognition(){ + return "/api/jobnet_app/profile/pds/other/non_acad_recognition/"; +} + // location utils path String getCounties(){ return "/api/jobnet_app/countries/"; From 4251002cbf263c43b4717a0f1320e8f9077dd518 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Mon, 6 Mar 2023 16:29:55 +0800 Subject: [PATCH 48/86] family background refactor and created its own bloc --- lib/bloc/family/family_bloc.dart | 24 + lib/bloc/family/family_event.dart | 17 + lib/bloc/family/family_state.dart | 28 + lib/model/profile/other_info.dart | 10 - lib/model/profile/profileInfomation.dart | 1 - .../contact_information_screen.dart | 2 +- .../components/family_background_screen.dart | 656 +++++++++++------- lib/screens/profile/profile.dart | 13 +- lib/sevices/profile/family_services.dart | 38 + lib/sevices/profile/profile_service.dart | 1 - lib/utils/urls.dart | 9 +- 11 files changed, 520 insertions(+), 279 deletions(-) create mode 100644 lib/bloc/family/family_bloc.dart create mode 100644 lib/bloc/family/family_event.dart create mode 100644 lib/bloc/family/family_state.dart delete mode 100644 lib/model/profile/other_info.dart create mode 100644 lib/sevices/profile/family_services.dart diff --git a/lib/bloc/family/family_bloc.dart b/lib/bloc/family/family_bloc.dart new file mode 100644 index 0000000..1d8289f --- /dev/null +++ b/lib/bloc/family/family_bloc.dart @@ -0,0 +1,24 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/sevices/profile/family_services.dart'; + +import '../../model/profile/family_backround.dart'; + +part 'family_event.dart'; +part 'family_state.dart'; + +class FamilyBloc extends Bloc { + FamilyBloc() : super(FamilyInitial()) { + List families = []; + on((event, emit) async{ + emit(FamilyLoadingState()); + try{ + List family = await FamilyService.instance.getFamilies(event.profileId, event.token); + families = family; + emit(FamilyLoaded(families: families)); + }catch(e){ + emit(FamilyErrorState(message: e.toString())); + } + }); + } +} diff --git a/lib/bloc/family/family_event.dart b/lib/bloc/family/family_event.dart new file mode 100644 index 0000000..366091c --- /dev/null +++ b/lib/bloc/family/family_event.dart @@ -0,0 +1,17 @@ +part of 'family_bloc.dart'; + +abstract class FamilyEvent extends Equatable { + const FamilyEvent(); + + @override + List get props => []; +} + +class GetFamilies extends FamilyEvent{ + final int profileId; + final String token; + const GetFamilies({required this.profileId, required this.token}); + + @override + List get props => [profileId,token]; +} diff --git a/lib/bloc/family/family_state.dart b/lib/bloc/family/family_state.dart new file mode 100644 index 0000000..7ac4003 --- /dev/null +++ b/lib/bloc/family/family_state.dart @@ -0,0 +1,28 @@ +part of 'family_bloc.dart'; + +abstract class FamilyState extends Equatable { + const FamilyState(); + + @override + List get props => []; +} + +class FamilyInitial extends FamilyState {} + +class FamilyLoaded extends FamilyState{ + final List families; + const FamilyLoaded({required this.families}); + + @override + List get props => [families]; +} + +class FamilyErrorState extends FamilyState{ + final String message; + const FamilyErrorState({required this.message}); + @override + List get props => [message]; +} +class FamilyLoadingState extends FamilyState{ + +} diff --git a/lib/model/profile/other_info.dart b/lib/model/profile/other_info.dart deleted file mode 100644 index ab2a6b7..0000000 --- a/lib/model/profile/other_info.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:unit2/model/profile/other_information/non_acedimic_recognition.dart'; -import 'package:unit2/model/profile/other_information/organization_memberships.dart'; -import 'package:unit2/model/profile/other_information/skills_and_hobbies.dart'; - -class OtherInformation{ - List skillsAndHobbies; - ListorgMemberships; - List nonAcademicRecognition; - OtherInformation({required this.skillsAndHobbies, required this.orgMemberships, required this.nonAcademicRecognition}); -} \ No newline at end of file diff --git a/lib/model/profile/profileInfomation.dart b/lib/model/profile/profileInfomation.dart index dc4e69c..53fc065 100644 --- a/lib/model/profile/profileInfomation.dart +++ b/lib/model/profile/profileInfomation.dart @@ -4,7 +4,6 @@ import 'package:unit2/model/profile/educational_background.dart'; import 'package:unit2/model/profile/eligibility.dart'; import 'package:unit2/model/profile/family_backround.dart'; import 'package:unit2/model/profile/learning_development.dart'; -import 'package:unit2/model/profile/other_info.dart'; import 'package:unit2/model/profile/references.dart'; import 'package:unit2/model/profile/voluntary_works.dart'; import 'package:unit2/model/profile/work_history.dart'; diff --git a/lib/screens/profile/components/basic_information/contact_information_screen.dart b/lib/screens/profile/components/basic_information/contact_information_screen.dart index 7c54f71..43edb28 100644 --- a/lib/screens/profile/components/basic_information/contact_information_screen.dart +++ b/lib/screens/profile/components/basic_information/contact_information_screen.dart @@ -20,7 +20,7 @@ class ContactInformationScreen extends StatelessWidget { backgroundColor: primary, actions: [AddLeading(onPressed: (){})], ), - body: contacts.isEmpty? ListView.builder( + body: contacts.isNotEmpty? ListView.builder( padding: const EdgeInsets.symmetric(vertical: 8,horizontal: 10), itemCount: contacts.length, itemBuilder: (BuildContext context, int index) { diff --git a/lib/screens/profile/components/family_background_screen.dart b/lib/screens/profile/components/family_background_screen.dart index 2592010..684df5c 100644 --- a/lib/screens/profile/components/family_background_screen.dart +++ b/lib/screens/profile/components/family_background_screen.dart @@ -1,4 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:unit2/bloc/family/family_bloc.dart'; +import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/family_backround.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; @@ -6,8 +12,9 @@ import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; class FamilyBackgroundScreen extends StatefulWidget { - final List familyBackground; - const FamilyBackgroundScreen({super.key, required this.familyBackground}); + const FamilyBackgroundScreen({ + super.key, + }); @override State createState() => _FamilyBackgroundScreenState(); @@ -21,279 +28,402 @@ class _FamilyBackgroundScreenState extends State { List otherRelated = []; @override Widget build(BuildContext context) { - father = widget.familyBackground - .firstWhere((element) => element.relationship!.id == 1); - mother = widget.familyBackground - .firstWhere((element) => element.relationship!.id == 2); - spouse = widget.familyBackground - .firstWhere((element) => element.relationship!.id == 3); - - // get all children - var childs = widget.familyBackground - .where((element) => element.relationship!.id == 4); - if (childs.isNotEmpty) { - for (var element in childs) { - children.add(element); - } - } - - //get all related persons - var relateds = widget.familyBackground - .where((element) => element.relationship!.id! > 4); - if (relateds.isNotEmpty) { - for (var element in relateds) { - otherRelated.add(element); - } - } return Scaffold( appBar: AppBar( title: const Text(familyBackgroundScreenTitle), centerTitle: true, backgroundColor: primary, ), - body: ListView(children: [ - //Father---------------------------------------------- - Container( - decoration: box1(), - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), - width: screenWidth, - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text(fatherText), - const SizedBox(height: 5,), - Text( - " ${father!.relatedPerson!.firstName} ${father!.relatedPerson!.middleName} ${father!.relatedPerson!.lastName} ${father!.relatedPerson!.nameExtension ?? ''},",style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500),), - Text(" $fullname",style: Theme.of(context).textTheme.bodySmall,), - Row( - children: [ - Checkbox(value: false, onChanged: (value) { - setState(() { - value = !value!; - }); - }), - const Text(incaseOfEmergency) - ], - ) - ]), - ), - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - )) - ], - ), - ), - const SizedBox( - height: 5, - ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocBuilder( + builder: (context, state) { + return BlocBuilder( + builder: (context, state) { + if (state is ProfileLoaded) { + return BlocConsumer( + listener: (context, state) { + if (state is FamilyLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is FamilyLoaded || state is FamilyErrorState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + }, + builder: (context, state) { + if (state is FamilyLoaded) { + father = state.families.firstWhere( + (element) => element.relationship!.id == 1); + mother = state.families.firstWhere( + (element) => element.relationship!.id == 2); + spouse = state.families.firstWhere( + (element) => element.relationship!.id == 3); - //Mother----------------------------------------------------- - Container( - decoration: box1(), - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), - width: screenWidth, - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text(motherText), - const SizedBox(height: 5,), - Text( - " ${mother!.relatedPerson!.firstName} ${mother!.relatedPerson!.middleName} ${mother!.relatedPerson!.lastName} ${mother!.relatedPerson!.nameExtension ?? ''}",style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500),), - Text(" $fullname",style: Theme.of(context).textTheme.bodySmall), - Row( - children: [ - Checkbox(value: false, onChanged: (value) {}), - const Text(incaseOfEmergency) - ], - ) - ]), - ), - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - )) - ], - ), - ), - const SizedBox( - height: 5, - ), - //Spouse --------------------------------------------------------- - spouse != null - ? Container( - decoration: box1(), - padding: - const EdgeInsets.symmetric(horizontal: 12, vertical: 8), - width: screenWidth, - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text(spouseText), - const SizedBox(height: 5,), - Text( - " ${spouse!.relatedPerson!.firstName} ${spouse!.relatedPerson!.middleName} ${spouse!.relatedPerson!.lastName} ${spouse!.relatedPerson!.nameExtension ?? ''}",style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500)), - Text(" $fullname",style: Theme.of(context).textTheme.bodySmall), - Row( + // get all children + var childs = state.families + .where((element) => element.relationship!.id == 4); + if (childs.isNotEmpty) { + for (var element in childs) { + children.add(element); + } + } + + //get all related persons + var relateds = state.families + .where((element) => element.relationship!.id! > 4); + if (relateds.isNotEmpty) { + for (var element in relateds) { + otherRelated.add(element); + } + } + return ListView(children: [ + //Father---------------------------------------------- + Container( + decoration: box1(), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + width: screenWidth, + child: Row( children: [ - Checkbox(value: false, onChanged: (value) {}), - const Text(incaseOfEmergency) + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + const Text(fatherText), + const SizedBox( + height: 5, + ), + Text( + " ${father!.relatedPerson!.firstName} ${father!.relatedPerson!.middleName} ${father!.relatedPerson!.lastName} ${father!.relatedPerson!.nameExtension ?? ''},", + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: FontWeight.w500), + ), + Text( + " ", + style: Theme.of(context) + .textTheme + .bodySmall, + ), + Row( + children: [ + Checkbox( + value: false, + onChanged: (value) { + setState(() { + value = !value!; + }); + }), + const Text(incaseOfEmergency) + ], + ) + ]), + ), + IconButton( + onPressed: () {}, + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + )) ], - ) - ]), - ), - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - )) - ], - ), - ) - : const SizedBox(), - const SizedBox( - height: 5, - ), + ), + ), + const SizedBox( + height: 5, + ), -// Childrens ---------------------------------- - children.isNotEmpty - ? Container( - decoration: box1(), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: children - .map( - (child){ - int index = children.indexOf(child); - return Container( - - padding: const EdgeInsets.symmetric( - horizontal: 12, vertical: 8), - width: screenWidth, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - index == 0? const Text(childrenText):const SizedBox(), - const SizedBox( - height: 5, - ), - Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - " ${child.relatedPerson!.firstName} ${child.relatedPerson!.middleName} ${child.relatedPerson!.lastName} ${child.relatedPerson!.nameExtension ?? ''}",style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500)), - Text(" $fullname",style: Theme.of(context).textTheme.bodySmall), - Row( - children: [ - Checkbox( - value: false, - onChanged: (value) {}), - const Text(incaseOfEmergency) - ], - ) - ]), - ), - IconButton( - onPressed: () {}, - icon: const Icon(Icons.more_vert,color: Colors.grey,)) - ], - ), - ], + //Mother----------------------------------------------------- + Container( + decoration: box1(), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + width: screenWidth, + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + const Text(motherText), + const SizedBox( + height: 5, + ), + Text( + " ${mother!.relatedPerson!.firstName} ${mother!.relatedPerson!.middleName} ${mother!.relatedPerson!.lastName} ${mother!.relatedPerson!.nameExtension ?? ''}", + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: FontWeight.w500), + ), + Text(" ", + style: Theme.of(context) + .textTheme + .bodySmall), + Row( + children: [ + Checkbox( + value: false, + onChanged: (value) {}), + const Text(incaseOfEmergency) + ], + ) + ]), + ), + IconButton( + onPressed: () {}, + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + )) + ], + ), ), - ); - } - ) - .toList()), - ) - : const SizedBox(), - const SizedBox( - height: 5, + const SizedBox( + height: 5, + ), + //Spouse --------------------------------------------------------- + spouse != null + ? Container( + decoration: box1(), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + width: screenWidth, + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + const Text(spouseText), + const SizedBox( + height: 5, + ), + Text( + " ${spouse!.relatedPerson!.firstName} ${spouse!.relatedPerson!.middleName} ${spouse!.relatedPerson!.lastName} ${spouse!.relatedPerson!.nameExtension ?? ''}", + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight.w500)), + Text(" ", + style: Theme.of(context) + .textTheme + .bodySmall), + Row( + children: [ + Checkbox( + value: false, + onChanged: (value) {}), + const Text(incaseOfEmergency) + ], + ) + ]), + ), + IconButton( + onPressed: () {}, + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + )) + ], + ), + ) + : const SizedBox(), + const SizedBox( + height: 5, + ), + + // Childrens ---------------------------------- + children.isNotEmpty + ? Container( + decoration: box1(), + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: children.map((child) { + int index = children.indexOf(child); + return Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + width: screenWidth, + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + index == 0 + ? const Text(childrenText) + : const SizedBox(), + const SizedBox( + height: 5, + ), + Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + Text( + " ${child.relatedPerson!.firstName} ${child.relatedPerson!.middleName} ${child.relatedPerson!.lastName} ${child.relatedPerson!.nameExtension ?? ''}", + style: Theme.of( + context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight + .w500)), + Text(" ", + style: Theme.of( + context) + .textTheme + .bodySmall), + Row( + children: [ + Checkbox( + value: false, + onChanged: + (value) {}), + const Text( + incaseOfEmergency) + ], + ) + ]), + ), + IconButton( + onPressed: () {}, + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + )) + ], + ), + ], + ), + ); + }).toList()), + ) + : const SizedBox(), + const SizedBox( + height: 5, + ), + //Other related person + otherRelated.isNotEmpty + ? Container( + decoration: box1(), + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: otherRelated.map((relative) { + int index2 = + otherRelated.indexOf(relative); + return Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + width: screenWidth, + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + index2 == 0 + ? const Text(otherRelatedText) + : const SizedBox(), + const SizedBox( + height: 5, + ), + Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + Text( + " ${relative.relatedPerson!.firstName} ${relative.relatedPerson!.middleName} ${relative.relatedPerson!.lastName} ${relative.relatedPerson!.nameExtension ?? ''}", + style: Theme.of( + context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight + .w500)), + Text(" ", + style: Theme.of( + context) + .textTheme + .bodySmall!), + Row( + children: [ + Checkbox( + value: false, + onChanged: + (value) {}), + const Text( + incaseOfEmergency) + ], + ) + ]), + ), + IconButton( + onPressed: () {}, + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + )) + ], + ), + ], + ), + ); + }).toList()), + ) + : const SizedBox(), + ]); + } + return Container(); + }, + ); + } + return Container(); + }, + ); + }, ), -//Other related person - otherRelated.isNotEmpty - ? Container( - decoration: box1(), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: otherRelated - .map( - (relative){ - int index2 = otherRelated.indexOf(relative); - return Container( - - padding: const EdgeInsets.symmetric( - horizontal: 12, vertical: 8), - width: screenWidth, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - index2 == 0? const Text(otherRelatedText):const SizedBox(), - const SizedBox( - height: 5, - ), - Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - " ${relative.relatedPerson!.firstName} ${relative.relatedPerson!.middleName} ${relative.relatedPerson!.lastName} ${relative.relatedPerson!.nameExtension ?? ''}",style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500)), - Text(" $fullname",style: Theme.of(context).textTheme.bodySmall!), - Row( - children: [ - Checkbox( - value: false, - onChanged: (value) {}), - const Text(incaseOfEmergency) - ], - ) - ]), - ), - IconButton( - onPressed: () {}, - icon: const Icon(Icons.more_vert,color: Colors.grey,)) - ], - ), - ], - ), - ); - } - ) - .toList()), - ) - : const SizedBox(), - ]), + ), ); } } diff --git a/lib/screens/profile/profile.dart b/lib/screens/profile/profile.dart index 88daa42..c27bb1d 100644 --- a/lib/screens/profile/profile.dart +++ b/lib/screens/profile/profile.dart @@ -10,6 +10,7 @@ import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:fluttericon/modern_pictograms_icons.dart'; import 'package:unit2/bloc/education/education_bloc.dart'; import 'package:unit2/bloc/eligibility/eligibility_bloc.dart'; +import 'package:unit2/bloc/family/family_bloc.dart'; import 'package:unit2/bloc/hobbies/hoobies_bloc.dart'; import 'package:unit2/bloc/learningDevelopment/learning_development_bloc.dart'; import 'package:unit2/bloc/non_academic_recognition.dart/non_academic_recognition_bloc.dart'; @@ -156,7 +157,17 @@ class _ProfileInfoState extends State { MainMenu( icon: Elusive.group, title: "Family", - onTap: () {}, + onTap: () { + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return BlocProvider( + create: (context) => FamilyBloc() + ..add(GetFamilies( + profileId: profileId!, token: token!)), + child: const FamilyBackgroundScreen(), + ); + })); + }, ), const Divider(), MainMenu( diff --git a/lib/sevices/profile/family_services.dart b/lib/sevices/profile/family_services.dart new file mode 100644 index 0000000..f48bc91 --- /dev/null +++ b/lib/sevices/profile/family_services.dart @@ -0,0 +1,38 @@ + + + +import 'dart:convert'; + +import 'package:unit2/utils/request.dart'; + +import '../../model/profile/family_backround.dart'; +import '../../utils/urls.dart'; +import 'package:http/http.dart' as http; +class FamilyService{ + static final FamilyService _instance = FamilyService(); + static FamilyService get instance => _instance; + Future< List> getFamilies(int profileId, String token)async{ + List families = []; + String authToken = "Token $token"; + String path = "${Url.instance.getFamilies()}$profileId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; +try{ + http.Response response = await Request.instance.getRequest(path:path, param: {},headers: headers); + if(response.statusCode == 200){ + Map data = jsonDecode(response.body); + if(data['data'] != null){ + data['data'].forEach((var family){ + FamilyBackground familyBackground = FamilyBackground.fromJson(family); + families.add(familyBackground); + }); + } + } +}catch(e){ + throw e.toString(); +} +return families; + } +} \ No newline at end of file diff --git a/lib/sevices/profile/profile_service.dart b/lib/sevices/profile/profile_service.dart index b38bf79..d1bfeb4 100644 --- a/lib/sevices/profile/profile_service.dart +++ b/lib/sevices/profile/profile_service.dart @@ -10,7 +10,6 @@ import 'package:unit2/model/profile/educational_background.dart'; import 'package:unit2/model/profile/eligibility.dart'; import 'package:unit2/model/profile/family_backround.dart'; import 'package:unit2/model/profile/learning_development.dart'; -import 'package:unit2/model/profile/other_info.dart'; import 'package:unit2/model/profile/other_information/non_acedimic_recognition.dart'; import 'package:unit2/model/profile/profileInfomation.dart'; import 'package:unit2/model/profile/references.dart'; diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index a1603c5..79444f4 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -6,8 +6,8 @@ class Url { // // return '192.168.10.221:3003'; // return 'agusandelnorte.gov.ph'; // return "192.168.10.219:3000"; - // return "devweb.agusandelnorte.gov.ph"; - return 'devapi.agusandelnorte.gov.ph:3004'; + return "devweb.agusandelnorte.gov.ph"; + // return 'devapi.agusandelnorte.gov.ph:3004'; } String authentication() { @@ -82,6 +82,11 @@ String getNonAcademicRecognition(){ return "/api/jobnet_app/profile/pds/other/non_acad_recognition/"; } +////family paths +String getFamilies(){ + return "/api/jobnet_app/profile/pds/family/"; +} + // location utils path String getCounties(){ return "/api/jobnet_app/countries/"; From 367402b288bb85103af11ecbf76f7ded5076c276 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Tue, 7 Mar 2023 10:31:28 +0800 Subject: [PATCH 49/86] fefactor address and created its own bloc --- .../education/education_bloc.dart | 0 .../education/education_event.dart | 0 .../education/education_state.dart | 0 .../eligibility/eligibility_bloc.dart | 18 +- .../eligibility/eligibility_event.dart | 0 .../eligibility/eligibility_state.dart | 0 .../{ => profile}/family/family_bloc.dart | 2 +- .../{ => profile}/family/family_event.dart | 0 .../{ => profile}/family/family_state.dart | 0 .../learning_development_bloc.dart | 2 +- .../learning_development_event.dart | 0 .../learning_development_state.dart | 0 .../hobbies/hoobies_bloc.dart | 2 +- .../hobbies/hoobies_event.dart | 0 .../hobbies/hoobies_state.dart | 0 .../non_academic_recognition_bloc.dart | 3 +- .../non_academic_recognition_event.dart | 0 .../non_academic_recognition_state.dart | 0 .../organization_membership_bloc.dart | 2 +- .../organization_membership_event.dart | 0 .../organization_membership_state.dart | 0 .../address/address_bloc.dart | 22 ++ .../address/address_event.dart | 15 ++ .../address/address_state.dart | 27 +++ .../references/references_bloc.dart | 2 +- .../references/references_event.dart | 0 .../references/references_state.dart | 0 .../voluntary_works/voluntary_work_bloc.dart | 3 +- .../voluntary_works/voluntary_work_event.dart | 0 .../voluntary_works/voluntary_work_state.dart | 0 .../workHistory/workHistory_bloc.dart | 0 .../workHistory/workHistory_event.dart | 0 .../workHistory/workHistory_state.dart | 0 .../basic_information/address_screen.dart | 195 ++++++++++++++---- .../identification_information_screen.dart | 32 ++- .../profile/components/education_screen.dart | 3 +- .../components/eligibility/add_modal.dart | 2 +- .../components/eligibility/edit_modal.dart | 2 +- .../components/eligibility_screen.dart | 2 +- .../components/family_background_screen.dart | 3 +- .../learning_and_development_screen.dart | 3 +- .../non_academic_recognition_screen.dart | 3 +- .../org_membership_screen.dart | 3 +- .../skills_and_hobbies_screen.dart | 3 +- .../profile/components/references_screen.dart | 3 +- .../components/voluntary_works_screen.dart | 3 +- .../components/work_history_screen.dart | 2 +- lib/screens/profile/profile.dart | 38 ++-- .../homepage.dart/components/dashboard.dart | 59 +++--- .../unit2/homepage.dart/module-screen.dart | 9 +- lib/sevices/profile/profile_service.dart | 10 - 51 files changed, 324 insertions(+), 149 deletions(-) rename lib/bloc/{ => profile}/education/education_bloc.dart (100%) rename lib/bloc/{ => profile}/education/education_event.dart (100%) rename lib/bloc/{ => profile}/education/education_state.dart (100%) rename lib/bloc/{ => profile}/eligibility/eligibility_bloc.dart (95%) rename lib/bloc/{ => profile}/eligibility/eligibility_event.dart (100%) rename lib/bloc/{ => profile}/eligibility/eligibility_state.dart (100%) rename lib/bloc/{ => profile}/family/family_bloc.dart (92%) rename lib/bloc/{ => profile}/family/family_event.dart (100%) rename lib/bloc/{ => profile}/family/family_state.dart (100%) rename lib/bloc/{ => profile}/learningDevelopment/learning_development_bloc.dart (94%) rename lib/bloc/{ => profile}/learningDevelopment/learning_development_event.dart (100%) rename lib/bloc/{ => profile}/learningDevelopment/learning_development_state.dart (100%) rename lib/bloc/{ => profile/other_information}/hobbies/hoobies_bloc.dart (90%) rename lib/bloc/{ => profile/other_information}/hobbies/hoobies_event.dart (100%) rename lib/bloc/{ => profile/other_information}/hobbies/hoobies_state.dart (100%) rename lib/bloc/{ => profile/other_information}/non_academic_recognition.dart/non_academic_recognition_bloc.dart (92%) rename lib/bloc/{ => profile/other_information}/non_academic_recognition.dart/non_academic_recognition_event.dart (100%) rename lib/bloc/{ => profile/other_information}/non_academic_recognition.dart/non_academic_recognition_state.dart (100%) rename lib/bloc/{ => profile/other_information}/org_membership/organization_membership_bloc.dart (91%) rename lib/bloc/{ => profile/other_information}/org_membership/organization_membership_event.dart (100%) rename lib/bloc/{ => profile/other_information}/org_membership/organization_membership_state.dart (100%) create mode 100644 lib/bloc/profile/primary_information/address/address_bloc.dart create mode 100644 lib/bloc/profile/primary_information/address/address_event.dart create mode 100644 lib/bloc/profile/primary_information/address/address_state.dart rename lib/bloc/{ => profile}/references/references_bloc.dart (93%) rename lib/bloc/{ => profile}/references/references_event.dart (100%) rename lib/bloc/{ => profile}/references/references_state.dart (100%) rename lib/bloc/{ => profile}/voluntary_works/voluntary_work_bloc.dart (87%) rename lib/bloc/{ => profile}/voluntary_works/voluntary_work_event.dart (100%) rename lib/bloc/{ => profile}/voluntary_works/voluntary_work_state.dart (100%) rename lib/bloc/{ => profile}/workHistory/workHistory_bloc.dart (100%) rename lib/bloc/{ => profile}/workHistory/workHistory_event.dart (100%) rename lib/bloc/{ => profile}/workHistory/workHistory_state.dart (100%) diff --git a/lib/bloc/education/education_bloc.dart b/lib/bloc/profile/education/education_bloc.dart similarity index 100% rename from lib/bloc/education/education_bloc.dart rename to lib/bloc/profile/education/education_bloc.dart diff --git a/lib/bloc/education/education_event.dart b/lib/bloc/profile/education/education_event.dart similarity index 100% rename from lib/bloc/education/education_event.dart rename to lib/bloc/profile/education/education_event.dart diff --git a/lib/bloc/education/education_state.dart b/lib/bloc/profile/education/education_state.dart similarity index 100% rename from lib/bloc/education/education_state.dart rename to lib/bloc/profile/education/education_state.dart diff --git a/lib/bloc/eligibility/eligibility_bloc.dart b/lib/bloc/profile/eligibility/eligibility_bloc.dart similarity index 95% rename from lib/bloc/eligibility/eligibility_bloc.dart rename to lib/bloc/profile/eligibility/eligibility_bloc.dart index 98de16c..13f14bc 100644 --- a/lib/bloc/eligibility/eligibility_bloc.dart +++ b/lib/bloc/profile/eligibility/eligibility_bloc.dart @@ -1,14 +1,14 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; -import '../../model/location/city.dart'; -import '../../model/location/country.dart'; -import '../../model/location/provinces.dart'; -import '../../model/location/region.dart'; -import '../../model/profile/eligibility.dart'; -import '../../model/utils/eligibility.dart'; -import '../../sevices/profile/eligibility_services.dart'; -import '../../utils/location_utilities.dart'; -import '../../utils/profile_utilities.dart'; +import '../../../model/location/city.dart'; +import '../../../model/location/country.dart'; +import '../../../model/location/provinces.dart'; +import '../../../model/location/region.dart'; +import '../../../model/profile/eligibility.dart'; +import '../../../model/utils/eligibility.dart'; +import '../../../sevices/profile/eligibility_services.dart'; +import '../../../utils/location_utilities.dart'; +import '../../../utils/profile_utilities.dart'; part 'eligibility_event.dart'; part 'eligibility_state.dart'; diff --git a/lib/bloc/eligibility/eligibility_event.dart b/lib/bloc/profile/eligibility/eligibility_event.dart similarity index 100% rename from lib/bloc/eligibility/eligibility_event.dart rename to lib/bloc/profile/eligibility/eligibility_event.dart diff --git a/lib/bloc/eligibility/eligibility_state.dart b/lib/bloc/profile/eligibility/eligibility_state.dart similarity index 100% rename from lib/bloc/eligibility/eligibility_state.dart rename to lib/bloc/profile/eligibility/eligibility_state.dart diff --git a/lib/bloc/family/family_bloc.dart b/lib/bloc/profile/family/family_bloc.dart similarity index 92% rename from lib/bloc/family/family_bloc.dart rename to lib/bloc/profile/family/family_bloc.dart index 1d8289f..30db4da 100644 --- a/lib/bloc/family/family_bloc.dart +++ b/lib/bloc/profile/family/family_bloc.dart @@ -2,7 +2,7 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:unit2/sevices/profile/family_services.dart'; -import '../../model/profile/family_backround.dart'; +import '../../../model/profile/family_backround.dart'; part 'family_event.dart'; part 'family_state.dart'; diff --git a/lib/bloc/family/family_event.dart b/lib/bloc/profile/family/family_event.dart similarity index 100% rename from lib/bloc/family/family_event.dart rename to lib/bloc/profile/family/family_event.dart diff --git a/lib/bloc/family/family_state.dart b/lib/bloc/profile/family/family_state.dart similarity index 100% rename from lib/bloc/family/family_state.dart rename to lib/bloc/profile/family/family_state.dart diff --git a/lib/bloc/learningDevelopment/learning_development_bloc.dart b/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart similarity index 94% rename from lib/bloc/learningDevelopment/learning_development_bloc.dart rename to lib/bloc/profile/learningDevelopment/learning_development_bloc.dart index 4457b4d..de39dac 100644 --- a/lib/bloc/learningDevelopment/learning_development_bloc.dart +++ b/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart @@ -2,7 +2,7 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:unit2/sevices/profile/learningDevelopment_service.dart'; -import '../../model/profile/learning_development.dart'; +import '../../../model/profile/learning_development.dart'; part 'learning_development_event.dart'; part 'learning_development_state.dart'; diff --git a/lib/bloc/learningDevelopment/learning_development_event.dart b/lib/bloc/profile/learningDevelopment/learning_development_event.dart similarity index 100% rename from lib/bloc/learningDevelopment/learning_development_event.dart rename to lib/bloc/profile/learningDevelopment/learning_development_event.dart diff --git a/lib/bloc/learningDevelopment/learning_development_state.dart b/lib/bloc/profile/learningDevelopment/learning_development_state.dart similarity index 100% rename from lib/bloc/learningDevelopment/learning_development_state.dart rename to lib/bloc/profile/learningDevelopment/learning_development_state.dart diff --git a/lib/bloc/hobbies/hoobies_bloc.dart b/lib/bloc/profile/other_information/hobbies/hoobies_bloc.dart similarity index 90% rename from lib/bloc/hobbies/hoobies_bloc.dart rename to lib/bloc/profile/other_information/hobbies/hoobies_bloc.dart index 6453222..3ad0c7e 100644 --- a/lib/bloc/hobbies/hoobies_bloc.dart +++ b/lib/bloc/profile/other_information/hobbies/hoobies_bloc.dart @@ -2,7 +2,7 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:unit2/sevices/skillshobbies_services.dart'; -import '../../model/profile/other_information/skills_and_hobbies.dart'; +import '../../../../model/profile/other_information/skills_and_hobbies.dart'; part 'hoobies_event.dart'; part 'hoobies_state.dart'; diff --git a/lib/bloc/hobbies/hoobies_event.dart b/lib/bloc/profile/other_information/hobbies/hoobies_event.dart similarity index 100% rename from lib/bloc/hobbies/hoobies_event.dart rename to lib/bloc/profile/other_information/hobbies/hoobies_event.dart diff --git a/lib/bloc/hobbies/hoobies_state.dart b/lib/bloc/profile/other_information/hobbies/hoobies_state.dart similarity index 100% rename from lib/bloc/hobbies/hoobies_state.dart rename to lib/bloc/profile/other_information/hobbies/hoobies_state.dart diff --git a/lib/bloc/non_academic_recognition.dart/non_academic_recognition_bloc.dart b/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_bloc.dart similarity index 92% rename from lib/bloc/non_academic_recognition.dart/non_academic_recognition_bloc.dart rename to lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_bloc.dart index 8a7cdf7..690015a 100644 --- a/lib/bloc/non_academic_recognition.dart/non_academic_recognition_bloc.dart +++ b/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_bloc.dart @@ -2,7 +2,8 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:unit2/sevices/profile/non_academic_services.dart'; -import '../../model/profile/other_information/non_acedimic_recognition.dart'; +import '../../../../model/profile/other_information/non_acedimic_recognition.dart'; + part 'non_academic_recognition_event.dart'; part 'non_academic_recognition_state.dart'; diff --git a/lib/bloc/non_academic_recognition.dart/non_academic_recognition_event.dart b/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_event.dart similarity index 100% rename from lib/bloc/non_academic_recognition.dart/non_academic_recognition_event.dart rename to lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_event.dart diff --git a/lib/bloc/non_academic_recognition.dart/non_academic_recognition_state.dart b/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_state.dart similarity index 100% rename from lib/bloc/non_academic_recognition.dart/non_academic_recognition_state.dart rename to lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_state.dart diff --git a/lib/bloc/org_membership/organization_membership_bloc.dart b/lib/bloc/profile/other_information/org_membership/organization_membership_bloc.dart similarity index 91% rename from lib/bloc/org_membership/organization_membership_bloc.dart rename to lib/bloc/profile/other_information/org_membership/organization_membership_bloc.dart index 83e0260..cb27502 100644 --- a/lib/bloc/org_membership/organization_membership_bloc.dart +++ b/lib/bloc/profile/other_information/org_membership/organization_membership_bloc.dart @@ -2,7 +2,7 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:unit2/sevices/profile/orgmembership_services.dart'; -import '../../model/profile/other_information/organization_memberships.dart'; +import '../../../../model/profile/other_information/organization_memberships.dart'; part 'organization_membership_event.dart'; part 'organization_membership_state.dart'; diff --git a/lib/bloc/org_membership/organization_membership_event.dart b/lib/bloc/profile/other_information/org_membership/organization_membership_event.dart similarity index 100% rename from lib/bloc/org_membership/organization_membership_event.dart rename to lib/bloc/profile/other_information/org_membership/organization_membership_event.dart diff --git a/lib/bloc/org_membership/organization_membership_state.dart b/lib/bloc/profile/other_information/org_membership/organization_membership_state.dart similarity index 100% rename from lib/bloc/org_membership/organization_membership_state.dart rename to lib/bloc/profile/other_information/org_membership/organization_membership_state.dart diff --git a/lib/bloc/profile/primary_information/address/address_bloc.dart b/lib/bloc/profile/primary_information/address/address_bloc.dart new file mode 100644 index 0000000..30195bc --- /dev/null +++ b/lib/bloc/profile/primary_information/address/address_bloc.dart @@ -0,0 +1,22 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; + +import '../../../../model/profile/basic_information/adress.dart'; + +part 'address_event.dart'; +part 'address_state.dart'; + +class AddressBloc extends Bloc { + AddressBloc() : super(AddressInitial()) { + List addresses = []; + on((event, emit) { + emit(AddressLoadingState()); + try{ + addresses = event.addresses; + emit(AddressLoadedState(addresses: addresses)); + }catch(e){ + emit(AddressErrorState(message: e.toString())); + } + }); + } +} diff --git a/lib/bloc/profile/primary_information/address/address_event.dart b/lib/bloc/profile/primary_information/address/address_event.dart new file mode 100644 index 0000000..1f4ba71 --- /dev/null +++ b/lib/bloc/profile/primary_information/address/address_event.dart @@ -0,0 +1,15 @@ +part of 'address_bloc.dart'; + +abstract class AddressEvent extends Equatable { + const AddressEvent(); + + @override + List get props => []; +} + +class GetAddress extends AddressEvent{ + final List addresses; + const GetAddress({required this.addresses}); + @override + List get props => [addresses]; +} diff --git a/lib/bloc/profile/primary_information/address/address_state.dart b/lib/bloc/profile/primary_information/address/address_state.dart new file mode 100644 index 0000000..fcdeea3 --- /dev/null +++ b/lib/bloc/profile/primary_information/address/address_state.dart @@ -0,0 +1,27 @@ +part of 'address_bloc.dart'; + +abstract class AddressState extends Equatable { + const AddressState(); + + @override + List get props => []; +} + +class AddressInitial extends AddressState {} + +class AddressLoadedState extends AddressState{ + final List addresses; + const AddressLoadedState({required this.addresses}); + @override + List get props => [addresses]; +} + +class AddressErrorState extends AddressState{ + final String message; + const AddressErrorState({required this.message}); + @override + List get props => [message]; +} +class AddressLoadingState extends AddressState{ + +} diff --git a/lib/bloc/references/references_bloc.dart b/lib/bloc/profile/references/references_bloc.dart similarity index 93% rename from lib/bloc/references/references_bloc.dart rename to lib/bloc/profile/references/references_bloc.dart index 569f774..9041de3 100644 --- a/lib/bloc/references/references_bloc.dart +++ b/lib/bloc/profile/references/references_bloc.dart @@ -1,7 +1,7 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:unit2/sevices/profile/references_services.dart'; -import '../../model/profile/references.dart'; +import '../../../model/profile/references.dart'; part 'references_event.dart'; part 'references_state.dart'; diff --git a/lib/bloc/references/references_event.dart b/lib/bloc/profile/references/references_event.dart similarity index 100% rename from lib/bloc/references/references_event.dart rename to lib/bloc/profile/references/references_event.dart diff --git a/lib/bloc/references/references_state.dart b/lib/bloc/profile/references/references_state.dart similarity index 100% rename from lib/bloc/references/references_state.dart rename to lib/bloc/profile/references/references_state.dart diff --git a/lib/bloc/voluntary_works/voluntary_work_bloc.dart b/lib/bloc/profile/voluntary_works/voluntary_work_bloc.dart similarity index 87% rename from lib/bloc/voluntary_works/voluntary_work_bloc.dart rename to lib/bloc/profile/voluntary_works/voluntary_work_bloc.dart index f12ce40..d2bbd22 100644 --- a/lib/bloc/voluntary_works/voluntary_work_bloc.dart +++ b/lib/bloc/profile/voluntary_works/voluntary_work_bloc.dart @@ -1,9 +1,8 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:unit2/sevices/profile/volunatary_services.dart'; -import 'package:unit2/sevices/profile/work_history_services.dart'; -import '../../model/profile/voluntary_works.dart'; +import '../../../model/profile/voluntary_works.dart'; part 'voluntary_work_event.dart'; part 'voluntary_work_state.dart'; diff --git a/lib/bloc/voluntary_works/voluntary_work_event.dart b/lib/bloc/profile/voluntary_works/voluntary_work_event.dart similarity index 100% rename from lib/bloc/voluntary_works/voluntary_work_event.dart rename to lib/bloc/profile/voluntary_works/voluntary_work_event.dart diff --git a/lib/bloc/voluntary_works/voluntary_work_state.dart b/lib/bloc/profile/voluntary_works/voluntary_work_state.dart similarity index 100% rename from lib/bloc/voluntary_works/voluntary_work_state.dart rename to lib/bloc/profile/voluntary_works/voluntary_work_state.dart diff --git a/lib/bloc/workHistory/workHistory_bloc.dart b/lib/bloc/profile/workHistory/workHistory_bloc.dart similarity index 100% rename from lib/bloc/workHistory/workHistory_bloc.dart rename to lib/bloc/profile/workHistory/workHistory_bloc.dart diff --git a/lib/bloc/workHistory/workHistory_event.dart b/lib/bloc/profile/workHistory/workHistory_event.dart similarity index 100% rename from lib/bloc/workHistory/workHistory_event.dart rename to lib/bloc/profile/workHistory/workHistory_event.dart diff --git a/lib/bloc/workHistory/workHistory_state.dart b/lib/bloc/profile/workHistory/workHistory_state.dart similarity index 100% rename from lib/bloc/workHistory/workHistory_state.dart rename to lib/bloc/profile/workHistory/workHistory_state.dart diff --git a/lib/screens/profile/components/basic_information/address_screen.dart b/lib/screens/profile/components/basic_information/address_screen.dart index 3032355..b394883 100644 --- a/lib/screens/profile/components/basic_information/address_screen.dart +++ b/lib/screens/profile/components/basic_information/address_screen.dart @@ -1,4 +1,9 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:unit2/bloc/profile/primary_information/address/address_bloc.dart'; +import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/basic_information/adress.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; @@ -6,52 +11,156 @@ import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; +import 'package:unit2/widgets/error_state.dart'; class AddressScreen extends StatelessWidget { - final List addresses; - const AddressScreen({super.key, required this.addresses}); + + const AddressScreen({super.key}); @override Widget build(BuildContext context) { - - return Scaffold( - appBar: AppBar(title: const Text(adressScreenTitle),centerTitle: true, backgroundColor: primary, actions: [AddLeading(onPressed: (){})],), - body: addresses.isNotEmpty ? ListView.builder( - padding: const EdgeInsets.symmetric(vertical: 8,horizontal: 10), - itemCount: addresses.length, - itemBuilder: ( - BuildContext context, int index){ - String? subdivision = addresses[index].details??''; - String category = addresses[index].address!.category!.name!; - String? barangay = addresses[index].address!.barangay != null?'${addresses[index].address!.barangay!.description!.toUpperCase()},':''; - String cityMunicipality = addresses[index].address!.cityMunicipality!.description!; - String province = addresses[index].address!.cityMunicipality!.province!.description!; - String region = addresses[index].address!.cityMunicipality!.province!.region!.description!; - return Column(children: [ - - Column( - children: [ - Container( - width: screenWidth, - decoration: box1(), - padding: const EdgeInsets.fromLTRB(8,16,0,16), - child: Row(children: [ - Expanded(child: Column(children: [ - Row(children: [Text(subdivision,style: Theme.of(context).textTheme.titleMedium,), const SizedBox(width: 5,), - - Text(category,style: Theme.of(context).textTheme.bodySmall,)],), - const Divider(), - const SizedBox(height: 5,), - Text("$barangay $cityMunicipality, $province, $region",style: Theme.of(context).textTheme.labelLarge,), - ],)), - IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert,color: Colors.grey,)) - ]), - ), - ], - ), - const SizedBox(height: 5,), - ],); - }):const EmptyData(message: "You don't have address added. Please click + to add."), - ); + return Scaffold( + appBar: AppBar( + title: const Text(adressScreenTitle), + centerTitle: true, + backgroundColor: primary, + actions: [AddLeading(onPressed: () {})], + ), + body: ProgressHUD( + child: BlocBuilder( + builder: (context, state) { + if (state is UserLoggedIn) { + return BlocBuilder( + builder: (context, state) { + if (state is ProfileLoaded) { + return BlocConsumer( + listener: (context, state) { + if (state is AddressLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is AddressLoadedState || + state is AddressErrorState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + }, + builder: (context, state) { + if (state is AddressLoadedState) { + if (state.addresses.isNotEmpty) { + return ListView.builder( + padding: const EdgeInsets.symmetric( + vertical: 8, horizontal: 10), + itemCount: state.addresses.length, + itemBuilder: + (BuildContext context, int index) { + String? subdivision = + state.addresses[index].details ?? ''; + String category = state.addresses[index] + .address! + .category! + .name!; + String? barangay = state.addresses[index] + .address! + .barangay != + null + ? '${state.addresses[index].address!.barangay!.description!.toUpperCase()},' + : ''; + String cityMunicipality = state.addresses[index] + .address! + .cityMunicipality! + .description!; + String province = state.addresses[index] + .address! + .cityMunicipality! + .province! + .description!; + String region = state.addresses[index] + .address! + .cityMunicipality! + .province! + .region! + .description!; + return Column( + children: [ + Column( + children: [ + Container( + width: screenWidth, + decoration: box1(), + padding: + const EdgeInsets.fromLTRB( + 8, 16, 0, 16), + child: Row(children: [ + Expanded( + child: Column( + children: [ + Row( + children: [ + Text( + subdivision, + style: + Theme.of(context) + .textTheme + .titleMedium, + ), + const SizedBox( + width: 5, + ), + Text( + category, + style: + Theme.of(context) + .textTheme + .bodySmall, + ) + ], + ), + const Divider(), + const SizedBox( + height: 5, + ), + Text( + "$barangay $cityMunicipality, $province, $region", + style: Theme.of(context) + .textTheme + .labelLarge, + ), + ], + )), + IconButton( + onPressed: () {}, + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + )) + ]), + ), + ], + ), + const SizedBox( + height: 5, + ), + ], + ); + }); + } else { + const EmptyData( + message: + "You don't have address added. Please click + to add."); + } + } + return Container(); + }, + ); + } + return Container(); + }, + ); + } + return Container(); + }, + ), + )); } -} \ No newline at end of file +} diff --git a/lib/screens/profile/components/basic_information/identification_information_screen.dart b/lib/screens/profile/components/basic_information/identification_information_screen.dart index 8f47db9..2eba37e 100644 --- a/lib/screens/profile/components/basic_information/identification_information_screen.dart +++ b/lib/screens/profile/components/basic_information/identification_information_screen.dart @@ -44,28 +44,24 @@ class IdentificationsScreen extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(agency,style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500)), + Expanded(child: Text(agency,style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w400))), const Divider(), const SizedBox(height: 5,), - Row( - children: [ - Expanded( - child: Text( - "$idNumberText : $idNumber",style: Theme.of(context).textTheme.titleSmall, - ), + Expanded( + child: Text( + "$idNumberText : $idNumber",style: Theme.of(context).textTheme.titleSmall, ), - - Badge( - - backgroundColor: success2, - label: Text( - government == - true - ? privateText.toUpperCase() - :governmentText.toUpperCase(), - style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.white),)), - ], ), + + Badge( + + backgroundColor: success2, + label: Text( + government == + true + ? privateText.toUpperCase() + :governmentText.toUpperCase(), + style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.white),)), const SizedBox(height: 5,), Text(issuedAt), ]), diff --git a/lib/screens/profile/components/education_screen.dart b/lib/screens/profile/components/education_screen.dart index 0e9860f..4771db8 100644 --- a/lib/screens/profile/components/education_screen.dart +++ b/lib/screens/profile/components/education_screen.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; -import 'package:unit2/bloc/education/education_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; @@ -14,6 +13,8 @@ import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; +import '../../../bloc/profile/education/education_bloc.dart'; + class EducationScreen extends StatelessWidget { const EducationScreen({super.key}); diff --git a/lib/screens/profile/components/eligibility/add_modal.dart b/lib/screens/profile/components/eligibility/add_modal.dart index 7e55df6..4e0fe91 100644 --- a/lib/screens/profile/components/eligibility/add_modal.dart +++ b/lib/screens/profile/components/eligibility/add_modal.dart @@ -6,11 +6,11 @@ import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:intl/intl.dart'; import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; -import 'package:unit2/bloc/eligibility/eligibility_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/eligibility.dart'; +import '../../../../bloc/profile/eligibility/eligibility_bloc.dart'; import '../../../../model/location/city.dart'; import '../../../../model/location/country.dart'; import '../../../../model/location/provinces.dart'; diff --git a/lib/screens/profile/components/eligibility/edit_modal.dart b/lib/screens/profile/components/eligibility/edit_modal.dart index 0241a5f..e221f4d 100644 --- a/lib/screens/profile/components/eligibility/edit_modal.dart +++ b/lib/screens/profile/components/eligibility/edit_modal.dart @@ -4,13 +4,13 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:intl/intl.dart'; import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; -import 'package:unit2/bloc/eligibility/eligibility_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/location/city.dart'; import 'package:unit2/model/profile/eligibility.dart'; import 'package:unit2/model/utils/eligibility.dart'; import 'package:unit2/utils/location_utilities.dart'; +import '../../../../bloc/profile/eligibility/eligibility_bloc.dart'; import '../../../../model/location/country.dart'; import '../../../../model/location/region.dart'; import '../../../../model/location/provinces.dart'; diff --git a/lib/screens/profile/components/eligibility_screen.dart b/lib/screens/profile/components/eligibility_screen.dart index 7fc3356..fec9dc2 100644 --- a/lib/screens/profile/components/eligibility_screen.dart +++ b/lib/screens/profile/components/eligibility_screen.dart @@ -4,7 +4,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:fluttericon/font_awesome_icons.dart'; -import 'package:unit2/bloc/eligibility/eligibility_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/eligibility.dart'; @@ -17,6 +16,7 @@ import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/Leadings/close_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; +import '../../../bloc/profile/eligibility/eligibility_bloc.dart'; import '../../../utils/alerts.dart'; class EligibiltyScreen extends StatelessWidget { diff --git a/lib/screens/profile/components/family_background_screen.dart b/lib/screens/profile/components/family_background_screen.dart index 684df5c..f179041 100644 --- a/lib/screens/profile/components/family_background_screen.dart +++ b/lib/screens/profile/components/family_background_screen.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; -import 'package:unit2/bloc/family/family_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/family_backround.dart'; @@ -11,6 +10,8 @@ import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; +import '../../../bloc/profile/family/family_bloc.dart'; + class FamilyBackgroundScreen extends StatefulWidget { const FamilyBackgroundScreen({ super.key, diff --git a/lib/screens/profile/components/learning_and_development_screen.dart b/lib/screens/profile/components/learning_and_development_screen.dart index 7f9f7cd..f2e8aa1 100644 --- a/lib/screens/profile/components/learning_and_development_screen.dart +++ b/lib/screens/profile/components/learning_and_development_screen.dart @@ -5,7 +5,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:intl/intl.dart'; -import 'package:unit2/bloc/learningDevelopment/learning_development_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/learning_development.dart'; @@ -17,6 +16,8 @@ import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; +import '../../../bloc/profile/learningDevelopment/learning_development_bloc.dart'; + class LearningAndDevelopmentScreen extends StatelessWidget { const LearningAndDevelopmentScreen( diff --git a/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart b/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart index 385ae45..e4cb52e 100644 --- a/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart +++ b/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; -import 'package:unit2/bloc/non_academic_recognition.dart/non_academic_recognition_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; @@ -12,6 +11,8 @@ import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; +import '../../../../bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_bloc.dart'; + class NonAcademicRecognitionScreen extends StatelessWidget { const NonAcademicRecognitionScreen({ super.key, diff --git a/lib/screens/profile/components/other_information/org_membership_screen.dart b/lib/screens/profile/components/other_information/org_membership_screen.dart index 480f01e..df8cc9e 100644 --- a/lib/screens/profile/components/other_information/org_membership_screen.dart +++ b/lib/screens/profile/components/other_information/org_membership_screen.dart @@ -10,8 +10,7 @@ import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; - -import '../../../../bloc/org_membership/organization_membership_bloc.dart'; +import '../../../../bloc/profile/other_information/org_membership/organization_membership_bloc.dart'; import '../../../../utils/global.dart'; class OrgMembershipsScreen extends StatelessWidget { diff --git a/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart b/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart index dad7d6d..1ea1de4 100644 --- a/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart +++ b/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart @@ -4,7 +4,6 @@ import 'package:flutter/src/widgets/placeholder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; -import 'package:unit2/bloc/hobbies/hoobies_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/other_information/skills_and_hobbies.dart'; @@ -14,6 +13,8 @@ import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; +import '../../../../bloc/profile/other_information/hobbies/hoobies_bloc.dart'; + class SkillHobbiesScreen extends StatelessWidget { const SkillHobbiesScreen({super.key}); diff --git a/lib/screens/profile/components/references_screen.dart b/lib/screens/profile/components/references_screen.dart index cd9b6ae..1849d9b 100644 --- a/lib/screens/profile/components/references_screen.dart +++ b/lib/screens/profile/components/references_screen.dart @@ -5,7 +5,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; -import 'package:unit2/bloc/references/references_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/references.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; @@ -14,6 +13,8 @@ import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; +import '../../../bloc/profile/references/references_bloc.dart'; + class ReferencesScreen extends StatelessWidget { const ReferencesScreen({super.key}); @override diff --git a/lib/screens/profile/components/voluntary_works_screen.dart b/lib/screens/profile/components/voluntary_works_screen.dart index 18b89f5..a26ff67 100644 --- a/lib/screens/profile/components/voluntary_works_screen.dart +++ b/lib/screens/profile/components/voluntary_works_screen.dart @@ -5,13 +5,14 @@ import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:intl/intl.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; -import 'package:unit2/bloc/voluntary_works/voluntary_work_bloc.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; +import '../../../bloc/profile/voluntary_works/voluntary_work_bloc.dart'; + class VolunataryWorkScreen extends StatelessWidget { const VolunataryWorkScreen({super.key}); diff --git a/lib/screens/profile/components/work_history_screen.dart b/lib/screens/profile/components/work_history_screen.dart index 74d9a0d..87666fa 100644 --- a/lib/screens/profile/components/work_history_screen.dart +++ b/lib/screens/profile/components/work_history_screen.dart @@ -5,7 +5,6 @@ import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:intl/intl.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; -import 'package:unit2/bloc/workHistory/workHistory_bloc.dart'; import 'package:unit2/model/profile/work_history.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; @@ -14,6 +13,7 @@ import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; +import '../../../bloc/profile/workHistory/workHistory_bloc.dart'; import '../../../utils/global.dart'; class WorkHistoryScreen extends StatelessWidget { diff --git a/lib/screens/profile/profile.dart b/lib/screens/profile/profile.dart index c27bb1d..b9f3da9 100644 --- a/lib/screens/profile/profile.dart +++ b/lib/screens/profile/profile.dart @@ -8,17 +8,8 @@ import 'package:fluttericon/elusive_icons.dart'; import 'package:fluttericon/entypo_icons.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:fluttericon/modern_pictograms_icons.dart'; -import 'package:unit2/bloc/education/education_bloc.dart'; -import 'package:unit2/bloc/eligibility/eligibility_bloc.dart'; -import 'package:unit2/bloc/family/family_bloc.dart'; -import 'package:unit2/bloc/hobbies/hoobies_bloc.dart'; -import 'package:unit2/bloc/learningDevelopment/learning_development_bloc.dart'; -import 'package:unit2/bloc/non_academic_recognition.dart/non_academic_recognition_bloc.dart'; +import 'package:unit2/bloc/profile/primary_information/address/address_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; -import 'package:unit2/bloc/references/references_bloc.dart'; -import 'package:unit2/bloc/voluntary_works/voluntary_work_bloc.dart'; -import 'package:unit2/bloc/workHistory/workHistory_bloc.dart'; -import 'package:unit2/model/login_data/employee_info/employee_info.dart'; import 'package:unit2/screens/profile/components/basic_information/address_screen.dart'; import 'package:unit2/screens/profile/components/basic_information/citizenship_screen.dart'; import 'package:unit2/screens/profile/components/basic_information/contact_information_screen.dart'; @@ -36,7 +27,16 @@ import 'package:unit2/screens/profile/components/references_screen.dart'; import 'package:unit2/screens/profile/components/work_history_screen.dart'; import 'package:unit2/screens/profile/components/voluntary_works_screen.dart'; import 'package:unit2/theme-data.dart/colors.dart'; -import '../../bloc/org_membership/organization_membership_bloc.dart'; +import '../../bloc/profile/eligibility/eligibility_bloc.dart'; +import '../../bloc/profile/family/family_bloc.dart'; +import '../../bloc/profile/education/education_bloc.dart'; +import '../../bloc/profile/learningDevelopment/learning_development_bloc.dart'; +import '../../bloc/profile/other_information/hobbies/hoobies_bloc.dart'; +import '../../bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_bloc.dart'; +import '../../bloc/profile/other_information/org_membership/organization_membership_bloc.dart'; +import '../../bloc/profile/references/references_bloc.dart'; +import '../../bloc/profile/voluntary_works/voluntary_work_bloc.dart'; +import '../../bloc/profile/workHistory/workHistory_bloc.dart'; import '../../bloc/user/user_bloc.dart'; import 'components/main_menu.dart'; import 'components/submenu.dart'; @@ -117,12 +117,16 @@ class _ProfileInfoState extends State { })); }), subMenu(Icons.home, "Home Addresses", () { - Navigator.push(context, MaterialPageRoute( - builder: (BuildContext context) { - return AddressScreen( - addresses: state.profileInformation - .basicInfo.addresses); - })); + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return BlocProvider( + create: (context) => AddressBloc() + ..add(GetAddress( + addresses: state.profileInformation.basicInfo.addresses)), + child: const AddressScreen(), + ); + })); + }), subMenu(Icons.contact_mail, "Identifications", () { diff --git a/lib/screens/unit2/homepage.dart/components/dashboard.dart b/lib/screens/unit2/homepage.dart/components/dashboard.dart index ecca003..f46b533 100644 --- a/lib/screens/unit2/homepage.dart/components/dashboard.dart +++ b/lib/screens/unit2/homepage.dart/components/dashboard.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:unit2/screens/unit2/homepage.dart/module-screen.dart'; +import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/global.dart'; class DashBoard extends StatelessWidget { final List roles; @@ -27,9 +29,9 @@ class DashBoard extends StatelessWidget { style: Theme.of(context) .textTheme .labelLarge! - .copyWith(fontSize: 16, fontWeight: FontWeight.w700), + .copyWith(fontSize: 14), ), - const Divider(), + const SizedBox( height: 8, ), @@ -42,33 +44,38 @@ class DashBoard extends StatelessWidget { padding: const EdgeInsets.symmetric( vertical: 5, horizontal: 5), children: roles[index].roles.map((role) { - return GestureDetector( - onTap: () { - }, - child: Column(children: [ - Icon( - role.icon, - size: 24, - color: second, - ), - const SizedBox( - height: 5, - ), - Expanded( - child: Text( - role.role.name!, - textAlign: TextAlign.center, - style: Theme.of(context) - .textTheme - .labelLarge! - .copyWith( - fontSize: 11, - fontWeight: FontWeight.bold), + return Container( + padding: const EdgeInsets.all(8), + decoration:box1(), + child: GestureDetector( + onTap: () { + }, + child: Column(children: [ + Icon( + role.icon, + size: 24, + color: second, ), - ), - ]), + const SizedBox( + height: 5, + ), + Expanded( + child: Text( + role.role.name!, + textAlign: TextAlign.center, + style: Theme.of(context) + .textTheme + .labelLarge! + .copyWith( + fontSize: blockSizeVertical*1.1, + fontWeight: FontWeight.bold), + ), + ), + ]), + ), ); }).toList()), + const SizedBox(height: 8,) ], ), ); diff --git a/lib/screens/unit2/homepage.dart/module-screen.dart b/lib/screens/unit2/homepage.dart/module-screen.dart index df1f7d7..ba232cc 100644 --- a/lib/screens/unit2/homepage.dart/module-screen.dart +++ b/lib/screens/unit2/homepage.dart/module-screen.dart @@ -31,10 +31,9 @@ class _MainScreenState extends State { }, child: BlocBuilder(builder: (context, state) { if (state is UserLoggedIn) { - print(state.userData!.user!.login!.token); - state.userData!.user!.login!.user!.roles!.forEach((role) { + for (var role in state.userData!.user!.login!.user!.roles!) { Role? getRole = role; - role!.modules!.forEach((module) { + for (var module in role!.modules!) { if (module!.name!.toLowerCase() == 'unit2') { IconData iconData = iconGenerator(getRole!.name!); Roles newRole = Roles(role: getRole, icon: iconData); @@ -45,8 +44,8 @@ class _MainScreenState extends State { Roles newRole = Roles(role: getRole, icon: iconData); roles[1].roles.add(newRole); } - }); - }); + } + } return Scaffold( appBar: AppBar( backgroundColor: primary, diff --git a/lib/sevices/profile/profile_service.dart b/lib/sevices/profile/profile_service.dart index d1bfeb4..d085a98 100644 --- a/lib/sevices/profile/profile_service.dart +++ b/lib/sevices/profile/profile_service.dart @@ -31,20 +31,10 @@ class ProfileService { String path = url + id.toString(); ProfileInformation? profileInformation0; PrimaryInformation? primaryInformation; - List workExperiences = []; - List references = []; List addresses = []; List identificationInformation = []; List contactInformation = []; - - List families = []; List citizenships = []; - List learningsDevelopments = []; - List educationalBackgrounds = []; - List voluntaryWorks = []; - List skillsHobbies = []; - List orgMemberships = []; - List nonAcademicRecognitions = []; Map headers = { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': "Token $token" From 5bea76102cb20a59d854f00deab2e76fdb2344cc Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Tue, 7 Mar 2023 15:01:56 +0800 Subject: [PATCH 50/86] contact refactor and created its own bloc --- .../contact/contact_bloc.dart | 22 ++ .../contact/contact_event.dart | 16 ++ .../contact/contact_state.dart | 27 ++ .../identification/identification_bloc.dart | 21 ++ .../identification/identification_event.dart | 15 ++ .../identification/identification_state.dart | 28 +++ .../contact_information_screen.dart | 236 +++++++++++++----- .../identification_information_screen.dart | 199 ++++++++++----- lib/screens/profile/profile.dart | 78 +++--- 9 files changed, 480 insertions(+), 162 deletions(-) create mode 100644 lib/bloc/profile/primary_information/contact/contact_bloc.dart create mode 100644 lib/bloc/profile/primary_information/contact/contact_event.dart create mode 100644 lib/bloc/profile/primary_information/contact/contact_state.dart create mode 100644 lib/bloc/profile/primary_information/identification/identification_bloc.dart create mode 100644 lib/bloc/profile/primary_information/identification/identification_event.dart create mode 100644 lib/bloc/profile/primary_information/identification/identification_state.dart diff --git a/lib/bloc/profile/primary_information/contact/contact_bloc.dart b/lib/bloc/profile/primary_information/contact/contact_bloc.dart new file mode 100644 index 0000000..b01bb81 --- /dev/null +++ b/lib/bloc/profile/primary_information/contact/contact_bloc.dart @@ -0,0 +1,22 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; + +import '../../../../model/profile/basic_information/contact_information.dart'; + +part 'contact_event.dart'; +part 'contact_state.dart'; + +class ContactBloc extends Bloc { + ContactBloc() : super(ContactInitial()) { + List contactInformations = []; + on((event, emit) { + emit(ContactLoadingState()); + try { + contactInformations = event.contactInformations; + emit(ContactLoadedState(contactInformation: contactInformations)); + } catch (e) { + emit(ContactErrorState(message: e.toString())); + } + }); + } +} diff --git a/lib/bloc/profile/primary_information/contact/contact_event.dart b/lib/bloc/profile/primary_information/contact/contact_event.dart new file mode 100644 index 0000000..e2bdc88 --- /dev/null +++ b/lib/bloc/profile/primary_information/contact/contact_event.dart @@ -0,0 +1,16 @@ +part of 'contact_bloc.dart'; + +abstract class ContactEvent extends Equatable { + const ContactEvent(); + + @override + List get props => []; +} + + +class GetContacts extends ContactEvent{ + final List contactInformations; + const GetContacts({required this.contactInformations}); + @override + List get props => []; +} \ No newline at end of file diff --git a/lib/bloc/profile/primary_information/contact/contact_state.dart b/lib/bloc/profile/primary_information/contact/contact_state.dart new file mode 100644 index 0000000..f5a403b --- /dev/null +++ b/lib/bloc/profile/primary_information/contact/contact_state.dart @@ -0,0 +1,27 @@ +part of 'contact_bloc.dart'; + +abstract class ContactState extends Equatable { + const ContactState(); + + @override + List get props => []; +} + +class ContactInitial extends ContactState {} + +class ContactLoadedState extends ContactState{ + final List contactInformation; + const ContactLoadedState({required this.contactInformation}); + @override + List get props => []; +} + +class ContactLoadingState extends ContactState{ + +} +class ContactErrorState extends ContactState{ + final String message; + const ContactErrorState({required this.message}); + @override + List get props => [message]; +} \ No newline at end of file diff --git a/lib/bloc/profile/primary_information/identification/identification_bloc.dart b/lib/bloc/profile/primary_information/identification/identification_bloc.dart new file mode 100644 index 0000000..2ea178d --- /dev/null +++ b/lib/bloc/profile/primary_information/identification/identification_bloc.dart @@ -0,0 +1,21 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; + +import '../../../../model/profile/basic_information/identification_information.dart'; + +part 'identification_event.dart'; +part 'identification_state.dart'; + +class IdentificationBloc extends Bloc { + IdentificationBloc() : super(IdentificationInitial()) { + List identificationInformations = []; + on((event, emit) { + try{ + identificationInformations = event.identificationInformation; + emit(IdentificationLoadedState(identificationInformation: identificationInformations)); + }catch(e){ + emit(IdenficationErrorState(message: e.toString())); + } + }); + } +} diff --git a/lib/bloc/profile/primary_information/identification/identification_event.dart b/lib/bloc/profile/primary_information/identification/identification_event.dart new file mode 100644 index 0000000..03bbc09 --- /dev/null +++ b/lib/bloc/profile/primary_information/identification/identification_event.dart @@ -0,0 +1,15 @@ +part of 'identification_bloc.dart'; + +abstract class IdentificationEvent extends Equatable { + const IdentificationEvent(); + + @override + List get props => []; +} + +class GetIdentifications extends IdentificationEvent{ + final List identificationInformation; + const GetIdentifications({required this.identificationInformation}); + @override + List get props => [identificationInformation]; +} diff --git a/lib/bloc/profile/primary_information/identification/identification_state.dart b/lib/bloc/profile/primary_information/identification/identification_state.dart new file mode 100644 index 0000000..0cae35d --- /dev/null +++ b/lib/bloc/profile/primary_information/identification/identification_state.dart @@ -0,0 +1,28 @@ +part of 'identification_bloc.dart'; + +abstract class IdentificationState extends Equatable { + const IdentificationState(); + + @override + List get props => []; +} + +class IdentificationInitial extends IdentificationState {} + +class IdentificationLoadedState extends IdentificationState{ + final List identificationInformation; + const IdentificationLoadedState({required this.identificationInformation}); + @override + List get props => [identificationInformation]; +} + +class IdenficationErrorState extends IdentificationState{ + final String message; + const IdenficationErrorState({required this.message}); + @override + List get props => [message]; +} + +class IdentificationLoadingState extends IdentificationState{ + +} diff --git a/lib/screens/profile/components/basic_information/contact_information_screen.dart b/lib/screens/profile/components/basic_information/contact_information_screen.dart index 43edb28..e7601a1 100644 --- a/lib/screens/profile/components/basic_information/contact_information_screen.dart +++ b/lib/screens/profile/components/basic_information/contact_information_screen.dart @@ -1,5 +1,10 @@ import 'package:flutter/material.dart'; -import 'package:unit2/model/profile/basic_information/contact_information.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:unit2/bloc/profile/primary_information/contact/contact_bloc.dart'; +import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; @@ -7,73 +12,178 @@ import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; class ContactInformationScreen extends StatelessWidget { - final List contacts; - const ContactInformationScreen({super.key, required this.contacts}); + const ContactInformationScreen({ + super.key, + }); @override Widget build(BuildContext context) { return SafeArea( child: Scaffold( - appBar: AppBar( - title: const Text(contactScreenTitle), - centerTitle: true, - backgroundColor: primary, - actions: [AddLeading(onPressed: (){})], - ), - body: contacts.isNotEmpty? ListView.builder( - padding: const EdgeInsets.symmetric(vertical: 8,horizontal: 10), - itemCount: contacts.length, - itemBuilder: (BuildContext context, int index) { - String numberMail = contacts[index].numbermail!; - String commService = contacts[index].commService!.serviceProvider!.alias!; - return Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - decoration: box1(), - padding: const EdgeInsets.symmetric(horizontal: 12,vertical: 8), - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(numberMail,style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500)), - const Divider(), - const SizedBox(height: 5,), - Row( - children: [ - Expanded( - child: Text(commService - .toString(),style: Theme.of(context).textTheme.titleSmall,), - ), - - contacts[index].active==true? const Badge(backgroundColor: Colors.green, label: Text("Active",),):const SizedBox(), - const SizedBox(width: 5), - contacts[index].primary==true? const Badge(backgroundColor: Colors.blue, label: Text("Primary"),):const SizedBox() - ], - ), - const SizedBox(height: 5,), - Text(contacts[index].commService! - .serviceProvider!.agency!.name - .toString()), - - ]), - ), - IconButton(onPressed: (){}, icon: const Icon(Icons.more_vert,color: Colors.grey,)) - ], - ), - ), - const SizedBox(height: 5,), - - - - ], - ); - }):const EmptyData(message: "You don't have contact information added. Please click + to add"), - ), + appBar: AppBar( + title: const Text(contactScreenTitle), + centerTitle: true, + backgroundColor: primary, + actions: [AddLeading(onPressed: () {})], + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocBuilder( + builder: (context, state) { + if (state is UserLoggedIn) { + return BlocBuilder( + builder: (context, state) { + if (state is ProfileLoaded) { + return BlocConsumer( + listener: (context, state) { + if (state is ContactLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is ContactLoadedState || + state is ContactErrorState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + }, + builder: (context, state) { + if (state is ContactLoadedState) { + if (state.contactInformation.isNotEmpty) { + return ListView.builder( + padding: const EdgeInsets.symmetric( + vertical: 8, horizontal: 10), + itemCount: state.contactInformation.length, + itemBuilder: + (BuildContext context, int index) { + String numberMail = state + .contactInformation[index] + .numbermail!; + String commService = state + .contactInformation[index] + .commService! + .serviceProvider! + .alias!; + return Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Container( + decoration: box1(), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + Text(numberMail, + style: Theme.of( + context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight + .w500)), + const Divider(), + const SizedBox( + height: 5, + ), + Row( + children: [ + Expanded( + child: Text( + commService + .toString(), + style: Theme.of( + context) + .textTheme + .titleSmall, + ), + ), + state.contactInformation[index] + .active == + true + ? const Badge( + backgroundColor: + Colors + .green, + label: Text( + "Active", + ), + ) + : const SizedBox(), + const SizedBox( + width: 5), + state.contactInformation[index] + .primary == + true + ? const Badge( + backgroundColor: + Colors + .blue, + label: Text( + "Primary"), + ) + : const SizedBox() + ], + ), + const SizedBox( + height: 5, + ), + Text(state + .contactInformation[ + index] + .commService! + .serviceProvider! + .agency! + .name + .toString()), + ]), + ), + IconButton( + onPressed: () {}, + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + )) + ], + ), + ), + const SizedBox( + height: 5, + ), + ], + ); + }); + } else { + const EmptyData( + message: + "You don't have contact information added. Please click + to add"); + } + } + return Container(); + }, + ); + } + return Container(); + }, + ); + } + return Container(); + }, + ), + )), ); } } diff --git a/lib/screens/profile/components/basic_information/identification_information_screen.dart b/lib/screens/profile/components/basic_information/identification_information_screen.dart index 2eba37e..2d59569 100644 --- a/lib/screens/profile/components/basic_information/identification_information_screen.dart +++ b/lib/screens/profile/components/basic_information/identification_information_screen.dart @@ -1,4 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:unit2/bloc/profile/primary_information/identification/identification_bloc.dart'; +import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/model/profile/basic_information/identification_information.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; @@ -7,76 +10,140 @@ import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; +import '../../../../bloc/user/user_bloc.dart'; + class IdentificationsScreen extends StatelessWidget { - final List identities; - const IdentificationsScreen({super.key, required this.identities}); + const IdentificationsScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: const Text(identificationScreenTitle), - centerTitle: true, - backgroundColor: primary, - actions: [AddLeading(onPressed: (){})], - ), - body:identities.isNotEmpty? ListView.builder( - padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 10), - itemCount: identities.length, - itemBuilder: (BuildContext context, int index) { - String agency = identities[index].agency!.name!; - String idNumber = identities[index].identificationNumber!; - bool government = identities[index].agency!.privateEntity!; - String issuedAt = "${identities[index].issuedAt!.cityMunicipality!.description!} ${identities[index].issuedAt!.cityMunicipality!.province!.description}"; - return Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: screenWidth, - decoration: box1(), - padding: - const EdgeInsets.symmetric(horizontal: 12, vertical: 8), - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded(child: Text(agency,style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w400))), - const Divider(), - const SizedBox(height: 5,), - Expanded( - child: Text( - "$idNumberText : $idNumber",style: Theme.of(context).textTheme.titleSmall, - ), - ), - - Badge( - - backgroundColor: success2, - label: Text( - government == - true - ? privateText.toUpperCase() - :governmentText.toUpperCase(), - style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.white),)), - const SizedBox(height: 5,), - Text(issuedAt), - ]), - ), - IconButton( - onPressed: () {}, icon: const Icon(Icons.more_vert,color: Colors.grey,)) - ], - ), - ), - const SizedBox( - height: 5, - ), - ], - ); - }):const EmptyData(message: "You don't have identifications added. Please click + to add."), - ); + appBar: AppBar( + title: const Text(identificationScreenTitle), + centerTitle: true, + backgroundColor: primary, + actions: [AddLeading(onPressed: () {})], + ), + body: BlocBuilder( + builder: (context, state) { + if (state is UserLoggedIn) { + return BlocBuilder( + builder: (context, state) { + if (state is ProfileLoaded) { + return BlocConsumer( + listener: (context, state) {}, + builder: (context, state) { + if (state is IdentificationLoadedState) { + if (state.identificationInformation.isNotEmpty) { + return ListView.builder( + padding: const EdgeInsets.symmetric( + vertical: 8, horizontal: 10), + itemCount: state.identificationInformation.length, + itemBuilder: (BuildContext context, int index) { + String agency = + state.identificationInformation[index].agency!.name!; + String idNumber = + state.identificationInformation[index].identificationNumber!; + bool government = + state.identificationInformation[index].agency!.privateEntity!; + String issuedAt = + "${state.identificationInformation[index].issuedAt!.cityMunicipality!.description!} ${state.identificationInformation[index].issuedAt!.cityMunicipality!.province!.description}"; + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Container( + decoration: box1(), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text(agency, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight + .w400)), + const Divider(), + const SizedBox( + height: 5, + ), + Row(children: [ + Expanded( + child: Text( + "$idNumberText : $idNumber", + style: + Theme.of(context) + .textTheme + .titleSmall, + ), + ), + Badge( + backgroundColor: + success2, + label: Text( + government == true + ? privateText + .toUpperCase() + : governmentText + .toUpperCase(), + style: Theme.of( + context) + .textTheme + .bodySmall! + .copyWith( + color: Colors + .white), + )) + ]), + const SizedBox( + height: 5, + ), + Text(issuedAt), + ]), + ), + IconButton( + onPressed: () {}, + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + )) + ], + ), + ), + const SizedBox( + height: 5, + ), + ], + ); + }); + } else { + const EmptyData( + message: + "You don't have identifications added. Please click + to add."); + } + } + return Container(); + }, + ); + } + return Container(); + }, + ); + } + return Container(); + }, + )); } } diff --git a/lib/screens/profile/profile.dart b/lib/screens/profile/profile.dart index b9f3da9..7d444dc 100644 --- a/lib/screens/profile/profile.dart +++ b/lib/screens/profile/profile.dart @@ -9,6 +9,8 @@ import 'package:fluttericon/entypo_icons.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:fluttericon/modern_pictograms_icons.dart'; import 'package:unit2/bloc/profile/primary_information/address/address_bloc.dart'; +import 'package:unit2/bloc/profile/primary_information/contact/contact_bloc.dart'; +import 'package:unit2/bloc/profile/primary_information/identification/identification_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/screens/profile/components/basic_information/address_screen.dart'; import 'package:unit2/screens/profile/components/basic_information/citizenship_screen.dart'; @@ -117,35 +119,43 @@ class _ProfileInfoState extends State { })); }), subMenu(Icons.home, "Home Addresses", () { - Navigator.push(context, MaterialPageRoute( - builder: (BuildContext context) { - return BlocProvider( - create: (context) => AddressBloc() - ..add(GetAddress( - addresses: state.profileInformation.basicInfo.addresses)), - child: const AddressScreen(), - ); - })); - + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return BlocProvider( + create: (context) => AddressBloc() + ..add(GetAddress( + addresses: state.profileInformation + .basicInfo.addresses)), + child: const AddressScreen(), + ); + })); }), subMenu(Icons.contact_mail, "Identifications", () { Navigator.push(context, MaterialPageRoute( builder: (BuildContext context) { - return IdentificationsScreen( - identities: state.profileInformation - .basicInfo.identifications); + return BlocProvider( + create: (context) => IdentificationBloc() + ..add(GetIdentifications( + identificationInformation: state + .profileInformation + .basicInfo + .identifications)), + child: const IdentificationsScreen(), + ); })); }), subMenu(Icons.contact_phone, "Contact Info", () { - Navigator.push(context, MaterialPageRoute( - builder: (BuildContext context) { - return ContactInformationScreen( - contacts: state.profileInformation - .basicInfo.contactInformation, - ); - })); + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return BlocProvider( + create: (context) => ContactBloc() + ..add(GetContacts( + contactInformations: state.profileInformation.basicInfo.contactInformation)), + child: ContactInformationScreen(), + ); + })); }), subMenu(Icons.flag, "Citizenships", () { Navigator.push(context, MaterialPageRoute( @@ -162,7 +172,7 @@ class _ProfileInfoState extends State { icon: Elusive.group, title: "Family", onTap: () { - Navigator.push(context, MaterialPageRoute( + Navigator.push(context, MaterialPageRoute( builder: (BuildContext context) { return BlocProvider( create: (context) => FamilyBloc() @@ -300,28 +310,30 @@ class _ProfileInfoState extends State { }), subMenu(FontAwesome5.certificate, "Organization Memberships", () { - Navigator.push(context, MaterialPageRoute( + Navigator.push(context, MaterialPageRoute( builder: (BuildContext context) { return BlocProvider( - create: (context) => OrganizationMembershipBloc() - ..add(GetOrganizationMembership( - profileId: profileId!, - token: token!)), + create: (context) => + OrganizationMembershipBloc() + ..add(GetOrganizationMembership( + profileId: profileId!, + token: token!)), child: const OrgMembershipsScreen(), ); })); }), subMenu(Entypo.doc_text, "Non-Academic Recognitions", () { - - Navigator.push(context, MaterialPageRoute( + Navigator.push(context, MaterialPageRoute( builder: (BuildContext context) { return BlocProvider( - create: (context) => NonAcademicRecognitionBloc() - ..add(GetNonAcademicRecognition( - profileId: profileId!, - token: token!)), - child: const NonAcademicRecognitionScreen(), + create: (context) => + NonAcademicRecognitionBloc() + ..add(GetNonAcademicRecognition( + profileId: profileId!, + token: token!)), + child: + const NonAcademicRecognitionScreen(), ); })); }), From e087eb147daa651c161df5d6d2766a19529974e2 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Thu, 16 Mar 2023 15:53:42 +0800 Subject: [PATCH 51/86] replacing selectdot2 --- ios/Podfile.lock | 6 + .../profile/eligibility/eligibility_bloc.dart | 1 + lib/bloc/profile/profile_bloc.dart | 3 +- .../profile/workHistory/workHistory_bloc.dart | 223 ++++- .../workHistory/workHistory_event.dart | 20 + .../workHistory/workHistory_state.dart | 22 + lib/model/utils/agency_position.dart | 53 ++ .../components/eligibility/add_modal.dart | 787 +++++++++--------- .../profile/components/loading_screen.dart | 270 +++--- .../components/work_history/add_modal.dart | 680 +++++++++++++++ .../components/work_history_screen.dart | 123 ++- lib/screens/profile/profile.dart | 41 +- .../homepage.dart/components/dashboard.dart | 58 +- .../profile/work_history_services.dart | 163 +++- lib/utils/custom_dropdown_button.dart | 149 ++++ lib/utils/urls.dart | 14 +- lib/utils/validators.dart | 4 + pubspec.lock | 48 ++ pubspec.yaml | 7 + 19 files changed, 2062 insertions(+), 610 deletions(-) create mode 100644 lib/model/utils/agency_position.dart create mode 100644 lib/screens/profile/components/work_history/add_modal.dart create mode 100644 lib/utils/custom_dropdown_button.dart diff --git a/ios/Podfile.lock b/ios/Podfile.lock index e6a0ac0..26d9bc2 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -22,6 +22,8 @@ PODS: - FlutterMacOS - permission_handler_apple (9.0.4): - Flutter + - search_choices (0.0.1): + - Flutter - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS @@ -40,6 +42,7 @@ DEPENDENCIES: - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) + - search_choices (from `.symlinks/plugins/search_choices/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`) @@ -67,6 +70,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/path_provider_foundation/ios" permission_handler_apple: :path: ".symlinks/plugins/permission_handler_apple/ios" + search_choices: + :path: ".symlinks/plugins/search_choices/ios" shared_preferences_foundation: :path: ".symlinks/plugins/shared_preferences_foundation/ios" sqflite: @@ -83,6 +88,7 @@ SPEC CHECKSUMS: package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852 permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce + search_choices: b50731e8c425078048f681f39c34375c58d6ce8d shared_preferences_foundation: 297b3ebca31b34ec92be11acd7fb0ba932c822ca sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 SwiftProtobuf: b02b5075dcf60c9f5f403000b3b0c202a11b6ae1 diff --git a/lib/bloc/profile/eligibility/eligibility_bloc.dart b/lib/bloc/profile/eligibility/eligibility_bloc.dart index 13f14bc..9ce54ee 100644 --- a/lib/bloc/profile/eligibility/eligibility_bloc.dart +++ b/lib/bloc/profile/eligibility/eligibility_bloc.dart @@ -172,6 +172,7 @@ class EligibilityBloc extends Bloc { if (success) { event.eligibilities.removeWhere( ((EligibityCert element) => element.id == event.eligibilityId)); + List eligibilities = event.eligibilities; emit(DeletedState(success: success, eligibilities: eligibilities)); } else { diff --git a/lib/bloc/profile/profile_bloc.dart b/lib/bloc/profile/profile_bloc.dart index 6316746..162bb9e 100644 --- a/lib/bloc/profile/profile_bloc.dart +++ b/lib/bloc/profile/profile_bloc.dart @@ -9,8 +9,9 @@ class ProfileBloc extends Bloc { ProfileBloc() : super(ProfileInitial()) { ProfileInformation? globalProfileInformation; on((event, emit) async { + emit(ProfileLoading()); try { - emit(ProfileLoading()); + ProfileInformation? profileInformation = await ProfileService.instance.getProfile(event.token, event.userID); globalProfileInformation = profileInformation; diff --git a/lib/bloc/profile/workHistory/workHistory_bloc.dart b/lib/bloc/profile/workHistory/workHistory_bloc.dart index 3c8d4a5..bf5a416 100644 --- a/lib/bloc/profile/workHistory/workHistory_bloc.dart +++ b/lib/bloc/profile/workHistory/workHistory_bloc.dart @@ -1,23 +1,228 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:select2dot1/select2dot1.dart'; import 'package:unit2/model/profile/work_history.dart'; +import 'package:unit2/model/utils/agency.dart'; +import 'package:unit2/model/utils/agency_position.dart'; import 'package:unit2/sevices/profile/work_history_services.dart'; +import '../../../model/utils/category.dart'; + part 'workHistory_event.dart'; part 'workHistory_state.dart'; class WorkHistoryBloc extends Bloc { - List workExperiences = []; + List workExperiences = []; + List agencyPositions = []; + List agencies = []; + List appointmentStatus = []; + List agencyCategory = []; WorkHistoryBloc() : super(EducationInitial()) { - on((event, emit)async { - emit(WorkHistoryLoadingState()); - // try{ - List works = await WorkHistoryService.instance.getWorkExperiences(event.profileId, event.token); - workExperiences = works; + on((event, emit) async { + emit(WorkHistoryLoadingState()); + try { + List works = await WorkHistoryService.instance + .getWorkExperiences(event.profileId, event.token); + workExperiences = works; + emit(WorkHistoryLoaded(workExperiences: workExperiences)); + } catch (e) { + emit(WorkHistoryErrorState(message: e.toString())); + } + }); + + on((event,emit){ + emit(WorkHistoryLoadingState()); + workExperiences = event.workHistories; emit(WorkHistoryLoaded(workExperiences: workExperiences)); - // }catch(e){ - // emit(WorkHistoryErrorState(message: e.toString())); - // } + }); + on((event,emit)async{ + emit(WorkHistoryLoadingState()); + try{ + final bool success = await WorkHistoryService.instance.delete(profileId: event.profileId,token: event.token, work: event.workHistory); + if(success){ + event.workHistories.removeWhere((WorkHistory element) => element.id == event.workHistory.id); + List newWorkHistories = event.workHistories; + emit(DeletedState(success: success,workHistories: newWorkHistories)); + }else{ + emit(DeletedState(success: success, workHistories: event.workHistories)); + } + + }catch(e){ + emit(WorkHistoryErrorState(message: e.toString())); + } + }); + on((event, emit) async { + emit(WorkHistoryLoadingState()); + try { + /////POSITIONS------------------------------------------ + List positions = + await WorkHistoryService.instance.getAgencyPosition(); + agencyPositions = positions; + + /////AGENCIES------------------------------------------ + List newAgencies = + await WorkHistoryService.instance.getAgecies(); + agencies = newAgencies; + + /////Category Agency------------------------------------------ + List categoryAgencies = + await WorkHistoryService.instance.agencyCategory(); + agencyCategory = categoryAgencies; + /////////------------------------------------- + List status = WorkHistoryService.instance.getAppointmentStatusList(); + appointmentStatus = status; + + +List agricultureList =[]; +List businessInfoList =[]; +List constructionList =[]; +List educationList =[]; +List financeList =[]; +List foodList =[]; +List gamingList =[]; +List healthList =[]; +List motorList =[]; +List naturalList =[]; +List otherList =[]; +List personalList =[]; +List publicList =[]; +List realStateList =[]; +List safetyList =[]; +List transportList =[]; + for (Category category in agencyCategory) { + if (category.industryClass!.name == "Agriculture & Forestry/Wildlife") { + agricultureList + .add(SingleItemCategoryModel(nameSingleItem: category.name!)); + } + if (category.industryClass!.name == "Business & Information") { + businessInfoList + .add(SingleItemCategoryModel(nameSingleItem: category.name!)); + } + if (category.industryClass!.name == "Construction/Utilities/Contracting") { + constructionList + .add(SingleItemCategoryModel(nameSingleItem: category.name!)); + } + if (category.industryClass!.name == "Education") { + educationList + .add(SingleItemCategoryModel(nameSingleItem: category.name!)); + } + if (category.industryClass!.name == "Finance & Insurance") { + financeList + .add(SingleItemCategoryModel(nameSingleItem: category.name!)); + } + if (category.industryClass!.name == "Food & Hospitality") { + foodList + .add(SingleItemCategoryModel(nameSingleItem: category.name!)); + } + if (category.industryClass!.name == "Gaming") { + gamingList + .add(SingleItemCategoryModel(nameSingleItem: category.name!)); + } + if (category.industryClass!.name == "Health Services") { + healthList + .add(SingleItemCategoryModel(nameSingleItem: category.name!)); + } + if (category.industryClass!.name == "Motor Vehicle") { + motorList + .add(SingleItemCategoryModel(nameSingleItem: category.name!)); + } + if (category.industryClass!.name == "Natural Resources/Environmental") { + naturalList + .add(SingleItemCategoryModel(nameSingleItem: category.name!)); + } + if (category.industryClass!.name == "Other") { + otherList + .add(SingleItemCategoryModel(nameSingleItem: category.name!)); + } + if (category.industryClass!.name == "Personal Services") { + personalList + .add(SingleItemCategoryModel(nameSingleItem: category.name!)); + } + if (category.industryClass!.name == "Public Governance") { + publicList + .add(SingleItemCategoryModel(nameSingleItem: category.name!)); + } + if (category.industryClass!.name == "Real Estate & Housing") { + realStateList + .add(SingleItemCategoryModel(nameSingleItem: category.name!)); + } + if (category.industryClass!.name == "Safety/Security & Legal") { + safetyList + .add(SingleItemCategoryModel(nameSingleItem: category.name!)); + } + if (category.industryClass!.name == "Transportation") { + transportList + .add(SingleItemCategoryModel(nameSingleItem: category.name!)); + } + } + + SingleCategoryModel agricultureForestryWildlife = + SingleCategoryModel( + nameCategory: "Agriculture & Forestry/Wildlife", + singleItemCategoryList: agricultureList); + + SingleCategoryModel businessInformation = SingleCategoryModel( + nameCategory: "Business & Information", singleItemCategoryList: businessInfoList); + + SingleCategoryModel constructionUtilitiesContracting = + SingleCategoryModel( + nameCategory: "Construction/Utilities/Contracting", + singleItemCategoryList: constructionList); + SingleCategoryModel education = SingleCategoryModel( + nameCategory: "Education", singleItemCategoryList: educationList); + SingleCategoryModel financeInsurance = SingleCategoryModel( + nameCategory: "Finance & Insurance", singleItemCategoryList: financeList); + SingleCategoryModel foodHospitality = SingleCategoryModel( + nameCategory: "Food & Hospitality", singleItemCategoryList: foodList); + SingleCategoryModel gaming = SingleCategoryModel( + nameCategory: "Gaming", singleItemCategoryList: gamingList); + SingleCategoryModel healthServices = SingleCategoryModel( + nameCategory: "Health Services", singleItemCategoryList: healthList); + SingleCategoryModel motorVehicle = SingleCategoryModel( + nameCategory: "Motor Vehicle", singleItemCategoryList: motorList); + SingleCategoryModel naturalResourcesEnvironmental = + SingleCategoryModel( + nameCategory: "Natural Resources/Environmental", + singleItemCategoryList: naturalList); + SingleCategoryModel others = SingleCategoryModel( + nameCategory: "Others", singleItemCategoryList: otherList); + SingleCategoryModel personalServices = SingleCategoryModel( + nameCategory: "Personal Services", singleItemCategoryList: personalList); + SingleCategoryModel publicGovernance = SingleCategoryModel( + nameCategory: "Public Governance", singleItemCategoryList: publicList); + SingleCategoryModel realStateHousing = SingleCategoryModel( + nameCategory: "Real Estate & Housing", singleItemCategoryList: realStateList); + SingleCategoryModel safetySecurityLegal = SingleCategoryModel( + nameCategory: "Safety/Security & Legal", + singleItemCategoryList: safetyList); + SingleCategoryModel transportation = SingleCategoryModel( + nameCategory: "Transportation", singleItemCategoryList: transportList); + final List agencyCategoryDropdownData = [ + agricultureForestryWildlife, + businessInformation, + constructionUtilitiesContracting, + education, + financeInsurance, + foodHospitality, + gaming, + healthServices, + motorVehicle, + naturalResourcesEnvironmental, + others, + personalServices, + publicGovernance, + realStateHousing, + safetySecurityLegal, + transportation, + ]; + emit(AddWorkHistoryState( + agencyPositions:agencyPositions, + appointmentStatus: appointmentStatus, + agencyCategory: agencyCategoryDropdownData, + agencies: agencies)); + } catch (e) { + emit(WorkHistoryErrorState(message: e.toString())); + } }); } } diff --git a/lib/bloc/profile/workHistory/workHistory_event.dart b/lib/bloc/profile/workHistory/workHistory_event.dart index 7f3e066..5624086 100644 --- a/lib/bloc/profile/workHistory/workHistory_event.dart +++ b/lib/bloc/profile/workHistory/workHistory_event.dart @@ -17,4 +17,24 @@ class GetWorkHistories extends WorkHistorytEvent{ List get props => [profileId, token]; } +class LoadWorkHistories extends WorkHistorytEvent{ + final List workHistories; + const LoadWorkHistories({required this.workHistories}); + @override + List get props => [workHistories]; +} + +class ShowAddWorkHistoryForm extends WorkHistorytEvent{ + +} +class DeleteWorkHistory extends WorkHistorytEvent{ + final List workHistories; + final String token; + final int profileId; + final WorkHistory workHistory; + const DeleteWorkHistory({required this.profileId, required this.token, required this.workHistory, required this.workHistories}); + @override + List get props => [token, profileId,workHistory, workHistories]; +} + diff --git a/lib/bloc/profile/workHistory/workHistory_state.dart b/lib/bloc/profile/workHistory/workHistory_state.dart index 21158a9..ae4a74f 100644 --- a/lib/bloc/profile/workHistory/workHistory_state.dart +++ b/lib/bloc/profile/workHistory/workHistory_state.dart @@ -26,3 +26,25 @@ class WorkHistoryErrorState extends WorkHistoryState{ @override List get props => [message]; } + + + +class AddWorkHistoryState extends WorkHistoryState{ + final List agencyPositions; + final List agencies; + final List agencyCategory; + final List appointmentStatus; + + const AddWorkHistoryState({required this.agencyPositions, required this.appointmentStatus,required this.agencies,required this.agencyCategory}); + @override + List get props => [agencyPositions,appointmentStatus,agencies,agencyCategory]; + +} + +class DeletedState extends WorkHistoryState{ + final List workHistories; + final bool success; + const DeletedState({required this.success, required this.workHistories}); + @override + List get props => [workHistories,success]; +} diff --git a/lib/model/utils/agency_position.dart b/lib/model/utils/agency_position.dart new file mode 100644 index 0000000..3e6e48d --- /dev/null +++ b/lib/model/utils/agency_position.dart @@ -0,0 +1,53 @@ + +import 'dart:convert'; + +AgencyPosition agencyPositionFromJson(String str) => AgencyPosition.fromJson(json.decode(str)); + +String agencyPositionToJson(AgencyPosition data) => json.encode(data.toJson()); + +class AgencyPosition { + AgencyPosition({ + required this.id, + required this.title, + }); + + final int? id; + final String? title; + + factory AgencyPosition.fromJson(Map json) => AgencyPosition( + id: json["id"], + title: json["title"], + ); + + Map toJson() => { + "id": id, + "title": title, + }; +} +// To parse this JSON data, do +// +// final appoinemtStatus = appoinemtStatusFromJson(jsonString); +AppoinemtStatus appoinemtStatusFromJson(String str) => AppoinemtStatus.fromJson(json.decode(str)); + +String appoinemtStatusToJson(AppoinemtStatus data) => json.encode(data.toJson()); + +class AppoinemtStatus { + AppoinemtStatus({ + required this.value, + required this.label, + }); + + final String value; + final String label; + + factory AppoinemtStatus.fromJson(Map json) => AppoinemtStatus( + value: json["value"], + label: json["label"], + ); + + Map toJson() => { + "value": value, + "label": label, + }; +} + diff --git a/lib/screens/profile/components/eligibility/add_modal.dart b/lib/screens/profile/components/eligibility/add_modal.dart index 4e0fe91..7e7e9fc 100644 --- a/lib/screens/profile/components/eligibility/add_modal.dart +++ b/lib/screens/profile/components/eligibility/add_modal.dart @@ -62,427 +62,438 @@ class _AddEligibilityScreenState extends State { profileId = state.userData!.user!.login!.user!.profileId.toString(); return BlocBuilder( builder: (context, state) { - if(state is ProfileLoaded){ - return BlocBuilder( - buildWhen: (previous, current) { - if (state is EditEligibilityState) {} - return false; - }, - builder: (context, state) { - //EDIT ELIGIBILITY STATE - if (state is AddEligibilityState) { - return ProgressHUD( - child: Center( - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 25, horizontal: 18), - child: FormBuilder( - key: formKey, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - //ELIGIBILITIES DROPDOWN - FormBuilderDropdown( - onChanged: (Eligibility? eligibility) { - selectedEligibility = eligibility; - }, - autovalidateMode: - AutovalidateMode.onUserInteraction, - validator: (value) => - value == null ? 'required' : null, - items: state.eligibilities - .map>( - (Eligibility eligibility) { - return DropdownMenuItem( - value: eligibility, - child: Text(eligibility.title)); - }).toList(), - name: "eligibility", - decoration: normalTextFieldStyle( - "Eligibility", "Eligibility")), - const SizedBox( - height: 20, - ), - - SizedBox( - width: screenWidth, - child: Row( - children: [ - //LICENSE NUMBER - Flexible( - flex: 1, - child: FormBuilderTextField( - onChanged: (value) { - license = value; - }, - name: 'license_number', - decoration: normalTextFieldStyle( - "license number", - "license number"), - ), - ), - const SizedBox( - width: 12, - ), - //RATING - Flexible( - flex: 1, - child: FormBuilderTextField( - keyboardType: const TextInputType - .numberWithOptions(), - onChanged: (value) { - rating = value; - }, - name: 'rating', - decoration: normalTextFieldStyle( - 'rating %', 'rating'), - ), - ), - ], + if (state is ProfileLoaded) { + return BlocBuilder( + buildWhen: (previous, current) { + if (state is EditEligibilityState) {} + return false; + }, + builder: (context, state) { + //EDIT ELIGIBILITY STATE + if (state is AddEligibilityState) { + return ProgressHUD( + child: Center( + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, horizontal: 18), + child: FormBuilder( + key: formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + //ELIGIBILITIES DROPDOWN + FormBuilderDropdown( + onChanged: (Eligibility? eligibility) { + selectedEligibility = eligibility; + }, + autovalidateMode: + AutovalidateMode.onUserInteraction, + validator: (value) => + value == null ? 'required' : null, + items: state.eligibilities + .map>( + (Eligibility eligibility) { + return DropdownMenuItem( + value: eligibility, + child: Text(eligibility.title)); + }).toList(), + name: "eligibility", + decoration: normalTextFieldStyle( + "Eligibility", "Eligibility")), + const SizedBox( + height: 20, ), - ), - const SizedBox( - height: 20, - ), - SizedBox( - width: screenWidth, - child: Row( - children: [ - //EXAM DATE - Flexible( + + SizedBox( + width: screenWidth, + child: Row( + children: [ + //LICENSE NUMBER + Flexible( + flex: 1, + child: FormBuilderTextField( + onChanged: (value) { + license = value; + }, + name: 'license_number', + decoration: normalTextFieldStyle( + "license number", + "license number"), + ), + ), + const SizedBox( + width: 12, + ), + //RATING + Flexible( + flex: 1, + child: FormBuilderTextField( + keyboardType: const TextInputType + .numberWithOptions(), + onChanged: (value) { + rating = value; + }, + name: 'rating', + decoration: normalTextFieldStyle( + 'rating %', 'rating'), + ), + ), + ], + ), + ), + const SizedBox( + height: 20, + ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + //EXAM DATE + Flexible( + flex: 1, + child: DateTimePicker( + use24HourFormat: false, + icon: const Icon( + Icons.date_range), + controller: examDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + timeHintText: + "Date of Examination/Conferment", + decoration: + normalTextFieldStyle( + "Exam date", "") + .copyWith( + prefixIcon: + const Icon( + Icons.date_range, + color: Colors.black87, + )), + initialValue: null, + )), + const SizedBox( + width: 12, + ), + //VALIDITY DATE + Flexible( flex: 1, child: DateTimePicker( - use24HourFormat: false, - icon: - const Icon(Icons.date_range), - controller: examDateController, + controller: + validityDateController, firstDate: DateTime(1970), lastDate: DateTime(2100), - timeHintText: - "Date of Examination/Conferment", decoration: normalTextFieldStyle( - "Exam date", "") + "Validity date", + "Validity date") .copyWith( prefixIcon: const Icon( Icons.date_range, color: Colors.black87, )), initialValue: null, - )), - const SizedBox( - width: 12, - ), - //VALIDITY DATE - Flexible( - flex: 1, - child: DateTimePicker( - controller: validityDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), - decoration: normalTextFieldStyle( - "Validity date", - "Validity date") - .copyWith( - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - )), - initialValue: null, + ), ), - ), - ], + ], + ), ), - ), - const SizedBox( - height: 20, - ), - Text( - "Placement of Examination/Conferment", - style: Theme.of(context) - .textTheme - .displaySmall! - .copyWith( - fontSize: blockSizeVertical * 2), - ), - const SizedBox( - height: 12, - ), - //OVERSEAS ADDRESS SWITCH - Column( - children: [ - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - setState(() { - overseas = value; - }); - }, - decoration: - normalTextFieldStyle("", ''), - name: 'overseas', - title: const Text("Overseas Address?"), - ), - const SizedBox( - height: 20, - ), - //COUNTRY DROPDOWN - SizedBox( - child: overseas == true - ? FormBuilderDropdown( - initialValue: null, - validator: (value) => - value == null - ? 'required' - : null, - items: state.countries.map< - DropdownMenuItem< - Country>>( - (Country country) { - return DropdownMenuItem< - Country>( - value: country, - child: FittedBox( - child: Text(country - .name!))); - }).toList(), - name: 'country', - decoration: - normalTextFieldStyle( - "Country*", - "Country"), - onChanged: (Country? value) { - selectedCountry = value; - }, - ) - : Column( - children: [ - //REGION DROPDOWN - FormBuilderDropdown< - Region?>( - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: (value) => - value == null - ? 'required' - : null, - onChanged: (Region? - region) async { - setState(() { - provinceCall = true; - }); - selectedRegion = region; - getProvinces(); - }, - initialValue: - selectedRegion, - decoration: - normalTextFieldStyle( - "Region*", - "Region"), - name: 'region', - items: state.regions.map< - DropdownMenuItem< - Region>>( - (Region region) { - return DropdownMenuItem< - Region>( - value: region, - child: Text(region - .description!)); - }).toList(), - ), - const SizedBox( - height: 20, - ), - //PROVINCE DROPDOWN - SizedBox( - height: 70, - child: ModalProgressHUD( - color: - Colors.transparent, - inAsyncCall: - provinceCall, - child: DropdownButtonFormField< - Province?>( - autovalidateMode: - AutovalidateMode - .onUserInteraction, + const SizedBox( + height: 20, + ), + Text( + "Placement of Examination/Conferment", + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith( + fontSize: blockSizeVertical * 2), + ), + const SizedBox( + height: 12, + ), + //OVERSEAS ADDRESS SWITCH + Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value; + }); + }, + decoration: + normalTextFieldStyle("", ''), + name: 'overseas', + title: + const Text("Overseas Address?"), + ), + const SizedBox( + height: 20, + ), + //COUNTRY DROPDOWN + SizedBox( + child: overseas == true + ? FormBuilderDropdown( + initialValue: null, + validator: (value) => + value == null + ? 'required' + : null, + items: state.countries.map< + DropdownMenuItem< + Country>>( + (Country country) { + return DropdownMenuItem< + Country>( + value: country, + child: FittedBox( + child: Text( + country + .name!))); + }).toList(), + name: 'country', + decoration: + normalTextFieldStyle( + "Country*", + "Country"), + onChanged: + (Country? value) { + selectedCountry = value; + }, + ) + : Column( + children: [ + //REGION DROPDOWN + FormBuilderDropdown< + Region?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null + ? 'required' + : null, + onChanged: (Region? + region) async { + setState(() { + provinceCall = true; + }); + selectedRegion = + region; + getProvinces(); + }, + initialValue: + selectedRegion, + decoration: + normalTextFieldStyle( + "Region*", + "Region"), + name: 'region', + items: state.regions.map< + DropdownMenuItem< + Region>>((Region + region) { + return DropdownMenuItem< + Region>( + value: region, + child: Text(region + .description!)); + }).toList(), + ), + const SizedBox( + height: 20, + ), + //PROVINCE DROPDOWN + SizedBox( + height: 70, + child: ModalProgressHUD( + color: Colors + .transparent, + inAsyncCall: + provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + value: + selectedProvince, + onChanged: + (Province? + province) { + setState(() { + cityCall = + true; + }); + selectedProvince = + province; + getCities(); + }, + items: provinces == + null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>((Province + province) { + return DropdownMenuItem( + value: + province, + child: + FittedBox( + child: + Text(province.description!), + )); + }).toList(), + decoration: + normalTextFieldStyle( + "Province*", + "Province")), + ), + ), + + // CityMunicipalities dropdown + SizedBox( + height: 70, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: DropdownButtonFormField< + CityMunicipality>( validator: (value) => value == null ? 'required' : null, isExpanded: true, - value: - selectedProvince, onChanged: - (Province? - province) { - setState(() { - cityCall = true; - }); - selectedProvince = - province; - getCities(); + (CityMunicipality? + city) { + selectedMunicipality = + city; }, - items: provinces == + decoration: normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: + selectedMunicipality, + items: citymuns == null ? [] - : provinces!.map< - DropdownMenuItem< - Province>>((Province - province) { + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality + c) { return DropdownMenuItem( value: - province, - child: - FittedBox( - child: - Text(province.description!), - )); + c, + child: Text( + c.description!)); }).toList(), - decoration: - normalTextFieldStyle( - "Province*", - "Province")), - ), - ), - - // CityMunicipalities dropdown - SizedBox( - height: 70, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: cityCall, - child: - DropdownButtonFormField< - CityMunicipality>( - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - onChanged: - (CityMunicipality? - city) { - selectedMunicipality = - city; - }, - decoration: - normalTextFieldStyle( - "Municipality*", - "Municipality"), - value: - selectedMunicipality, - items: citymuns == - null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality - c) { - return DropdownMenuItem( - value: c, - child: Text( - c.description!)); - }).toList(), + ), ), ), - ), - const SizedBox( - height: 20, - ), - ], - )), - ], - ), + const SizedBox( + height: 20, + ), + ], + )), + ], + ), - const Expanded( - child: SizedBox(), - ), + const Expanded( + child: SizedBox(), + ), - SizedBox( - width: screenWidth, - height: 60, - child: ElevatedButton( - style: mainBtnStyle(primary, - Colors.transparent, second), - onPressed: () { - //rating - double? rate = rating == null - ? null - : double.parse(rating!); - //lisence - String? licenseNumber = license; - CityMunicipality? cityMunicipality = - selectedMunicipality; - DateTime? examDate = - examDateController.text.isEmpty - ? null - : DateTime.parse( - examDateController.text); - DateTime? validityDate = - validityDateController - .text.isEmpty - ? null - : DateTime.parse( - validityDateController - .text); + SizedBox( + width: screenWidth, + height: 60, + child: ElevatedButton( + style: mainBtnStyle(primary, + Colors.transparent, second), + onPressed: () { + //rating + double? rate = rating == null + ? null + : double.parse(rating!); + //lisence + String? licenseNumber = license; + CityMunicipality? cityMunicipality = + selectedMunicipality; + DateTime? examDate = + examDateController.text.isEmpty + ? null + : DateTime.parse( + examDateController + .text); + DateTime? validityDate = + validityDateController + .text.isEmpty + ? null + : DateTime.parse( + validityDateController + .text); - ExamAddress examAddress = ExamAddress( - barangay: null, - id: null, - addressCategory: null, - examAddressClass: null, - country: selectedCountry ?? - Country( - id: 175, - name: 'Philippines', - code: 'PH'), - cityMunicipality: - cityMunicipality); - EligibityCert eligibityCert = - EligibityCert( - id: null, - rating: rate, - examDate: examDate, - attachments: null, - eligibility: - selectedEligibility, - examAddress: examAddress, - validityDate: validityDate, - licenseNumber: licenseNumber, - overseas: overseas); - if (formKey.currentState! - .saveAndValidate()) { - context.read().add( - AddEligibility( - eligibityCert: - eligibityCert, - profileId: profileId!, - token: token!)); - } - // context.read().add(AddEligibility(eligibityCert: eligibityCert, profileId: profileId, token: token)) - }, - child: const Text(submit)), - ), - const SizedBox( - height: 20, - ), - ]), + ExamAddress examAddress = + ExamAddress( + barangay: null, + id: null, + addressCategory: null, + examAddressClass: null, + country: selectedCountry ?? + Country( + id: 175, + name: 'Philippines', + code: 'PH'), + cityMunicipality: + cityMunicipality); + EligibityCert eligibityCert = + EligibityCert( + id: null, + rating: rate, + examDate: examDate, + attachments: null, + eligibility: + selectedEligibility, + examAddress: examAddress, + validityDate: validityDate, + licenseNumber: + licenseNumber, + overseas: overseas); + if (formKey.currentState! + .saveAndValidate()) { + context + .read() + .add(AddEligibility( + eligibityCert: + eligibityCert, + profileId: profileId!, + token: token!)); + } + // context.read().add(AddEligibility(eligibityCert: eligibityCert, profileId: profileId, token: token)) + }, + child: const Text(submit)), + ), + const SizedBox( + height: 20, + ), + ]), + ), ), ), - ), - ); - } - return Container(); - }, - ); - } - return Container(); + ); + } + return Container(); + }, + ); + } + return Container(); }, ); } diff --git a/lib/screens/profile/components/loading_screen.dart b/lib/screens/profile/components/loading_screen.dart index e9ccf8b..f3078be 100644 --- a/lib/screens/profile/components/loading_screen.dart +++ b/lib/screens/profile/components/loading_screen.dart @@ -8,7 +8,7 @@ import 'package:fluttericon/modern_pictograms_icons.dart'; import 'package:unit2/screens/profile/components/main_menu.dart'; import 'package:unit2/screens/profile/components/submenu.dart'; import 'package:unit2/utils/global.dart'; - +import 'package:flutter_spinkit/flutter_spinkit.dart'; import '../../../theme-data.dart/colors.dart'; class LoadingScreen extends StatelessWidget { @@ -18,147 +18,143 @@ class LoadingScreen extends StatelessWidget { Widget build(BuildContext context) { return Stack( children: [ - + + Container( + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 12), + child: ListView( + children: [ + const Text( + "View and Update your Profile Information", + textAlign: TextAlign.center, + ), + ExpandableGroup( + collapsedIcon: const Icon(Icons.keyboard_arrow_down), + expandedIcon: const Icon(Icons.keyboard_arrow_up), + header: const ListTile( + leading: Icon( + Elusive.address_book, + color: primary, + ), + title: Text( + "Basic Information", + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + items: [ + subMenu(Icons.person, "Primary", () {}), + subMenu(Icons.home, "Home Addresses", () {}), + subMenu(Icons.contact_mail, "Identifications", () {}), + subMenu(Icons.contact_phone, "Contact Info", () {}), + subMenu(Icons.flag, "Citizenships", () {}), + ]), + const Divider(), + MainMenu( + icon: Elusive.group, + title: "Family", + onTap: () {}, + ), + const Divider(), + MainMenu( + icon: FontAwesome5.graduation_cap, + title: "Education", + onTap: () {}, + ), + const Divider(), + MainMenu( + icon: Icons.stars, + title: "Eligibility", + onTap: () {}, + ), + const Divider(), + MainMenu( + icon: FontAwesome5.shopping_bag, + title: "Work History", + onTap: () {}, + ), + const Divider(), + MainMenu( + icon: FontAwesome5.walking, + title: "Voluntary Work & Civic Services", + onTap: () {}, + ), + const Divider(), + MainMenu( + icon: Elusive.lightbulb, + title: "Learning & Development", + onTap: () {}, + ), + const Divider(), + MainMenu( + icon: Brandico.codepen, + title: "Personal References", + onTap: () {}, + ), + ExpandableGroup( + collapsedIcon: const Icon(Icons.keyboard_arrow_down), + expandedIcon: const Icon(Icons.keyboard_arrow_up), + header: const ListTile( + leading: Icon( + Icons.info, + color: primary, + ), + title: Text( + "Other Information", + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + items: [ + subMenu(Icons.fitness_center, "Skills & Hobbies", () {}), + subMenu(FontAwesome5.certificate, + "Organization Memberships", () {}), + subMenu( + Entypo.doc_text, "Non-Academic Recognitions", () {}), + ]), + ExpandableGroup( + collapsedIcon: const Icon(Icons.keyboard_arrow_down), + expandedIcon: const Icon(Icons.keyboard_arrow_up), + header: const ListTile( + leading: Icon( + FontAwesome5.laptop_house, + color: primary, + ), + title: Text( + "Assets", + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + items: [ + subMenu(ModernPictograms.home, "Real Property Tax", () {}), + ]), + ], + ), + ), Container( - padding: const EdgeInsets.symmetric( - vertical: 12, horizontal: 12), - child: ListView( - children: [ - const Text( - "View and Update your Profile Information",textAlign: TextAlign.center,), - ExpandableGroup( - collapsedIcon: - const Icon(Icons.keyboard_arrow_down), - expandedIcon: - const Icon(Icons.keyboard_arrow_up), - header: const ListTile( - leading: Icon( - Elusive.address_book, - color: primary, - ), - title: Text( - "Basic Information", - style: - TextStyle(fontWeight: FontWeight.bold), - ), - ), - items: [ - subMenu(Icons.person, "Primary",(){}), - subMenu(Icons.home, "Home Addresses",(){}), - subMenu( - Icons.contact_mail, "Identifications",(){}), - subMenu(Icons.contact_phone, "Contact Info",(){}), - subMenu(Icons.flag, "Citizenships",(){}), - ]), - const Divider(), - MainMenu( - icon: Elusive.group, - title: "Family", - onTap: () { - - }, - ), - const Divider(), - MainMenu( - icon: FontAwesome5.graduation_cap, - title: "Education", - onTap: () { - - }, - ), - const Divider(), - MainMenu( - icon: Icons.stars, - title: "Eligibility", - onTap: () { - - }, - ), - const Divider(), - MainMenu( - icon: FontAwesome5.shopping_bag, - title: "Work History", - onTap: () { - - }, - ), - const Divider(), - MainMenu( - icon: FontAwesome5.walking, - title: "Voluntary Work & Civic Services", - onTap: () { - - }, - ), - const Divider(), - MainMenu( - icon: Elusive.lightbulb, - title: "Learning & Development", - onTap: () { - - }, - ), - const Divider(), - MainMenu( - icon: Brandico.codepen, - title: "Personal References", - onTap: () { - - }, - ), - ExpandableGroup( - collapsedIcon: - const Icon(Icons.keyboard_arrow_down), - expandedIcon: - const Icon(Icons.keyboard_arrow_up), - header: const ListTile( - leading: Icon( - Icons.info, - color: primary, - ), - title: Text( - "Other Information", - style: - TextStyle(fontWeight: FontWeight.bold), - ), - ), - items: [ - subMenu( - Icons.fitness_center, "Skills & Hobbies",(){}), - subMenu(FontAwesome5.certificate, - "Organization Memberships",(){}), - subMenu(Entypo.doc_text, - "Non-Academic Recognitions",(){}), - ]), - ExpandableGroup( - collapsedIcon: - const Icon(Icons.keyboard_arrow_down), - expandedIcon: - const Icon(Icons.keyboard_arrow_up), - header: const ListTile( - leading: Icon( - FontAwesome5.laptop_house, - color: primary, - ), - title: Text( - "Assets", - style: - TextStyle(fontWeight: FontWeight.bold), - ), - ), - items: [ - subMenu(ModernPictograms.home, - "Real Property Tax",(){}), - ]), - ], - ), - ), - Container( width: screenWidth, height: screenHeight, color: Colors.white70, ), + Center( + child: Container( + height: 150, + width: 150, + decoration:const BoxDecoration( + color: Colors.black87, + borderRadius: BorderRadius.all(Radius.circular(25)) + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: const[ + SpinKitFadingCircle( + + color: Colors.white), + SizedBox(height: 10,), + Text("Loading Profile",textAlign: TextAlign.center, style: TextStyle(color: Colors.white),) + ], + ), + ), + ), ], ); } -} \ No newline at end of file +} diff --git a/lib/screens/profile/components/work_history/add_modal.dart b/lib/screens/profile/components/work_history/add_modal.dart new file mode 100644 index 0000000..522b6ac --- /dev/null +++ b/lib/screens/profile/components/work_history/add_modal.dart @@ -0,0 +1,680 @@ +import 'package:date_time_picker/date_time_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:searchfield/searchfield.dart'; +import 'package:select2dot1/select2dot1.dart'; +import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/profile/workHistory/workHistory_bloc.dart'; +import 'package:unit2/bloc/user/user_bloc.dart'; +import 'package:unit2/model/utils/agency.dart'; +import 'package:unit2/model/utils/agency_position.dart'; +import 'package:unit2/theme-data.dart/btn-style.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/theme-data.dart/form-style.dart'; +import 'package:unit2/utils/global.dart'; +import 'package:unit2/utils/text_container.dart'; +import 'package:unit2/utils/validators.dart'; + +class AddWorkHistoryScreen extends StatefulWidget { + const AddWorkHistoryScreen({super.key}); + + @override + State createState() => _AddWorkHistoryScreenState(); +} + +class _AddWorkHistoryScreenState extends State { + final addAgencyController = TextEditingController(); + final addPositionController = TextEditingController(); + final toDateController = TextEditingController(); + final fromDateController = TextEditingController(); + final _formKey = GlobalKey(); + AgencyPosition? selectedPosition; + Agency? selectedAgency; + AppoinemtStatus? selectedStatus; + String? salary; + String? salaryGrade; + String? salaryGradeStep; + SingleItemCategoryModel selectedAgencyCategory = + const SingleItemCategoryModel(nameSingleItem: ""); + ScrollController agencyScrollController = ScrollController(); + bool showAgency = false; + bool showSalaryGradeAndSalaryStep = false; + bool? isPrivate = false; + bool showIsPrivateRadio = false; + bool currentlyEmployed = false; + final agencyFocusNode = FocusNode(); + final positionFocusNode = FocusNode(); + final appointmentStatusNode = FocusNode(); + @override + void dispose() { + addPositionController.dispose(); + addAgencyController.dispose(); + toDateController.dispose(); + fromDateController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + print("exc"); + return BlocBuilder( + builder: (context, state) { + if (state is UserLoggedIn) { + return BlocBuilder( + builder: (context, state) { + if (state is ProfileLoaded) { + return BlocConsumer( + listener: (context, state) { + if (state is AddWorkHistoryState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + }, builder: (context, state) { + if (state is AddWorkHistoryState) { + return SingleChildScrollView( + child: SizedBox( + height: blockSizeVertical * 90, + child: FormBuilder( + key: _formKey, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, horizontal: 18), + child: Column( + children: [ + ////POSITIONS + StatefulBuilder(builder: (context, setState) { + return SearchField( + suggestions: state.agencyPositions + .map((AgencyPosition position) => + SearchFieldListItem(position.title!, + item: position)) + .toList(), + focusNode: positionFocusNode, + searchInputDecoration: + normalTextFieldStyle("Position *", "") + .copyWith( + suffixIcon: const Icon( + Icons.arrow_drop_down)), + initialValue: selectedPosition != null + ? SearchFieldListItem( + selectedPosition!.title!, + item: selectedPosition) + : null, + onSuggestionTap: (position) { + setState(() { + selectedPosition = position.item; + positionFocusNode.unfocus(); + }); + }, + emptyWidget: Container( + color: Colors.white, + height: 100, + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.center, + children: [ + const SizedBox( + height: 20, + ), + const Text("No result found"), + const SizedBox( + height: 10, + ), + TextButton( + onPressed: () { + showDialog( + context: context, + builder: (BuildContext + context) { + return AlertDialog( + title: const Text( + "Add Position"), + content: SizedBox( + height: 130, + child: Column( + children: [ + TextFormField( + controller: + addPositionController, + decoration: + normalTextFieldStyle( + "", + ""), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: double + .infinity, + height: 50, + child: ElevatedButton( + style: mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + AgencyPosition + newAgencyPosition = + AgencyPosition(id: null, title: addPositionController.text.toUpperCase()); + + state.agencyPositions.insert( + 0, + newAgencyPosition); + selectedPosition = + newAgencyPosition; + addPositionController.text = + ""; + setState( + () {}); + Navigator.pop( + context); + }, + child: const Text("Add"))), + ], + ), + ), + ); + }); + }, + child: + const Text("Add position")) + ]), + ), + validator: (position) { + if (position!.isEmpty) { + return "This field is required"; + } + return null; + }, + ); + }), + const SizedBox( + height: 12, + ), + ////APPOINTMENT STATUS' + SearchField( + suggestions: state.appointmentStatus + .map((AppoinemtStatus status) => + SearchFieldListItem(status.label, + item: status)) + .toList(), + focusNode: appointmentStatusNode, + validator: (value) { + if (value!.isEmpty) { + return "This field is required"; + } + return null; + }, + onSuggestionTap: (status) { + selectedStatus = status.item; + appointmentStatusNode.unfocus(); + }, + searchInputDecoration: normalTextFieldStyle( + "Appointment Status", "") + .copyWith( + suffixIcon: const Icon( + Icons.arrow_drop_down)), + ), + + const SizedBox( + height: 12, + ), + + ////AGENCY + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + SearchField( + itemHeight: 70, + focusNode: agencyFocusNode, + suggestions: state.agencies + .map((Agency agency) => + SearchFieldListItem( + agency.name!, + item: agency, + child: ListTile( + title: Text( + agency.name!, + overflow: TextOverflow + .ellipsis, + ), + subtitle: Text( + agency.privateEntity == + true + ? "Private" + : "Government"), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle("Agency *", "") + .copyWith( + suffixIcon: const Icon( + Icons.arrow_drop_down)), + onSuggestionTap: (agency) { + setState(() { + selectedAgency = agency.item; + + if (selectedAgency!.privateEntity == + null) { + showIsPrivateRadio = true; + } else { + showIsPrivateRadio = false; + } + if (selectedAgency!.privateEntity == + true) { + showSalaryGradeAndSalaryStep = + false; + } + if (selectedAgency!.privateEntity == + false) { + showSalaryGradeAndSalaryStep = + true; + } + agencyFocusNode.unfocus(); + }); + }, + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + emptyWidget: Container( + color: Colors.white, + height: 100, + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.center, + children: [ + const SizedBox( + height: 20, + ), + const Text("No result found"), + const SizedBox( + height: 10, + ), + TextButton( + onPressed: () { + showDialog( + context: context, + builder: (BuildContext + context) { + return AlertDialog( + title: const Text( + "Add Position"), + content: SizedBox( + height: 130, + child: Column( + children: [ + TextFormField( + controller: + addAgencyController, + decoration: + normalTextFieldStyle( + "", + ""), + ), + const SizedBox( + height: + 12, + ), + SizedBox( + width: double + .infinity, + height: + 50, + child: ElevatedButton( + style: mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + setState(() { + Agency newAgency = Agency(id: null, name: addAgencyController.text.toUpperCase(), category: null, privateEntity: null); + + state.agencies.insert(0, newAgency); + selectedAgency = newAgency; + addAgencyController.text = ""; + showAgency = true; + + showIsPrivateRadio = true; + + Navigator.pop(context); + }); + }, + child: const Text("Add"))), + ], + ), + ), + ); + }); + }, + child: const Text( + "Add Agency")) + ]), + ), + ), + ////AGENCY CATEGORY + + SizedBox( + child: showAgency + ? Select2dot1( + selectEmptyInfoSettings: + const SelectEmptyInfoSettings( + text: + "Select Agency Category"), + + scrollController: + agencyScrollController, + selectDataController: + SelectDataController( + isMultiSelect: false, + data: state + .agencyCategory, + initSelected: [ + selectedAgencyCategory + ]), + onChanged: (value) { + print("sdasdsa"); + // print(value[0].nameSingleItem); + // setState(() { + // selectedAgencyCategory = + // value[0]; + // print(value[0].nameSingleItem); + // agencyFocusNode.unfocus(); + // }); + }, + ) + : const SizedBox(), + ), + + ////PRVIATE SECTOR + SizedBox( + child: showIsPrivateRadio + ? FormBuilderRadioGroup( + decoration: InputDecoration( + border: InputBorder.none, + label: Row( + children: [ + Text( + "Is this private sector? ", + style: Theme.of( + context) + .textTheme + .headlineSmall! + .copyWith( + fontSize: 24), + ), + const Icon(FontAwesome + .help_circled) + ], + ), + ), + + ////onvhange private sector + onChanged: (value) { + setState(() { + if (value.toString() == + "YES") { + isPrivate = true; + showSalaryGradeAndSalaryStep = + false; + } else { + isPrivate = false; + showSalaryGradeAndSalaryStep = + true; + } + agencyFocusNode.unfocus(); + }); + }, + + name: 'isPrivate', + validator: + FormBuilderValidators + .required(), + options: ["YES", "NO"] + .map((lang) => + FormBuilderFieldOption( + value: lang)) + .toList(growable: false), + ) + : const SizedBox()), + ////SALARY GRADE AND SALARY GRADE STEP + SizedBox( + child: showSalaryGradeAndSalaryStep + ? Column( + children: [ + Row( + children: [ + Flexible( + flex: 1, + child: + FormBuilderTextField( + name: + 'salary_grade', + keyboardType: + TextInputType + .number, + decoration: + normalTextFieldStyle( + "Salary Grade (SG)", + "0"), + onChanged: (value) { + salaryGrade = + value; + }, + ), + ), + const SizedBox( + width: 12, + ), + Flexible( + flex: 1, + child: + FormBuilderTextField( + name: 'salary_step', + keyboardType: + TextInputType + .number, + decoration: + normalTextFieldStyle( + "SG Step (SG)", + "0"), + onChanged: (value) { + setState(() { + salaryGradeStep = + value; + }); + }, + ), + ) + ], + ) + ], + ) + : null), + ], + ); + }), + const SizedBox( + height: 12, + ), + ////MONTHLY SALARY + FormBuilderTextField( + onChanged: (value) { + setState(() { + salary = value; + }); + }, + validator: numericRequired, + name: "salary", + decoration: normalTextFieldStyle( + "Monthly Salary *", "") + .copyWith(prefix: const Text("₱ ")), + ), + + const SizedBox( + height: 12, + ), + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + ////CURRENTLY EMPLOYED + FormBuilderSwitch( + initialValue: currentlyEmployed, + activeColor: second, + onChanged: (value) { + setState(() { + if (value == true) { + currentlyEmployed = true; + toDateController.text = "PRESENT"; + } else { + currentlyEmployed = false; + toDateController.text = ""; + } + }); + }, + decoration: + normalTextFieldStyle("", ''), + name: 'overseas', + title: + const Text("Currently Employed?"), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + //// FROM DATE + Flexible( + flex: 1, + child: DateTimePicker( + validator: (value) { + if (value == null) { + return "This field is required"; + } + return null; + }, + use24HourFormat: false, + icon: const Icon( + Icons.date_range), + controller: + fromDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + timeHintText: + "Date of Examination/Conferment", + decoration: + normalTextFieldStyle( + "From *", + "From *") + .copyWith( + prefixIcon: + const Icon( + Icons.date_range, + color: Colors.black87, + )), + initialValue: null, + )), + const SizedBox( + width: 12, + ), + //// TO DATE + Flexible( + flex: 1, + child: currentlyEmployed + ? TextFormField( + enabled: false, + initialValue: "PRESENT", + style: const TextStyle( + color: + Colors.black45), + decoration: + normalTextFieldStyle( + "", "") + .copyWith(), + ) + : DateTimePicker( + validator: (val) { + print(val); + return null; + }, + controller: + toDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + decoration: normalTextFieldStyle( + "To *", "To *") + .copyWith( + prefixIcon: + const Icon( + Icons + .date_range, + color: Colors + .black87, + ), + prefixText: + currentlyEmployed + ? "PRESENT" + : ""), + initialValue: null, + ), + ), + ], + ), + ), + ], + ); + }), + const Expanded(child: SizedBox()), + ////SUBMIT BUTTON + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + print(selectedPosition?.title); + print(selectedStatus?.label); + print(selectedAgency?.name); + print(salary); + print(fromDateController.text); + print(toDateController.text); + print(isPrivate); + print(salaryGrade); + print(salaryGradeStep); + if (_formKey.currentState! + .validate()) {} + }, + child: const Text(submit)), + ), + const SizedBox( + height: 20, + ), + ], + ), + ), + ), + ), + ); + } + return Container(); + }); + } + return Container(); + }, + ); + } + return const Center( + child: Text("Add Work History"), + ); + }, + ); + } +} diff --git a/lib/screens/profile/components/work_history_screen.dart b/lib/screens/profile/components/work_history_screen.dart index 87666fa..580d33a 100644 --- a/lib/screens/profile/components/work_history_screen.dart +++ b/lib/screens/profile/components/work_history_screen.dart @@ -1,11 +1,13 @@ +import 'package:app_popup_menu/app_popup_menu.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; import 'package:intl/intl.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; -import 'package:unit2/model/profile/work_history.dart'; +import 'package:unit2/screens/profile/components/work_history/add_modal.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; @@ -14,6 +16,7 @@ import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; import '../../../bloc/profile/workHistory/workHistory_bloc.dart'; +import '../../../utils/alerts.dart'; import '../../../utils/global.dart'; class WorkHistoryScreen extends StatelessWidget { @@ -22,24 +25,32 @@ class WorkHistoryScreen extends StatelessWidget { @override Widget build(BuildContext context) { DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); + String? token; + int profileId; return Scaffold( appBar: AppBar( title: const Text(workHistoryScreenTitle), backgroundColor: primary, centerTitle: true, - actions: [AddLeading(onPressed: () {})], + actions: [ + AddLeading(onPressed: () { + context.read().add(ShowAddWorkHistoryForm()); + }) + ], ), body: //UserBloc ProgressHUD( padding: const EdgeInsets.all(24), - backgroundColor: Colors.black87, + backgroundColor: Colors.black87, indicatorWidget: const SpinKitFadingCircle( color: Colors.white, ), child: BlocBuilder( builder: (context, state) { if (state is UserLoggedIn) { + token = state.userData!.user!.login!.token; + profileId = state.userData!.user!.login!.user!.profileId!; return BlocBuilder( builder: (context, state) { //ProfileBloc @@ -51,10 +62,32 @@ class WorkHistoryScreen extends StatelessWidget { final progress = ProgressHUD.of(context); progress!.showWithText("Please wait..."); } - if (state is WorkHistoryLoaded || state is WorkHistoryErrorState) { + if (state is WorkHistoryLoaded || + state is WorkHistoryErrorState || + state is AddWorkHistoryState) { final progress = ProgressHUD.of(context); progress!.dismiss(); } + //DELETED STATE + if (state is DeletedState) { + if (state.success) { + successAlert(context, "Deletion Successfull", + "Work has been deleted successfully", () { + Navigator.of(context).pop(); + context.read().add( + LoadWorkHistories( + workHistories: state.workHistories)); + }); + } else { + errorAlert(context, "Deletion Failed", + "Error deleting Work History", () { + Navigator.of(context).pop(); + context.read().add( + LoadWorkHistories( + workHistories: state.workHistories)); + }); + } + } }, builder: (context, state) { if (state is WorkHistoryLoaded) { @@ -77,8 +110,9 @@ class WorkHistoryScreen extends StatelessWidget { state.workExperiences[index].toDate == null ? present.toUpperCase() - : dteFormat2.format( - state.workExperiences[index].toDate!); + : dteFormat2.format(state + .workExperiences[index] + .toDate!); return Column( children: [ Container( @@ -127,12 +161,55 @@ class WorkHistoryScreen extends StatelessWidget { ), ], )), - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - )) + AppPopupMenu( + offset: const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + final progress = + ProgressHUD.of(context); + progress! + .showWithText("Loading..."); + ////delete workhistory-= = = = = = = = =>> + if (value == 2) { + confirmAlert(context, () { + BlocProvider.of( + context) + .add(DeleteWorkHistory( + profileId: + profileId, + token: token!, + workHistory: state + .workExperiences[ + index], + workHistories: state + .workExperiences)); + }, "Delete?", + "Confirm Delete?"); + } + if (value == 1) { + ////edit eligibilty-= = = = = = = = =>> + } + }, + menuItems: [ + popMenuItem( + text: "Edit", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Delete", + value: 2, + icon: Icons.delete), + popMenuItem( + text: "Attachment", + value: 3, + icon: FontAwesome.attach) + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ) ]), ), const SizedBox( @@ -147,6 +224,9 @@ class WorkHistoryScreen extends StatelessWidget { "You don't have any work experience added. Please click + to add"); } } + if (state is AddWorkHistoryState) { + return const AddWorkHistoryScreen(); + } if (state is WorkHistoryErrorState) { return SomethingWentWrong( message: state.message, onpressed: () {}); @@ -165,3 +245,22 @@ class WorkHistoryScreen extends StatelessWidget { )); } } + +PopupMenuItem popMenuItem({String? text, int? value, IconData? icon}) { + return PopupMenuItem( + value: value, + child: Row( + children: [ + Icon( + icon, + ), + const SizedBox( + width: 10, + ), + Text( + text!, + ), + ], + ), + ); +} diff --git a/lib/screens/profile/profile.dart b/lib/screens/profile/profile.dart index 7d444dc..da19d56 100644 --- a/lib/screens/profile/profile.dart +++ b/lib/screens/profile/profile.dart @@ -43,18 +43,14 @@ import '../../bloc/user/user_bloc.dart'; import 'components/main_menu.dart'; import 'components/submenu.dart'; -class ProfileInfo extends StatefulWidget { +class ProfileInfo extends StatelessWidget { const ProfileInfo({super.key}); - @override - State createState() => _ProfileInfoState(); -} - -class _ProfileInfoState extends State { - int? profileId; - String? token; @override Widget build(BuildContext context) { + int? profileId; + + String? token; return Scaffold( appBar: AppBar( backgroundColor: primary, @@ -62,6 +58,7 @@ class _ProfileInfoState extends State { title: const Text('Profile'), ), body: ProgressHUD( + padding: const EdgeInsets.all(24), backgroundColor: Colors.black87, indicatorWidget: const SpinKitFadingCircle(color: Colors.white), child: BlocBuilder(builder: (context, state) { @@ -69,12 +66,10 @@ class _ProfileInfoState extends State { profileId = state.userData!.user!.login!.user!.profileId; token = state.userData!.user!.login!.token!; return BlocConsumer( - listener: (context, state) { + listener: (context, state,){ if (state is ProfileLoading) { final progress = ProgressHUD.of(context); - progress?.showWithText( - 'Loading Profile', - ); + progress!.showWithText("Please wait..."); } if (state is ProfileLoaded || state is ProfileErrorState) { final progress = ProgressHUD.of(context); @@ -147,15 +142,18 @@ class _ProfileInfoState extends State { }), subMenu(Icons.contact_phone, "Contact Info", () { - Navigator.push(context, MaterialPageRoute( - builder: (BuildContext context) { - return BlocProvider( - create: (context) => ContactBloc() - ..add(GetContacts( - contactInformations: state.profileInformation.basicInfo.contactInformation)), - child: ContactInformationScreen(), - ); - })); + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return BlocProvider( + create: (context) => ContactBloc() + ..add(GetContacts( + contactInformations: state + .profileInformation + .basicInfo + .contactInformation)), + child: const ContactInformationScreen(), + ); + })); }), subMenu(Icons.flag, "Citizenships", () { Navigator.push(context, MaterialPageRoute( @@ -361,6 +359,7 @@ class _ProfileInfoState extends State { ); } if (state is ProfileLoading) { + return const LoadingScreen(); } if (state is ProfileErrorState) { diff --git a/lib/screens/unit2/homepage.dart/components/dashboard.dart b/lib/screens/unit2/homepage.dart/components/dashboard.dart index f46b533..eed381c 100644 --- a/lib/screens/unit2/homepage.dart/components/dashboard.dart +++ b/lib/screens/unit2/homepage.dart/components/dashboard.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:unit2/screens/unit2/homepage.dart/module-screen.dart'; -import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; @@ -25,11 +24,11 @@ class DashBoard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - roles[index].name, + roles[index].name.toUpperCase(), style: Theme.of(context) .textTheme .labelLarge! - .copyWith(fontSize: 14), + .copyWith(fontSize: 12), ), const SizedBox( @@ -38,38 +37,51 @@ class DashBoard extends StatelessWidget { GridView.count( shrinkWrap: true, crossAxisCount: 4, - crossAxisSpacing: 5, - mainAxisSpacing: 2, + crossAxisSpacing: 8, + mainAxisSpacing: 10, physics: const BouncingScrollPhysics(), padding: const EdgeInsets.symmetric( vertical: 5, horizontal: 5), children: roles[index].roles.map((role) { return Container( - padding: const EdgeInsets.all(8), - decoration:box1(), + padding: const EdgeInsetsDirectional.fromSTEB(8,5,8,13), + alignment: Alignment.center, + + decoration:const BoxDecoration( + color: Colors.white, + boxShadow:[ BoxShadow(color: Colors.black12,spreadRadius: 2,blurRadius: 3)], + borderRadius: BorderRadius.all(Radius.circular(8)) + ), child: GestureDetector( onTap: () { }, - child: Column(children: [ - Icon( - role.icon, - size: 24, - color: second, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Icon( + + role.icon, + size: 20, + weight: 100, + grade: 100, + color:second, + ), ), const SizedBox( height: 5, ), - Expanded( - child: Text( - role.role.name!, - textAlign: TextAlign.center, - style: Theme.of(context) - .textTheme - .labelLarge! - .copyWith( - fontSize: blockSizeVertical*1.1, - fontWeight: FontWeight.bold), - ), + Text( + role.role.name!.toLowerCase() == "establishment point-person"?"Est. point-person": + role.role.name!, + textAlign: TextAlign.center, + style: Theme.of(context) + .textTheme + .labelLarge! + .copyWith( + fontSize: blockSizeVertical*1.1, + fontWeight: FontWeight.bold), ), ]), ), diff --git a/lib/sevices/profile/work_history_services.dart b/lib/sevices/profile/work_history_services.dart index b62f526..cb93409 100644 --- a/lib/sevices/profile/work_history_services.dart +++ b/lib/sevices/profile/work_history_services.dart @@ -2,38 +2,165 @@ import 'dart:convert'; import 'package:unit2/model/profile/work_history.dart'; - import 'package:http/http.dart' as http; +import 'package:unit2/model/utils/agency.dart'; +import 'package:unit2/model/utils/agency_position.dart'; +import 'package:unit2/model/utils/category.dart'; import 'package:unit2/utils/request.dart'; import '../../utils/urls.dart'; -class WorkHistoryService{ + +class WorkHistoryService { static final WorkHistoryService _instance = WorkHistoryService(); static WorkHistoryService get instance => _instance; - Future > getWorkExperiences( int profileId, String token)async{ - List workExperiences = []; - String authToken = "Token $token"; - Map headers = { + Future> getWorkExperiences( + int profileId, String token) async { + List workExperiences = []; + String authToken = "Token $token"; + Map headers = { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': authToken }; - String path= Url.instance.getWorkHistories()+profileId.toString(); - // try{ - http.Response response =await Request.instance.getRequest(path: path, headers: headers, param: {}); - if(response.statusCode == 200){ + String path = Url.instance.getWorkHistories() + profileId.toString(); + try { + http.Response response = await Request.instance + .getRequest(path: path, headers: headers, param: {}); + if (response.statusCode == 200) { Map data = jsonDecode(response.body); - if(data['data'] != null){ - data['data'].forEach((var workHistory){ + if (data['data'] != null) { + data['data'].forEach((var workHistory) { workExperiences.add(WorkHistory.fromJson(workHistory)); }); } - } - // }catch(e){ - // throw e.toString(); - // } + } catch (e) { + throw e.toString(); + } return workExperiences; - } -} \ No newline at end of file + + Future> getAgecies() async { + List agencies = []; + String path = Url.instance.getAgencies(); + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + try { + http.Response response = await Request.instance + .getRequest(path: path, param: {}, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((var agency) { + Agency newAgency = Agency.fromJson(agency); + agencies.add(newAgency); + }); + } + } + } catch (e) { + throw e.toString(); + } + return agencies; + } + + Future delete( + {required int profileId, + required String token, + required WorkHistory work}) async { + bool? success; + Map params = {"force_mode": "true"}; + String authToken = "Token $token"; + String path = "${Url.instance.deleteWorkHistory()}$profileId/"; + Map body = { + "id": work.id, + "position_id": work.position!.id, + "agency_id": work.agency!.id, + "from_date": work.fromDate?.toString(), + "to_date": work.toDate?.toString(), + "monthly_salary": work.monthlySalary, + "appointment_status": work.appointmentStatus, + "salary_step": work.sgStep, + "salary_grade": work.salaryGrade, + }; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + try { + http.Response response = await Request.instance.deleteRequest( + path: path, headers: headers, body: body, param: params); + + if(response.statusCode == 200){ + Map data = jsonDecode(response.body); + success = data['success']; + }else{ + success = false; + } + } catch (e) { + throw e.toString(); + } + return success!; + } + + Future> agencyCategory() async { + List agencyCategory = []; + String path = Url.instance.getAgencyCategory(); + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + try { + http.Response response = await Request.instance + .getRequest(param: {}, path: path, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((var agency) { + Category category = Category.fromJson(agency); + agencyCategory.add(category); + }); + } + } + } catch (e) { + throw e.toString(); + } + return agencyCategory; + } + + Future> getAgencyPosition() async { + List agencyPositions = []; + String path = Url.instance.getPositions(); + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + try { + http.Response response = await Request.instance + .getRequest(param: {}, path: path, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((var agencyPosition) { + AgencyPosition position = AgencyPosition.fromJson(agencyPosition); + agencyPositions.add(position); + }); + } + } + } catch (e) { + throw (e.toString()); + } + return agencyPositions; + } + + List getAppointmentStatusList() { + return [ + AppoinemtStatus(value: "Appointed", label: "Appointed"), + AppoinemtStatus(value: "Casual", label: "Casual"), + AppoinemtStatus( + value: "Contact of Service", label: "Contract of Service"), + AppoinemtStatus(value: "Coterminous", label: "Coterminous"), + AppoinemtStatus(value: "Elected", label: "Elected"), + AppoinemtStatus(value: "Job Order", label: "Permanent"), + AppoinemtStatus(value: "Elected", label: "Elected"), + ]; + } +} diff --git a/lib/utils/custom_dropdown_button.dart b/lib/utils/custom_dropdown_button.dart new file mode 100644 index 0000000..11bf81d --- /dev/null +++ b/lib/utils/custom_dropdown_button.dart @@ -0,0 +1,149 @@ +import 'package:dropdown_button2/dropdown_button2.dart'; +import 'package:flutter/material.dart'; + +class CustomDropdownButton2 extends StatelessWidget { + final String hint; + final String? value; + final List dropdownItems; + final ValueChanged? onChanged; + final DropdownButtonBuilder? selectedItemBuilder; + final Alignment? hintAlignment; + final Alignment? valueAlignment; + final double? buttonHeight, buttonWidth; + final EdgeInsetsGeometry? buttonPadding; + final BoxDecoration? buttonDecoration; + final int? buttonElevation; + final Widget? icon; + final double? iconSize; + final Color? iconEnabledColor; + final Color? iconDisabledColor; + final double? itemHeight; + final EdgeInsetsGeometry? itemPadding; + final double? dropdownHeight, dropdownWidth; + final EdgeInsetsGeometry? dropdownPadding; + final BoxDecoration? dropdownDecoration; + final int? dropdownElevation; + final Radius? scrollbarRadius; + final double? scrollbarThickness; + final bool? scrollbarAlwaysShow; + final Offset offset; + + const CustomDropdownButton2({ + required this.hint, + required this.value, + required this.dropdownItems, + required this.onChanged, + this.selectedItemBuilder, + this.hintAlignment, + this.valueAlignment, + this.buttonHeight, + this.buttonWidth, + this.buttonPadding, + this.buttonDecoration, + this.buttonElevation, + this.icon, + this.iconSize, + this.iconEnabledColor, + this.iconDisabledColor, + this.itemHeight, + this.itemPadding, + this.dropdownHeight, + this.dropdownWidth, + this.dropdownPadding, + this.dropdownDecoration, + this.dropdownElevation, + this.scrollbarRadius, + this.scrollbarThickness, + this.scrollbarAlwaysShow, + this.offset = const Offset(0, 0), + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return DropdownButtonHideUnderline( + child: DropdownButton2( + //To avoid long text overflowing. + isExpanded: true, + hint: Container( + alignment: hintAlignment, + child: Text( + hint, + overflow: TextOverflow.ellipsis, + maxLines: 1, + style: TextStyle( + fontSize: 14, + color: Theme.of(context).hintColor, + ), + ), + ), + value: value, + items: dropdownItems + .map((item) => DropdownMenuItem( + value: item, + child: Container( + alignment: valueAlignment, + child: Text( + item, + overflow: TextOverflow.ellipsis, + maxLines: 1, + style: const TextStyle( + fontSize: 14, + ), + ), + ), + )) + .toList(), + onChanged: onChanged, + selectedItemBuilder: selectedItemBuilder, + buttonStyleData: ButtonStyleData( + height: buttonHeight ?? 40, + width: buttonWidth ?? 140, + padding: buttonPadding ?? const EdgeInsets.only(left: 14, right: 14), + decoration: buttonDecoration ?? + BoxDecoration( + borderRadius: BorderRadius.circular(14), + border: Border.all( + color: Colors.black45, + ), + ), + elevation: buttonElevation, + ), + iconStyleData: IconStyleData( + icon: icon ?? const Icon(Icons.arrow_forward_ios_outlined), + iconSize: iconSize ?? 12, + iconEnabledColor: iconEnabledColor, + iconDisabledColor: iconDisabledColor, + ), + dropdownStyleData: DropdownStyleData( + //Max height for the dropdown menu & becoming scrollable if there are more items. If you pass Null it will take max height possible for the items. + maxHeight: dropdownHeight ?? 200, + width: dropdownWidth ?? 140, + padding: dropdownPadding, + decoration: dropdownDecoration ?? + BoxDecoration( + borderRadius: BorderRadius.circular(14), + ), + elevation: dropdownElevation ?? 8, + //Null or Offset(0, 0) will open just under the button. You can edit as you want. + offset: offset, + //Default is false to show menu below button + isOverButton: false, + scrollbarTheme: ScrollbarThemeData( + radius: scrollbarRadius ?? const Radius.circular(40), + thickness: scrollbarThickness != null + ? MaterialStateProperty.all(scrollbarThickness!) + : null, + thumbVisibility: scrollbarAlwaysShow != null + ? MaterialStateProperty.all(scrollbarAlwaysShow!) + : null, + ), + ), + menuItemStyleData: MenuItemStyleData( + height: itemHeight ?? 40, + padding: itemPadding ?? const EdgeInsets.only(left: 14, right: 14), + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index 79444f4..fb0fd90 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -3,7 +3,7 @@ class Url { static Url get instance => _instance; String host() { - // // return '192.168.10.221:3003'; + // // // return '192.168.10.221:3003'; // return 'agusandelnorte.gov.ph'; // return "192.168.10.219:3000"; return "devweb.agusandelnorte.gov.ph"; @@ -45,6 +45,18 @@ String updateEligibility(){ String getWorkHistories(){ return "/api/jobnet_app/profile/pds/work/"; } +String getPositions(){ + return "/api/jobnet_app/positions/"; +} +String getAgencies(){ + return "/api/jobnet_app/agencies/"; +} +String getAgencyCategory(){ + return "api/jobnet_app/agency_categories/"; +} +String deleteWorkHistory(){ + return "/api/jobnet_app/profile/pds/work/"; +} ////educational background paths String getEducationalBackgrounds(){ diff --git a/lib/utils/validators.dart b/lib/utils/validators.dart index 52f4c1f..fb4ab79 100644 --- a/lib/utils/validators.dart +++ b/lib/utils/validators.dart @@ -6,6 +6,10 @@ final mobileNumberValidator = FormBuilderValidators.compose([ FormBuilderValidators.required(errorText: mobileNumberRequired), FormBuilderValidators.numeric(errorText: numericValidator) ]); +final numericRequired = FormBuilderValidators.compose([ + FormBuilderValidators.required(errorText: "This field is required"), + FormBuilderValidators.numeric(errorText: numericValidator) +]); final registerPasswordValidator = FormBuilderValidators.compose([ FormBuilderValidators.required(errorText: "Password is required"), diff --git a/pubspec.lock b/pubspec.lock index 5132e7a..f3b631c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -201,6 +201,22 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.6" + dropdown_button2: + dependency: "direct main" + description: + name: dropdown_button2 + sha256: "4458d81bfd24207f3d58f66f78097064e02f810f94cf1bc80bf20fe7685ebc80" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + dropdown_plus: + dependency: "direct main" + description: + name: dropdown_plus + sha256: "707c364066dcfcd2f5b672116d5adee8e3454ede3ff6fc34f5b351bfbbfbecbe" + url: "https://pub.dev" + source: hosted + version: "0.0.9" easy_app_installer: dependency: "direct main" description: @@ -310,6 +326,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" + flutter_dropdown_search: + dependency: "direct main" + description: + name: flutter_dropdown_search + sha256: ead6f0e5dc67ae20822b211d26e617efe1d8922159de9b07a8a2168805b541d1 + url: "https://pub.dev" + source: hosted + version: "0.0.2" flutter_form_builder: dependency: "direct main" description: @@ -773,6 +797,30 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.3" + search_choices: + dependency: "direct main" + description: + name: search_choices + sha256: "4c379407ea642613669f788ec9b41cfb156f55800e8f69c727478c37db025c47" + url: "https://pub.dev" + source: hosted + version: "2.2.5" + searchfield: + dependency: "direct main" + description: + name: searchfield + sha256: deb363c95b9e64ea9ffd1a3b69926b0fe0344daedab872fc42014755a8199de9 + url: "https://pub.dev" + source: hosted + version: "0.7.5" + select2dot1: + dependency: "direct main" + description: + name: select2dot1 + sha256: bce3cef060d48b9c728924aa76f2fbcefb887fe7219e370391696902739cb2ed + url: "https://pub.dev" + source: hosted + version: "1.0.2" shared_preferences: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index acba4dd..3b0fda0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -70,6 +70,13 @@ dependencies: badges: ^3.0.2 app_popup_menu: ^1.0.0 modal_progress_hud_nsn: ^0.3.0 + search_choices: ^2.2.5 + dropdown_button2: ^2.0.0 + flutter_dropdown_search: ^0.0.2 + select2dot1: ^1.0.2 + dropdown_plus: ^0.0.9 + searchfield: ^0.7.5 + dev_dependencies: flutter_test: From f1ed33a6c06887eea47cd363c8199932ac2a58b2 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Tue, 21 Mar 2023 09:37:55 +0800 Subject: [PATCH 52/86] implemented delete,update, and edit in work history screen --- ios/Podfile.lock | 6 - .../profile/workHistory/workHistory_bloc.dart | 281 +++--- .../workHistory/workHistory_event.dart | 27 + .../workHistory/workHistory_state.dart | 31 +- lib/model/utils/agency_position.dart | 22 - .../components/eligibility/edit_modal.dart | 8 +- .../profile/components/loading_screen.dart | 6 +- .../components/work_history/add_modal.dart | 242 +++-- .../components/work_history/edit_modal.dart | 828 ++++++++++++++++++ .../components/work_history_screen.dart | 49 +- .../profile/work_history_services.dart | 93 +- lib/utils/custom_dropdown_button.dart | 292 +++--- lib/utils/urls.dart | 11 +- lib/utils/validators.dart | 16 + pubspec.lock | 40 - pubspec.yaml | 5 - 16 files changed, 1495 insertions(+), 462 deletions(-) create mode 100644 lib/screens/profile/components/work_history/edit_modal.dart diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 26d9bc2..e6a0ac0 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -22,8 +22,6 @@ PODS: - FlutterMacOS - permission_handler_apple (9.0.4): - Flutter - - search_choices (0.0.1): - - Flutter - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS @@ -42,7 +40,6 @@ DEPENDENCIES: - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - - search_choices (from `.symlinks/plugins/search_choices/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`) @@ -70,8 +67,6 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/path_provider_foundation/ios" permission_handler_apple: :path: ".symlinks/plugins/permission_handler_apple/ios" - search_choices: - :path: ".symlinks/plugins/search_choices/ios" shared_preferences_foundation: :path: ".symlinks/plugins/shared_preferences_foundation/ios" sqflite: @@ -88,7 +83,6 @@ SPEC CHECKSUMS: package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852 permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce - search_choices: b50731e8c425078048f681f39c34375c58d6ce8d shared_preferences_foundation: 297b3ebca31b34ec92be11acd7fb0ba932c822ca sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 SwiftProtobuf: b02b5075dcf60c9f5f403000b3b0c202a11b6ae1 diff --git a/lib/bloc/profile/workHistory/workHistory_bloc.dart b/lib/bloc/profile/workHistory/workHistory_bloc.dart index bf5a416..c645ad0 100644 --- a/lib/bloc/profile/workHistory/workHistory_bloc.dart +++ b/lib/bloc/profile/workHistory/workHistory_bloc.dart @@ -1,9 +1,10 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; -import 'package:select2dot1/select2dot1.dart'; +import 'package:unit2/bloc/profile/eligibility/eligibility_bloc.dart'; import 'package:unit2/model/profile/work_history.dart'; import 'package:unit2/model/utils/agency.dart'; import 'package:unit2/model/utils/agency_position.dart'; +import 'package:unit2/model/utils/position.dart'; import 'package:unit2/sevices/profile/work_history_services.dart'; import '../../../model/utils/category.dart'; @@ -13,11 +14,12 @@ part 'workHistory_state.dart'; class WorkHistoryBloc extends Bloc { List workExperiences = []; - List agencyPositions = []; + List agencyPositions = []; List agencies = []; List appointmentStatus = []; List agencyCategory = []; WorkHistoryBloc() : super(EducationInitial()) { + ////GET WORK HISTORIES on((event, emit) async { emit(WorkHistoryLoadingState()); try { @@ -29,196 +31,145 @@ class WorkHistoryBloc extends Bloc { emit(WorkHistoryErrorState(message: e.toString())); } }); - - on((event,emit){ +///// LOAD WORK HISTORIES + on((event, emit) { emit(WorkHistoryLoadingState()); workExperiences = event.workHistories; emit(WorkHistoryLoaded(workExperiences: workExperiences)); }); - on((event,emit)async{ + on((event, emit) async { emit(WorkHistoryLoadingState()); - try{ - final bool success = await WorkHistoryService.instance.delete(profileId: event.profileId,token: event.token, work: event.workHistory); - if(success){ - event.workHistories.removeWhere((WorkHistory element) => element.id == event.workHistory.id); + try { + final bool success = await WorkHistoryService.instance.delete( + profileId: event.profileId, + token: event.token, + work: event.workHistory); + if (success) { + event.workHistories.removeWhere( + (WorkHistory element) => element.id == event.workHistory.id); List newWorkHistories = event.workHistories; - emit(DeletedState(success: success,workHistories: newWorkHistories)); - }else{ - emit(DeletedState(success: success, workHistories: event.workHistories)); + emit(DeletedState(success: success, workHistories: newWorkHistories)); + } else { + emit(DeletedState( + success: success, workHistories: event.workHistories)); } - - }catch(e){ + } catch (e) { emit(WorkHistoryErrorState(message: e.toString())); } }); + //// ADD WORK HISTORIES + on((event, emit) async { + try { + emit(WorkHistoryLoadingState()); + Map status = await WorkHistoryService.instance.add( + isPrivate: event.isPrivate, + workHistory: event.workHistory, + token: event.token, + profileId: event.profileId); + if (status['success']) { + WorkHistory workHistory = WorkHistory.fromJson(status['data']); + workExperiences.add(workHistory); + emit(WorkHistoryAddedState( + response: status, workExperiences: workExperiences)); + } else { + emit(WorkHistoryAddedState( + response: status, workExperiences: workExperiences)); + } + } catch (e) { + emit(WorkHistoryErrorState(message: e.toString())); + } + }); + + +////UPDATE WORK HISTORY +on((event, emit)async{ + emit(WorkHistoryLoadingState()); + // try{ + Map status = await WorkHistoryService.instance.update(oldWorkHistory: event.oldWorkHistory, newWorkHistory: event.workHistory, token: event.token, profileId: event.profileId); + if(status['success']){ + WorkHistory workHistory = WorkHistory.fromJson(status['data']); + workExperiences.removeWhere((WorkHistory work) { + return work.id == event.oldWorkHistory.id; + }); + workExperiences.add(workHistory); + emit(WorkHistoryEditedState(workExperiences: workExperiences,response: status)); + }else{ + emit(WorkHistoryEditedState(response: status, workExperiences: workExperiences)); + } + + // }catch(e){ + // emit(WorkHistoryErrorState(message: e.toString())); + // } +}); + + +////SHOW EDIT WORK HISTORIES + on((event, emit) async { + try { + /////POSITIONS------------------------------------------ + if (agencyPositions.isEmpty) { + List positions = + await WorkHistoryService.instance.getAgencyPosition(); + agencyPositions = positions; + } + + /////AGENCIES------------------------------------------ + if (agencies.isEmpty) { + List newAgencies = + await WorkHistoryService.instance.getAgecies(); + agencies = newAgencies; + } + + /////Category Agency------------------------------------------ + if (agencyCategory.isEmpty) { + List categoryAgencies = + await WorkHistoryService.instance.agencyCategory(); + agencyCategory = categoryAgencies; + } + /////////------------------------------------- + if (appointmentStatus.isEmpty) { + List status = + WorkHistoryService.instance.getAppointmentStatusList(); + appointmentStatus = status; + } + + emit(EditWorkHistoryState( + workHistory: event.workHistory, + agencyPositions: agencyPositions, + appointmentStatus: appointmentStatus, + agencyCategory: agencyCategory, + agencies: agencies)); + } catch (e) { + emit(WorkHistoryErrorState(message: e.toString())); + } + }); + ////SHOW ADD FORM WORK HISTORIES on((event, emit) async { emit(WorkHistoryLoadingState()); try { /////POSITIONS------------------------------------------ - List positions = + List positions = await WorkHistoryService.instance.getAgencyPosition(); agencyPositions = positions; - + /////AGENCIES------------------------------------------ List newAgencies = await WorkHistoryService.instance.getAgecies(); - agencies = newAgencies; + agencies = newAgencies; - /////Category Agency------------------------------------------ + /////Category Agency------------------------------------------ List categoryAgencies = await WorkHistoryService.instance.agencyCategory(); agencyCategory = categoryAgencies; /////////------------------------------------- - List status = WorkHistoryService.instance.getAppointmentStatusList(); - appointmentStatus = status; + List status = + WorkHistoryService.instance.getAppointmentStatusList(); + appointmentStatus = status; - -List agricultureList =[]; -List businessInfoList =[]; -List constructionList =[]; -List educationList =[]; -List financeList =[]; -List foodList =[]; -List gamingList =[]; -List healthList =[]; -List motorList =[]; -List naturalList =[]; -List otherList =[]; -List personalList =[]; -List publicList =[]; -List realStateList =[]; -List safetyList =[]; -List transportList =[]; - for (Category category in agencyCategory) { - if (category.industryClass!.name == "Agriculture & Forestry/Wildlife") { - agricultureList - .add(SingleItemCategoryModel(nameSingleItem: category.name!)); - } - if (category.industryClass!.name == "Business & Information") { - businessInfoList - .add(SingleItemCategoryModel(nameSingleItem: category.name!)); - } - if (category.industryClass!.name == "Construction/Utilities/Contracting") { - constructionList - .add(SingleItemCategoryModel(nameSingleItem: category.name!)); - } - if (category.industryClass!.name == "Education") { - educationList - .add(SingleItemCategoryModel(nameSingleItem: category.name!)); - } - if (category.industryClass!.name == "Finance & Insurance") { - financeList - .add(SingleItemCategoryModel(nameSingleItem: category.name!)); - } - if (category.industryClass!.name == "Food & Hospitality") { - foodList - .add(SingleItemCategoryModel(nameSingleItem: category.name!)); - } - if (category.industryClass!.name == "Gaming") { - gamingList - .add(SingleItemCategoryModel(nameSingleItem: category.name!)); - } - if (category.industryClass!.name == "Health Services") { - healthList - .add(SingleItemCategoryModel(nameSingleItem: category.name!)); - } - if (category.industryClass!.name == "Motor Vehicle") { - motorList - .add(SingleItemCategoryModel(nameSingleItem: category.name!)); - } - if (category.industryClass!.name == "Natural Resources/Environmental") { - naturalList - .add(SingleItemCategoryModel(nameSingleItem: category.name!)); - } - if (category.industryClass!.name == "Other") { - otherList - .add(SingleItemCategoryModel(nameSingleItem: category.name!)); - } - if (category.industryClass!.name == "Personal Services") { - personalList - .add(SingleItemCategoryModel(nameSingleItem: category.name!)); - } - if (category.industryClass!.name == "Public Governance") { - publicList - .add(SingleItemCategoryModel(nameSingleItem: category.name!)); - } - if (category.industryClass!.name == "Real Estate & Housing") { - realStateList - .add(SingleItemCategoryModel(nameSingleItem: category.name!)); - } - if (category.industryClass!.name == "Safety/Security & Legal") { - safetyList - .add(SingleItemCategoryModel(nameSingleItem: category.name!)); - } - if (category.industryClass!.name == "Transportation") { - transportList - .add(SingleItemCategoryModel(nameSingleItem: category.name!)); - } - } - - SingleCategoryModel agricultureForestryWildlife = - SingleCategoryModel( - nameCategory: "Agriculture & Forestry/Wildlife", - singleItemCategoryList: agricultureList); - - SingleCategoryModel businessInformation = SingleCategoryModel( - nameCategory: "Business & Information", singleItemCategoryList: businessInfoList); - - SingleCategoryModel constructionUtilitiesContracting = - SingleCategoryModel( - nameCategory: "Construction/Utilities/Contracting", - singleItemCategoryList: constructionList); - SingleCategoryModel education = SingleCategoryModel( - nameCategory: "Education", singleItemCategoryList: educationList); - SingleCategoryModel financeInsurance = SingleCategoryModel( - nameCategory: "Finance & Insurance", singleItemCategoryList: financeList); - SingleCategoryModel foodHospitality = SingleCategoryModel( - nameCategory: "Food & Hospitality", singleItemCategoryList: foodList); - SingleCategoryModel gaming = SingleCategoryModel( - nameCategory: "Gaming", singleItemCategoryList: gamingList); - SingleCategoryModel healthServices = SingleCategoryModel( - nameCategory: "Health Services", singleItemCategoryList: healthList); - SingleCategoryModel motorVehicle = SingleCategoryModel( - nameCategory: "Motor Vehicle", singleItemCategoryList: motorList); - SingleCategoryModel naturalResourcesEnvironmental = - SingleCategoryModel( - nameCategory: "Natural Resources/Environmental", - singleItemCategoryList: naturalList); - SingleCategoryModel others = SingleCategoryModel( - nameCategory: "Others", singleItemCategoryList: otherList); - SingleCategoryModel personalServices = SingleCategoryModel( - nameCategory: "Personal Services", singleItemCategoryList: personalList); - SingleCategoryModel publicGovernance = SingleCategoryModel( - nameCategory: "Public Governance", singleItemCategoryList: publicList); - SingleCategoryModel realStateHousing = SingleCategoryModel( - nameCategory: "Real Estate & Housing", singleItemCategoryList: realStateList); - SingleCategoryModel safetySecurityLegal = SingleCategoryModel( - nameCategory: "Safety/Security & Legal", - singleItemCategoryList: safetyList); - SingleCategoryModel transportation = SingleCategoryModel( - nameCategory: "Transportation", singleItemCategoryList: transportList); - final List agencyCategoryDropdownData = [ - agricultureForestryWildlife, - businessInformation, - constructionUtilitiesContracting, - education, - financeInsurance, - foodHospitality, - gaming, - healthServices, - motorVehicle, - naturalResourcesEnvironmental, - others, - personalServices, - publicGovernance, - realStateHousing, - safetySecurityLegal, - transportation, - ]; emit(AddWorkHistoryState( - agencyPositions:agencyPositions, + agencyPositions: agencyPositions, appointmentStatus: appointmentStatus, - agencyCategory: agencyCategoryDropdownData, + agencyCategory: agencyCategory, agencies: agencies)); } catch (e) { emit(WorkHistoryErrorState(message: e.toString())); diff --git a/lib/bloc/profile/workHistory/workHistory_event.dart b/lib/bloc/profile/workHistory/workHistory_event.dart index 5624086..5a0dbe1 100644 --- a/lib/bloc/profile/workHistory/workHistory_event.dart +++ b/lib/bloc/profile/workHistory/workHistory_event.dart @@ -26,6 +26,13 @@ class LoadWorkHistories extends WorkHistorytEvent{ class ShowAddWorkHistoryForm extends WorkHistorytEvent{ +} +class ShowEditWorkHistoryForm extends WorkHistorytEvent{ + final WorkHistory workHistory; + const ShowEditWorkHistoryForm({required this.workHistory}); + @override + List get props => [workHistory]; + } class DeleteWorkHistory extends WorkHistorytEvent{ final List workHistories; @@ -37,4 +44,24 @@ class DeleteWorkHistory extends WorkHistorytEvent{ List get props => [token, profileId,workHistory, workHistories]; } +class UpdateWorkHistory extends WorkHistorytEvent{ + final WorkHistory workHistory; + final WorkHistory oldWorkHistory; + final String profileId; + final String token; + const UpdateWorkHistory({required this.oldWorkHistory, required this.profileId, required this.token, required this.workHistory}); + @override + List get props => [profileId,token,workHistory,oldWorkHistory]; +} + +class AddWorkHostory extends WorkHistorytEvent{ + final WorkHistory workHistory; + final bool isPrivate; + final int profileId; + final String token; + const AddWorkHostory({required this.workHistory, required this.isPrivate, required this.profileId, required this.token}); + @override + List get props => [workHistory,profileId,token,isPrivate]; +} + diff --git a/lib/bloc/profile/workHistory/workHistory_state.dart b/lib/bloc/profile/workHistory/workHistory_state.dart index ae4a74f..12a2037 100644 --- a/lib/bloc/profile/workHistory/workHistory_state.dart +++ b/lib/bloc/profile/workHistory/workHistory_state.dart @@ -30,9 +30,9 @@ class WorkHistoryErrorState extends WorkHistoryState{ class AddWorkHistoryState extends WorkHistoryState{ - final List agencyPositions; + final List agencyPositions; final List agencies; - final List agencyCategory; + final List agencyCategory; final List appointmentStatus; const AddWorkHistoryState({required this.agencyPositions, required this.appointmentStatus,required this.agencies,required this.agencyCategory}); @@ -41,6 +41,17 @@ class AddWorkHistoryState extends WorkHistoryState{ } +class EditWorkHistoryState extends WorkHistoryState{ + final WorkHistory workHistory; + final List agencyPositions; + final List agencies; + final List agencyCategory; + final List appointmentStatus; + const EditWorkHistoryState({required this.workHistory, required this.agencies,required this.agencyCategory, required this.agencyPositions, required this.appointmentStatus}); + @override + List get props => [workHistory, agencyPositions,appointmentStatus,agencies,agencyCategory]; +} + class DeletedState extends WorkHistoryState{ final List workHistories; final bool success; @@ -48,3 +59,19 @@ class DeletedState extends WorkHistoryState{ @override List get props => [workHistories,success]; } + +class WorkHistoryEditedState extends WorkHistoryState{ + final List workExperiences; + final Map response; + const WorkHistoryEditedState({required this.response, required this.workExperiences}); + @override + List get props => [workExperiences,response]; +} + +class WorkHistoryAddedState extends WorkHistoryState{ + final List workExperiences; + final Map response; + const WorkHistoryAddedState({required this.response, required this.workExperiences}); + @override + List get props => [workExperiences,response]; +} diff --git a/lib/model/utils/agency_position.dart b/lib/model/utils/agency_position.dart index 3e6e48d..f2d4f01 100644 --- a/lib/model/utils/agency_position.dart +++ b/lib/model/utils/agency_position.dart @@ -1,29 +1,7 @@ import 'dart:convert'; -AgencyPosition agencyPositionFromJson(String str) => AgencyPosition.fromJson(json.decode(str)); -String agencyPositionToJson(AgencyPosition data) => json.encode(data.toJson()); - -class AgencyPosition { - AgencyPosition({ - required this.id, - required this.title, - }); - - final int? id; - final String? title; - - factory AgencyPosition.fromJson(Map json) => AgencyPosition( - id: json["id"], - title: json["title"], - ); - - Map toJson() => { - "id": id, - "title": title, - }; -} // To parse this JSON data, do // // final appoinemtStatus = appoinemtStatusFromJson(jsonString); diff --git a/lib/screens/profile/components/eligibility/edit_modal.dart b/lib/screens/profile/components/eligibility/edit_modal.dart index e221f4d..69b7f64 100644 --- a/lib/screens/profile/components/eligibility/edit_modal.dart +++ b/lib/screens/profile/components/eligibility/edit_modal.dart @@ -99,7 +99,7 @@ class _EditEligibilityScreenState extends State { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ - //ELIGIBILITIES DROPDOWN + ////ELIGIBILITIES DROPDOWN DropdownButtonFormField( validator: (value) => value == null ? 'required' : null, @@ -125,7 +125,7 @@ class _EditEligibilityScreenState extends State { width: screenWidth, child: Row( children: [ - //LICENSE NUMBER + ////LICENSE NUMBER Flexible( flex: 1, child: FormBuilderTextField( @@ -142,7 +142,7 @@ class _EditEligibilityScreenState extends State { const SizedBox( width: 12, ), - //RATING + // //RATING Flexible( flex: 1, child: FormBuilderTextField( @@ -169,7 +169,7 @@ class _EditEligibilityScreenState extends State { width: screenWidth, child: Row( children: [ - //EXAM DATE + // //EXAM DATE Flexible( flex: 1, child: DateTimePicker( diff --git a/lib/screens/profile/components/loading_screen.dart b/lib/screens/profile/components/loading_screen.dart index f3078be..36952e2 100644 --- a/lib/screens/profile/components/loading_screen.dart +++ b/lib/screens/profile/components/loading_screen.dart @@ -135,8 +135,8 @@ class LoadingScreen extends StatelessWidget { ), Center( child: Container( - height: 150, - width: 150, + height: 120, + width: 120, decoration:const BoxDecoration( color: Colors.black87, borderRadius: BorderRadius.all(Radius.circular(25)) @@ -149,7 +149,7 @@ class LoadingScreen extends StatelessWidget { color: Colors.white), SizedBox(height: 10,), - Text("Loading Profile",textAlign: TextAlign.center, style: TextStyle(color: Colors.white),) + Text("Loading Profile",textAlign: TextAlign.center, style: TextStyle(color: Colors.white,fontSize: 10),) ], ), ), diff --git a/lib/screens/profile/components/work_history/add_modal.dart b/lib/screens/profile/components/work_history/add_modal.dart index 522b6ac..d8382e3 100644 --- a/lib/screens/profile/components/work_history/add_modal.dart +++ b/lib/screens/profile/components/work_history/add_modal.dart @@ -6,12 +6,14 @@ import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:fluttericon/font_awesome_icons.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:searchfield/searchfield.dart'; -import 'package:select2dot1/select2dot1.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/profile/workHistory/workHistory_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; +import 'package:unit2/model/profile/work_history.dart'; import 'package:unit2/model/utils/agency.dart'; import 'package:unit2/model/utils/agency_position.dart'; +import 'package:unit2/model/utils/category.dart'; +import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/btn-style.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/theme-data.dart/form-style.dart'; @@ -19,6 +21,8 @@ import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/utils/validators.dart'; +import '../../../../model/utils/position.dart'; + class AddWorkHistoryScreen extends StatefulWidget { const AddWorkHistoryScreen({super.key}); @@ -32,15 +36,13 @@ class _AddWorkHistoryScreenState extends State { final toDateController = TextEditingController(); final fromDateController = TextEditingController(); final _formKey = GlobalKey(); - AgencyPosition? selectedPosition; + Position? selectedPosition; Agency? selectedAgency; AppoinemtStatus? selectedStatus; + Category? selectedAgencyCategory; String? salary; String? salaryGrade; String? salaryGradeStep; - SingleItemCategoryModel selectedAgencyCategory = - const SingleItemCategoryModel(nameSingleItem: ""); - ScrollController agencyScrollController = ScrollController(); bool showAgency = false; bool showSalaryGradeAndSalaryStep = false; bool? isPrivate = false; @@ -49,6 +51,9 @@ class _AddWorkHistoryScreenState extends State { final agencyFocusNode = FocusNode(); final positionFocusNode = FocusNode(); final appointmentStatusNode = FocusNode(); + final agencyCategoryFocusNode = FocusNode(); + int? profileId; + String? token; @override void dispose() { addPositionController.dispose(); @@ -60,10 +65,11 @@ class _AddWorkHistoryScreenState extends State { @override Widget build(BuildContext context) { - print("exc"); return BlocBuilder( builder: (context, state) { if (state is UserLoggedIn) { + profileId = state.userData!.user!.login!.user!.profileId; + token = state.userData!.user!.login!.token; return BlocBuilder( builder: (context, state) { if (state is ProfileLoaded) { @@ -88,10 +94,20 @@ class _AddWorkHistoryScreenState extends State { ////POSITIONS StatefulBuilder(builder: (context, setState) { return SearchField( + itemHeight: 50, + suggestionsDecoration: box1(), suggestions: state.agencyPositions - .map((AgencyPosition position) => + .map((Position position) => SearchFieldListItem(position.title!, - item: position)) + item: position, + child: Padding( + padding: const EdgeInsets + .symmetric( + horizontal: 10), + child: ListTile( + title: + Text(position.title!), + )))) .toList(), focusNode: positionFocusNode, searchInputDecoration: @@ -99,11 +115,6 @@ class _AddWorkHistoryScreenState extends State { .copyWith( suffixIcon: const Icon( Icons.arrow_drop_down)), - initialValue: selectedPosition != null - ? SearchFieldListItem( - selectedPosition!.title!, - item: selectedPosition) - : null, onSuggestionTap: (position) { setState(() { selectedPosition = position.item; @@ -111,7 +122,7 @@ class _AddWorkHistoryScreenState extends State { }); }, emptyWidget: Container( - color: Colors.white, + decoration: box1(), height: 100, child: Column( mainAxisAlignment: @@ -122,7 +133,7 @@ class _AddWorkHistoryScreenState extends State { const SizedBox( height: 20, ), - const Text("No result found"), + const Text("No result found..."), const SizedBox( height: 10, ), @@ -134,7 +145,7 @@ class _AddWorkHistoryScreenState extends State { context) { return AlertDialog( title: const Text( - "Add Position"), + "Add Position?"), content: SizedBox( height: 130, child: Column( @@ -157,21 +168,20 @@ class _AddWorkHistoryScreenState extends State { child: ElevatedButton( style: mainBtnStyle(primary, Colors.transparent, second), onPressed: () { - AgencyPosition - newAgencyPosition = - AgencyPosition(id: null, title: addPositionController.text.toUpperCase()); - - state.agencyPositions.insert( - 0, - newAgencyPosition); - selectedPosition = - newAgencyPosition; - addPositionController.text = - ""; setState( - () {}); - Navigator.pop( - context); + () { + Position + newAgencyPosition = + Position(id: null, title: addPositionController.text.toUpperCase()); + + state.agencyPositions.insert(0, + newAgencyPosition); + selectedPosition = + newAgencyPosition; + addPositionController.text = + ""; + Navigator.pop(context); + }); }, child: const Text("Add"))), ], @@ -284,7 +294,7 @@ class _AddWorkHistoryScreenState extends State { return null; }, emptyWidget: Container( - color: Colors.white, + decoration: box1(), height: 100, child: Column( mainAxisAlignment: @@ -357,37 +367,70 @@ class _AddWorkHistoryScreenState extends State { ]), ), ), - ////AGENCY CATEGORY - + + SizedBox( + height: showAgency ? 12 : 0, + ), + ////SHOW CATEGORY AGENCY SizedBox( child: showAgency - ? Select2dot1( - selectEmptyInfoSettings: - const SelectEmptyInfoSettings( - text: - "Select Agency Category"), - - scrollController: - agencyScrollController, - selectDataController: - SelectDataController( - isMultiSelect: false, - data: state - .agencyCategory, - initSelected: [ - selectedAgencyCategory - ]), - onChanged: (value) { - print("sdasdsa"); - // print(value[0].nameSingleItem); - // setState(() { - // selectedAgencyCategory = - // value[0]; - // print(value[0].nameSingleItem); - // agencyFocusNode.unfocus(); - // }); + ? SearchField( + focusNode: + agencyCategoryFocusNode, + itemHeight: 70, + suggestions: state + .agencyCategory + .map((Category category) => + SearchFieldListItem( + category.name!, + item: category, + child: ListTile( + title: Text( + category + .name!), + subtitle: Text( + category + .industryClass! + .name!), + ))) + .toList(), + emptyWidget: Container( + height: 100, + decoration: box1(), + child: const Center( + child: Text( + "No result found ...")), + ), + onSuggestionTap: + (agencyCategory) { + setState(() { + selectedAgencyCategory = + agencyCategory.item; + agencyCategoryFocusNode + .unfocus(); + selectedAgency = Agency( + id: null, + name: selectedAgency! + .name, + category: + selectedAgencyCategory, + privateEntity: null); + }); }, - ) + searchInputDecoration: + normalTextFieldStyle( + "Category *", "") + .copyWith( + suffixIcon: + const Icon(Icons + .arrow_drop_down)), + validator: (value) { + if (value!.isEmpty) { + return "This field is required"; + } + return null; + }, + ) : const SizedBox(), ), @@ -427,7 +470,16 @@ class _AddWorkHistoryScreenState extends State { showSalaryGradeAndSalaryStep = true; } + selectedAgency = Agency( + id: null, + name: selectedAgency! + .name, + category: + selectedAgencyCategory, + privateEntity: value == "YES"?true:false); agencyFocusNode.unfocus(); + agencyCategoryFocusNode + .unfocus(); }); }, @@ -466,6 +518,22 @@ class _AddWorkHistoryScreenState extends State { salaryGrade = value; }, + validator: + FormBuilderValidators + .compose([ + FormBuilderValidators + .integer( + radix: 10, + errorText: + "Please enter a number"), + FormBuilderValidators + .numeric( + errorText: + "Please enter a number") + ]), + autovalidateMode: + AutovalidateMode + .onUserInteraction, ), ), const SizedBox( @@ -483,6 +551,22 @@ class _AddWorkHistoryScreenState extends State { normalTextFieldStyle( "SG Step (SG)", "0"), + validator: + FormBuilderValidators + .compose([ + FormBuilderValidators + .integer( + radix: 10, + errorText: + "Please enter a number"), + FormBuilderValidators + .numeric( + errorText: + "Please enter a number") + ]), + autovalidateMode: + AutovalidateMode + .onUserInteraction, onChanged: (value) { setState(() { salaryGradeStep = @@ -601,7 +685,6 @@ class _AddWorkHistoryScreenState extends State { ) : DateTimePicker( validator: (val) { - print(val); return null; }, controller: @@ -646,11 +729,44 @@ class _AddWorkHistoryScreenState extends State { print(salary); print(fromDateController.text); print(toDateController.text); - print(isPrivate); + print(salaryGrade); print(salaryGradeStep); - if (_formKey.currentState! - .validate()) {} + print(isPrivate); + if (_formKey.currentState!.validate()) { + WorkHistory workHistory = WorkHistory( + position: selectedPosition, + id: null, + agency: selectedAgency, + fromDate: fromDateController + .text.isEmpty + ? null + : DateTime.parse( + fromDateController.text), + toDate: toDateController.text.isEmpty || + toDateController.text + .toUpperCase() == + "PRESENT" + ? null + : DateTime.parse( + toDateController.text), + salaryGrade: salaryGrade == null + ? null + : int.parse(salaryGrade!), + sgStep: salaryGradeStep == null + ? null + : int.parse(salaryGradeStep!), + monthlySalary: + double.parse(salary!), + appointmentStatus: + selectedStatus!.value); + context.read().add( + AddWorkHostory( + workHistory: workHistory, + profileId: profileId!, + token: token!, + isPrivate: isPrivate!)); + } }, child: const Text(submit)), ), diff --git a/lib/screens/profile/components/work_history/edit_modal.dart b/lib/screens/profile/components/work_history/edit_modal.dart new file mode 100644 index 0000000..975bfcd --- /dev/null +++ b/lib/screens/profile/components/work_history/edit_modal.dart @@ -0,0 +1,828 @@ +import 'package:date_time_picker/date_time_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:searchfield/searchfield.dart'; +import '../../../../bloc/profile/profile_bloc.dart'; +import '../../../../bloc/profile/workHistory/workHistory_bloc.dart'; +import '../../../../bloc/user/user_bloc.dart'; +import '../../../../model/profile/work_history.dart'; +import '../../../../model/utils/agency.dart'; +import '../../../../model/utils/agency_position.dart'; +import '../../../../model/utils/category.dart'; +import '../../../../model/utils/position.dart'; +import '../../../../theme-data.dart/box_shadow.dart'; +import '../../../../theme-data.dart/btn-style.dart'; +import '../../../../theme-data.dart/colors.dart'; +import '../../../../theme-data.dart/form-style.dart'; +import '../../../../utils/global.dart'; +import '../../../../utils/text_container.dart'; +import '../../../../utils/validators.dart'; + +class EditWorkHistoryScreen extends StatefulWidget { + const EditWorkHistoryScreen({super.key}); + + @override + State createState() => _EditWorkHistoryScreenState(); +} + +class _EditWorkHistoryScreenState extends State { + final addAgencyController = TextEditingController(); + final addPositionController = TextEditingController(); + final toDateController = TextEditingController(); + final fromDateController = TextEditingController(); + final oldPositionController = TextEditingController(); + final oldAppointmentStatusController = TextEditingController(); + final oldAgencyController = TextEditingController(); + final _formKey = GlobalKey(); + Position? selectedPosition; + Agency? selectedAgency; + AppoinemtStatus? selectedStatus; + Category? selectedAgencyCategory; + String? salary; + String? salaryGrade; + String? salaryGradeStep; + //show agency category is a variable to show adding of agency category if you add agency manually + bool showAgencyCategory = false; + //showSalaryGadeAndSalaryStep is a variable that will show salary + //and salary step if selected agency is government + bool showSalaryGradeAndSalaryStep = false; + //isPrivate is the value of the isPrivate radion button + bool? isPrivate = false; + //showIsPrivateRadion is a variable that will show isPrivate radio if you + //add agency manually + bool showIsPrivateRadio = false; + bool currentlyEmployed = false; + final agencyFocusNode = FocusNode(); + final positionFocusNode = FocusNode(); + final appointmentStatusNode = FocusNode(); + final agencyCategoryFocusNode = FocusNode(); + int? profileId; + String? token; + @override + void dispose() { + addPositionController.dispose(); + addAgencyController.dispose(); + toDateController.dispose(); + fromDateController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is UserLoggedIn) { + profileId = state.userData!.user!.login!.user!.profileId; + token = state.userData!.user!.login!.token; + return BlocBuilder( + builder: (context, state) { + if (state is ProfileLoaded) { + return BlocConsumer( + listener: (context, state) { + if (state is AddWorkHistoryState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + }, builder: (context, state) { + if (state is EditWorkHistoryState) { + oldPositionController.text = + state.workHistory.position!.title!; + oldAppointmentStatusController.text = + state.workHistory.appointmentStatus!; + oldAgencyController.text = state.workHistory.agency!.name!; + currentlyEmployed = + state.workHistory.toDate == null ? true : false; + showSalaryGradeAndSalaryStep = + !state.workHistory.agency!.privateEntity!; + fromDateController.text = + state.workHistory.fromDate.toString(); + toDateController.text = state.workHistory.toDate.toString(); + currentlyEmployed = + state.workHistory.toDate == null ? true : false; + + return SingleChildScrollView( + child: SizedBox( + height: blockSizeVertical * 90, + child: FormBuilder( + key: _formKey, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, horizontal: 18), + child: Column( + children: [ + ////POSITIONS + StatefulBuilder(builder: (context, setState) { + return SearchField( + controller: oldPositionController, + itemHeight: 50, + suggestionsDecoration: box1(), + suggestions: state.agencyPositions + .map((Position position) => + SearchFieldListItem(position.title!, + item: position, + child: Padding( + padding: const EdgeInsets + .symmetric( + horizontal: 10), + child: ListTile( + title: + Text(position.title!), + )))) + .toList(), + focusNode: positionFocusNode, + searchInputDecoration: + normalTextFieldStyle("Position *", "") + .copyWith( + suffixIcon: const Icon( + Icons.arrow_drop_down)), + onSuggestionTap: (position) { + setState(() { + selectedPosition = position.item; + positionFocusNode.unfocus(); + }); + }, + emptyWidget: Container( + decoration: box1(), + height: 100, + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.center, + children: [ + const SizedBox( + height: 20, + ), + const Text("No result found..."), + const SizedBox( + height: 10, + ), + TextButton( + onPressed: () { + showDialog( + context: context, + builder: (BuildContext + context) { + return AlertDialog( + title: const Text( + "Add Position?"), + content: SizedBox( + height: 130, + child: Column( + children: [ + TextFormField( + controller: + addPositionController, + decoration: + normalTextFieldStyle( + "", + ""), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: double + .infinity, + height: 50, + child: ElevatedButton( + style: mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + setState( + () { + Position + newAgencyPosition = + Position(id: null, title: addPositionController.text.toUpperCase()); + state.agencyPositions.insert(0, + newAgencyPosition); + selectedPosition = + newAgencyPosition; + addPositionController.text = + ""; + Navigator.pop(context); + }); + }, + child: const Text("Add"))), + ], + ), + ), + ); + }); + }, + child: + const Text("Add position")) + ]), + ), + validator: (position) { + if (position!.isEmpty) { + return "This field is required"; + } + return null; + }, + ); + }), + const SizedBox( + height: 12, + ), + ////APPOINTMENT STATUS' + SearchField( + controller: oldAppointmentStatusController, + suggestions: state.appointmentStatus + .map((AppoinemtStatus status) => + SearchFieldListItem(status.label, + item: status)) + .toList(), + focusNode: appointmentStatusNode, + validator: (value) { + if (value!.isEmpty) { + return "This field is required"; + } + return null; + }, + onSuggestionTap: (status) { + selectedStatus = status.item; + appointmentStatusNode.unfocus(); + }, + searchInputDecoration: normalTextFieldStyle( + "Appointment Status", "") + .copyWith( + suffixIcon: const Icon( + Icons.arrow_drop_down)), + ), + + const SizedBox( + height: 12, + ), + + ////AGENCY + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + SearchField( + controller: oldAgencyController, + itemHeight: 70, + focusNode: agencyFocusNode, + suggestions: state.agencies + .map((Agency agency) => + SearchFieldListItem( + agency.name!, + item: agency, + child: ListTile( + title: Text( + agency.name!, + overflow: TextOverflow + .ellipsis, + ), + subtitle: Text( + agency.privateEntity == + true + ? "Private" + : "Government"), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle("Agency *", "") + .copyWith( + suffixIcon: const Icon( + Icons.arrow_drop_down)), + onSuggestionTap: (agency) { + setState(() { + selectedAgency = agency.item; + if (selectedAgency!.privateEntity == + null) { + showIsPrivateRadio = true; + } else { + showIsPrivateRadio = false; + } + if (selectedAgency!.privateEntity == + true) { + showSalaryGradeAndSalaryStep = + false; + } + if (selectedAgency!.privateEntity == + false) { + showSalaryGradeAndSalaryStep = + true; + } + agencyFocusNode.unfocus(); + }); + }, + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + emptyWidget: Container( + decoration: box1(), + height: 100, + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.center, + children: [ + const SizedBox( + height: 20, + ), + const Text("No result found"), + const SizedBox( + height: 10, + ), + TextButton( + onPressed: () { + showDialog( + context: context, + builder: (BuildContext + context) { + return AlertDialog( + title: const Text( + "Add Position"), + content: SizedBox( + height: 130, + child: Column( + children: [ + TextFormField( + controller: + addAgencyController, + decoration: + normalTextFieldStyle( + "", + ""), + ), + const SizedBox( + height: + 12, + ), + SizedBox( + width: double + .infinity, + height: + 50, + child: ElevatedButton( + style: mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + setState(() { + Agency newAgency = Agency(id: null, name: addAgencyController.text.toUpperCase(), category: null, privateEntity: null); + state.agencies.insert(0, newAgency); + selectedAgency = newAgency; + addAgencyController.text = ""; + showAgencyCategory = true; + + showIsPrivateRadio = true; + + Navigator.pop(context); + }); + }, + child: const Text("Add"))), + ], + ), + ), + ); + }); + }, + child: const Text( + "Add Agency")) + ]), + ), + ), + + SizedBox( + height: showAgencyCategory ? 12 : 0, + ), + ////SHOW AGENCY CATEGORY + SizedBox( + child: showAgencyCategory + ? SearchField( + focusNode: + agencyCategoryFocusNode, + itemHeight: 70, + suggestions: state + .agencyCategory + .map((Category category) => + SearchFieldListItem( + category.name!, + item: category, + child: ListTile( + title: Text( + category + .name!), + subtitle: Text( + category + .industryClass! + .name!), + ))) + .toList(), + emptyWidget: Container( + height: 100, + decoration: box1(), + child: const Center( + child: Text( + "No result found ...")), + ), + onSuggestionTap: + (agencyCategory) { + setState(() { + selectedAgencyCategory = + agencyCategory.item; + agencyCategoryFocusNode + .unfocus(); + selectedAgency = Agency( + id: null, + name: selectedAgency! + .name, + category: + selectedAgencyCategory, + privateEntity: null); + }); + }, + searchInputDecoration: + normalTextFieldStyle( + "Category *", "") + .copyWith( + suffixIcon: + const Icon(Icons + .arrow_drop_down)), + validator: (value) { + if (value!.isEmpty) { + return "This field is required"; + } + return null; + }, + ) + : const SizedBox(), + ), + + ////PRVIATE SECTOR + SizedBox( + child: showIsPrivateRadio + ? FormBuilderRadioGroup( + decoration: InputDecoration( + border: InputBorder.none, + label: Row( + children: [ + Text( + "Is this private sector? ", + style: Theme.of( + context) + .textTheme + .headlineSmall! + .copyWith( + fontSize: 24), + ), + const Icon(FontAwesome + .help_circled) + ], + ), + ), + + ////onvhange private sector + onChanged: (value) { + setState(() { + if (value.toString() == + "YES") { + isPrivate = true; + showSalaryGradeAndSalaryStep = + false; + } else { + isPrivate = false; + showSalaryGradeAndSalaryStep = + true; + } + selectedAgency = Agency( + id: null, + name: selectedAgency! + .name, + category: + selectedAgencyCategory, + privateEntity: + value == "YES" + ? true + : false); + agencyFocusNode.unfocus(); + agencyCategoryFocusNode + .unfocus(); + }); + }, + + name: 'isPrivate', + validator: + FormBuilderValidators + .required(), + options: ["YES", "NO"] + .map((lang) => + FormBuilderFieldOption( + value: lang)) + .toList(growable: false), + ) + : const SizedBox()), + SizedBox( + height: showSalaryGradeAndSalaryStep + ? 12 + : 0, + ), + ////SALARY GRADE AND SALARY GRADE STEP + SizedBox( + child: showSalaryGradeAndSalaryStep + ? Column( + children: [ + Row( + children: [ + ////SALARY GRADE + Flexible( + flex: 1, + child: + FormBuilderTextField( + initialValue: state + .workHistory + .salaryGrade + ?.toString(), + name: + 'salary_grade', + keyboardType: + TextInputType + .number, + decoration: + normalTextFieldStyle( + "Salary Grade (SG)", + "0"), + validator: + integerAndNumeric, + autovalidateMode: + AutovalidateMode + .onUserInteraction, + ), + ), + const SizedBox( + width: 12, + ), + //// SALARY STEP + Flexible( + flex: 1, + child: + FormBuilderTextField( + initialValue: state + .workHistory + .sgStep + ?.toString(), + name: 'salary_step', + keyboardType: + TextInputType + .number, + decoration: + normalTextFieldStyle( + "SG Step (SG)", + "0"), + validator: + integerAndNumeric, + autovalidateMode: + AutovalidateMode + .onUserInteraction, + ), + ) + ], + ) + ], + ) + : null), + ], + ); + }), + const SizedBox( + height: 12, + ), + ////MONTHLY SALARY + FormBuilderTextField( + initialValue: state.workHistory.monthlySalary + .toString(), + onChanged: (value) { + setState(() { + salary = value; + }); + }, + validator: numericRequired, + name: "salary", + decoration: normalTextFieldStyle( + "Monthly Salary *", "") + .copyWith(prefix: const Text("₱ ")), + ), + + const SizedBox( + height: 12, + ), + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + ////CURRENTLY EMPLOYED + FormBuilderSwitch( + initialValue: currentlyEmployed, + activeColor: second, + onChanged: (value) { + setState(() { + if (value == true) { + currentlyEmployed = true; + toDateController.text = "PRESENT"; + } else { + currentlyEmployed = false; + toDateController.text = ""; + } + }); + }, + decoration: + normalTextFieldStyle("", ''), + name: 'overseas', + title: + const Text("Currently Employed?"), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + //// FROM DATE + Flexible( + flex: 1, + child: DateTimePicker( + validator: (value) { + if (value == null) { + return "This field is required"; + } + return null; + }, + use24HourFormat: false, + icon: const Icon( + Icons.date_range), + controller: + fromDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + timeHintText: + "Date of Examination/Conferment", + decoration: + normalTextFieldStyle( + "From *", + "From *") + .copyWith( + prefixIcon: + const Icon( + Icons.date_range, + color: Colors.black87, + )), + initialValue: null, + )), + const SizedBox( + width: 12, + ), + //// TO DATE + Flexible( + flex: 1, + child: currentlyEmployed + ? TextFormField( + enabled: false, + initialValue: "PRESENT", + style: const TextStyle( + color: + Colors.black45), + decoration: + normalTextFieldStyle( + "", "") + .copyWith( + prefixIcon: + const Icon( + Icons.date_range, + color: Colors.black45, + )), + ) + : DateTimePicker( + validator: (val) { + return null; + }, + controller: + toDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + decoration: normalTextFieldStyle( + "To *", "To *") + .copyWith( + prefixIcon: + const Icon( + Icons + .date_range, + color: Colors + .black87, + ), + prefixText: + currentlyEmployed + ? "PRESENT" + : ""), + initialValue: null, + ), + ), + ], + ), + ), + ], + ); + }), + const Expanded(child: SizedBox()), + ////SUBMIT BUTTON + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + if (_formKey.currentState! + .saveAndValidate()) { + salary = _formKey + .currentState!.value['salary']; + selectedPosition ??= + state.workHistory.position; + salaryGrade = _formKey.currentState! + .value['salary_grade']; + salaryGradeStep = _formKey + .currentState! + .value['salary_step']; + selectedAgency ??= + state.workHistory.agency; + + selectedStatus ??= AppoinemtStatus( + value: state.workHistory + .appointmentStatus!, + label: state.workHistory + .appointmentStatus!); + WorkHistory newWorkHistory = + WorkHistory( + id: state.workHistory.id, + position: selectedPosition, + agency: selectedAgency, + fromDate: fromDateController + .text.isEmpty + ? null + : DateTime.parse( + fromDateController.text), + toDate: toDateController + .text.isEmpty || + toDateController.text + .toUpperCase() == + "PRESENT" || + toDateController.text + .toLowerCase() == + 'null' + ? null + : DateTime.parse( + toDateController.text), + monthlySalary: + double.parse(salary!), + appointmentStatus: + selectedStatus!.value, + salaryGrade: salaryGrade == null + ? null + : int.parse(salaryGrade!), + sgStep: salaryGradeStep == null + ? null + : int.parse(salaryGradeStep!), + ); + context.read().add( + UpdateWorkHistory( + oldWorkHistory: + state.workHistory, + profileId: + profileId.toString(), + token: token!, + workHistory: newWorkHistory)); + } + }, + child: const Text(submit)), + ), + const SizedBox( + height: 20, + ), + ], + ), + ), + ), + ), + ); + } + return Container(); + }); + } + return Container(); + }, + ); + } + return const Center( + child: Text("Add Work History"), + ); + }, + ); + } +} diff --git a/lib/screens/profile/components/work_history_screen.dart b/lib/screens/profile/components/work_history_screen.dart index 580d33a..c725291 100644 --- a/lib/screens/profile/components/work_history_screen.dart +++ b/lib/screens/profile/components/work_history_screen.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:app_popup_menu/app_popup_menu.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -8,6 +10,7 @@ import 'package:intl/intl.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/screens/profile/components/work_history/add_modal.dart'; +import 'package:unit2/screens/profile/components/work_history/edit_modal.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; @@ -16,6 +19,7 @@ import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; import '../../../bloc/profile/workHistory/workHistory_bloc.dart'; +import '../../../model/profile/work_history.dart'; import '../../../utils/alerts.dart'; import '../../../utils/global.dart'; @@ -64,7 +68,7 @@ class WorkHistoryScreen extends StatelessWidget { } if (state is WorkHistoryLoaded || state is WorkHistoryErrorState || - state is AddWorkHistoryState) { + state is AddWorkHistoryState ||state is WorkHistoryAddedState || state is EditWorkHistoryState) { final progress = ProgressHUD.of(context); progress!.dismiss(); } @@ -88,6 +92,44 @@ class WorkHistoryScreen extends StatelessWidget { }); } } + ////ADDED STATE + if(state is WorkHistoryAddedState){ + if (state.response['success']) { + successAlert(context, "Adding Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context.read().add(LoadWorkHistories( + workHistories: state.workExperiences)); + }); + } else { + errorAlert(context, "Adding Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context.read().add(LoadWorkHistories( + workHistories: state.workExperiences)); + }); + } + } + //// EDITED STATE + if(state is WorkHistoryEditedState){ + if (state.response['success']) { + successAlert(context, "Update Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context.read().add(LoadWorkHistories( + workHistories: state.workExperiences)); + }); + } else { + errorAlert(context, "Update Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context.read().add(LoadWorkHistories( + workHistories: state.workExperiences)); + }); + } + } }, builder: (context, state) { if (state is WorkHistoryLoaded) { @@ -188,6 +230,8 @@ class WorkHistoryScreen extends StatelessWidget { } if (value == 1) { ////edit eligibilty-= = = = = = = = =>> + WorkHistory workHistory = state.workExperiences[index]; + context.read().add(ShowEditWorkHistoryForm(workHistory: workHistory)); } }, menuItems: [ @@ -227,6 +271,9 @@ class WorkHistoryScreen extends StatelessWidget { if (state is AddWorkHistoryState) { return const AddWorkHistoryScreen(); } + if(state is EditWorkHistoryState){ + return const EditWorkHistoryScreen(); + } if (state is WorkHistoryErrorState) { return SomethingWentWrong( message: state.message, onpressed: () {}); diff --git a/lib/sevices/profile/work_history_services.dart b/lib/sevices/profile/work_history_services.dart index cb93409..bb43770 100644 --- a/lib/sevices/profile/work_history_services.dart +++ b/lib/sevices/profile/work_history_services.dart @@ -6,6 +6,7 @@ import 'package:http/http.dart' as http; import 'package:unit2/model/utils/agency.dart'; import 'package:unit2/model/utils/agency_position.dart'; import 'package:unit2/model/utils/category.dart'; +import 'package:unit2/model/utils/position.dart'; import 'package:unit2/utils/request.dart'; import '../../utils/urls.dart'; @@ -14,6 +15,7 @@ class WorkHistoryService { static final WorkHistoryService _instance = WorkHistoryService(); static WorkHistoryService get instance => _instance; +//get all workhistories Future> getWorkExperiences( int profileId, String token) async { List workExperiences = []; @@ -40,6 +42,7 @@ class WorkHistoryService { return workExperiences; } +//get agencies Future> getAgecies() async { List agencies = []; String path = Url.instance.getAgencies(); @@ -64,6 +67,7 @@ class WorkHistoryService { return agencies; } +//delete workhistory Future delete( {required int profileId, required String token, @@ -103,6 +107,7 @@ class WorkHistoryService { return success!; } +//get agency category Future> agencyCategory() async { List agencyCategory = []; String path = Url.instance.getAgencyCategory(); @@ -127,8 +132,89 @@ class WorkHistoryService { return agencyCategory; } - Future> getAgencyPosition() async { - List agencyPositions = []; + //edit work history + Future> update({required WorkHistory oldWorkHistory, required WorkHistory newWorkHistory, required String token, required String profileId})async{ + Map? statusResponse={}; + String authtoken = "Token $token"; + String path = '${Url.instance.updateWorkHistories()}$profileId/'; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authtoken + }; + Map body = { + "id":newWorkHistory.id, + "position_id":newWorkHistory.position!.id, + "agency_id":newWorkHistory.agency!.id, + "from_date":newWorkHistory.fromDate?.toString(), + "to_date":newWorkHistory.toDate?.toString(), + "monthly_salary":newWorkHistory.monthlySalary, + "appointment_status":newWorkHistory.appointmentStatus, + "salary_grade":newWorkHistory.salaryGrade, + "sg_step":newWorkHistory.sgStep, + "_positionName":newWorkHistory.position!.title!, + "_agencyName":newWorkHistory.agency!.name!, + "_agencyCatId":newWorkHistory.agency!.category!.id!, + "_privateEntity":newWorkHistory.agency!.privateEntity, + "oldPosId":oldWorkHistory.position!.id, + "_oldAgencyId":oldWorkHistory.agency!.id, + "oldFromDate":oldWorkHistory.fromDate?.toString(), + }; + // try{ + http.Response response = await Request.instance.putRequest(path: path, headers: headers, body: body, param: {}); + if(response.statusCode == 200 ){ + Map data = jsonDecode(response.body); + statusResponse = data; + }else{ + statusResponse.addAll({'success':false}); + } + return statusResponse; + // }catch(e){ + // throw e.toString(); + // } + } + + //Add work history + Future>add({required WorkHistory workHistory, required String token, required int profileId , required bool isPrivate})async{ + String authtoken = "Token $token"; + String path = '${Url.instance.addWorkHistory()}$profileId/'; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authtoken + }; + Map statusResponse = {}; + Map body = { + 'position_id':workHistory.position?.id, + 'agency_id': workHistory.agency?.id, + 'from_date': workHistory.fromDate?.toString(), + 'to_date': workHistory.toDate?.toString(), + 'monthly_salary': workHistory.monthlySalary, + 'appointment_status': workHistory.appointmentStatus, + 'salary_grade': workHistory.salaryGrade, + 'sg_step':workHistory.sgStep, + '_positionName':workHistory.position?.title, + '_agencyName':workHistory.agency?.name, + '_agencyCatId':workHistory.agency?.category?.id, + '_privateEntity':workHistory.agency?.privateEntity, + }; + try{ + http.Response response = await Request.instance.postRequest(path: path,param: {},body: body,headers: headers); + if(response.statusCode == 201){ + Map data = jsonDecode(response.body); + statusResponse = data; + + }else{ + statusResponse.addAll({'success':false}); + } + return statusResponse; + }catch(e){ + throw e.toString(); + } + + } + +//get agency position + Future> getAgencyPosition() async { + List agencyPositions = []; String path = Url.instance.getPositions(); Map headers = { 'Content-Type': 'application/json; charset=UTF-8', @@ -140,7 +226,7 @@ class WorkHistoryService { Map data = jsonDecode(response.body); if (data['data'] != null) { data['data'].forEach((var agencyPosition) { - AgencyPosition position = AgencyPosition.fromJson(agencyPosition); + Position position = Position.fromJson(agencyPosition); agencyPositions.add(position); }); } @@ -151,6 +237,7 @@ class WorkHistoryService { return agencyPositions; } +//get appointment status List getAppointmentStatusList() { return [ AppoinemtStatus(value: "Appointed", label: "Appointed"), diff --git a/lib/utils/custom_dropdown_button.dart b/lib/utils/custom_dropdown_button.dart index 11bf81d..308c72b 100644 --- a/lib/utils/custom_dropdown_button.dart +++ b/lib/utils/custom_dropdown_button.dart @@ -1,149 +1,149 @@ -import 'package:dropdown_button2/dropdown_button2.dart'; -import 'package:flutter/material.dart'; +// import 'package:dropdown_button2/dropdown_button2.dart'; +// import 'package:flutter/material.dart'; -class CustomDropdownButton2 extends StatelessWidget { - final String hint; - final String? value; - final List dropdownItems; - final ValueChanged? onChanged; - final DropdownButtonBuilder? selectedItemBuilder; - final Alignment? hintAlignment; - final Alignment? valueAlignment; - final double? buttonHeight, buttonWidth; - final EdgeInsetsGeometry? buttonPadding; - final BoxDecoration? buttonDecoration; - final int? buttonElevation; - final Widget? icon; - final double? iconSize; - final Color? iconEnabledColor; - final Color? iconDisabledColor; - final double? itemHeight; - final EdgeInsetsGeometry? itemPadding; - final double? dropdownHeight, dropdownWidth; - final EdgeInsetsGeometry? dropdownPadding; - final BoxDecoration? dropdownDecoration; - final int? dropdownElevation; - final Radius? scrollbarRadius; - final double? scrollbarThickness; - final bool? scrollbarAlwaysShow; - final Offset offset; +// class CustomDropdownButton2 extends StatelessWidget { +// final String hint; +// final String? value; +// final List dropdownItems; +// final ValueChanged? onChanged; +// final DropdownButtonBuilder? selectedItemBuilder; +// final Alignment? hintAlignment; +// final Alignment? valueAlignment; +// final double? buttonHeight, buttonWidth; +// final EdgeInsetsGeometry? buttonPadding; +// final BoxDecoration? buttonDecoration; +// final int? buttonElevation; +// final Widget? icon; +// final double? iconSize; +// final Color? iconEnabledColor; +// final Color? iconDisabledColor; +// final double? itemHeight; +// final EdgeInsetsGeometry? itemPadding; +// final double? dropdownHeight, dropdownWidth; +// final EdgeInsetsGeometry? dropdownPadding; +// final BoxDecoration? dropdownDecoration; +// final int? dropdownElevation; +// final Radius? scrollbarRadius; +// final double? scrollbarThickness; +// final bool? scrollbarAlwaysShow; +// final Offset offset; - const CustomDropdownButton2({ - required this.hint, - required this.value, - required this.dropdownItems, - required this.onChanged, - this.selectedItemBuilder, - this.hintAlignment, - this.valueAlignment, - this.buttonHeight, - this.buttonWidth, - this.buttonPadding, - this.buttonDecoration, - this.buttonElevation, - this.icon, - this.iconSize, - this.iconEnabledColor, - this.iconDisabledColor, - this.itemHeight, - this.itemPadding, - this.dropdownHeight, - this.dropdownWidth, - this.dropdownPadding, - this.dropdownDecoration, - this.dropdownElevation, - this.scrollbarRadius, - this.scrollbarThickness, - this.scrollbarAlwaysShow, - this.offset = const Offset(0, 0), - Key? key, - }) : super(key: key); +// const CustomDropdownButton2({ +// required this.hint, +// required this.value, +// required this.dropdownItems, +// required this.onChanged, +// this.selectedItemBuilder, +// this.hintAlignment, +// this.valueAlignment, +// this.buttonHeight, +// this.buttonWidth, +// this.buttonPadding, +// this.buttonDecoration, +// this.buttonElevation, +// this.icon, +// this.iconSize, +// this.iconEnabledColor, +// this.iconDisabledColor, +// this.itemHeight, +// this.itemPadding, +// this.dropdownHeight, +// this.dropdownWidth, +// this.dropdownPadding, +// this.dropdownDecoration, +// this.dropdownElevation, +// this.scrollbarRadius, +// this.scrollbarThickness, +// this.scrollbarAlwaysShow, +// this.offset = const Offset(0, 0), +// Key? key, +// }) : super(key: key); - @override - Widget build(BuildContext context) { - return DropdownButtonHideUnderline( - child: DropdownButton2( - //To avoid long text overflowing. - isExpanded: true, - hint: Container( - alignment: hintAlignment, - child: Text( - hint, - overflow: TextOverflow.ellipsis, - maxLines: 1, - style: TextStyle( - fontSize: 14, - color: Theme.of(context).hintColor, - ), - ), - ), - value: value, - items: dropdownItems - .map((item) => DropdownMenuItem( - value: item, - child: Container( - alignment: valueAlignment, - child: Text( - item, - overflow: TextOverflow.ellipsis, - maxLines: 1, - style: const TextStyle( - fontSize: 14, - ), - ), - ), - )) - .toList(), - onChanged: onChanged, - selectedItemBuilder: selectedItemBuilder, - buttonStyleData: ButtonStyleData( - height: buttonHeight ?? 40, - width: buttonWidth ?? 140, - padding: buttonPadding ?? const EdgeInsets.only(left: 14, right: 14), - decoration: buttonDecoration ?? - BoxDecoration( - borderRadius: BorderRadius.circular(14), - border: Border.all( - color: Colors.black45, - ), - ), - elevation: buttonElevation, - ), - iconStyleData: IconStyleData( - icon: icon ?? const Icon(Icons.arrow_forward_ios_outlined), - iconSize: iconSize ?? 12, - iconEnabledColor: iconEnabledColor, - iconDisabledColor: iconDisabledColor, - ), - dropdownStyleData: DropdownStyleData( - //Max height for the dropdown menu & becoming scrollable if there are more items. If you pass Null it will take max height possible for the items. - maxHeight: dropdownHeight ?? 200, - width: dropdownWidth ?? 140, - padding: dropdownPadding, - decoration: dropdownDecoration ?? - BoxDecoration( - borderRadius: BorderRadius.circular(14), - ), - elevation: dropdownElevation ?? 8, - //Null or Offset(0, 0) will open just under the button. You can edit as you want. - offset: offset, - //Default is false to show menu below button - isOverButton: false, - scrollbarTheme: ScrollbarThemeData( - radius: scrollbarRadius ?? const Radius.circular(40), - thickness: scrollbarThickness != null - ? MaterialStateProperty.all(scrollbarThickness!) - : null, - thumbVisibility: scrollbarAlwaysShow != null - ? MaterialStateProperty.all(scrollbarAlwaysShow!) - : null, - ), - ), - menuItemStyleData: MenuItemStyleData( - height: itemHeight ?? 40, - padding: itemPadding ?? const EdgeInsets.only(left: 14, right: 14), - ), - ), - ); - } -} \ No newline at end of file +// @override +// Widget build(BuildContext context) { +// return DropdownButtonHideUnderline( +// child: DropdownButton2( +// //To avoid long text overflowing. +// isExpanded: true, +// hint: Container( +// alignment: hintAlignment, +// child: Text( +// hint, +// overflow: TextOverflow.ellipsis, +// maxLines: 1, +// style: TextStyle( +// fontSize: 14, +// color: Theme.of(context).hintColor, +// ), +// ), +// ), +// value: value, +// items: dropdownItems +// .map((item) => DropdownMenuItem( +// value: item, +// child: Container( +// alignment: valueAlignment, +// child: Text( +// item, +// overflow: TextOverflow.ellipsis, +// maxLines: 1, +// style: const TextStyle( +// fontSize: 14, +// ), +// ), +// ), +// )) +// .toList(), +// onChanged: onChanged, +// selectedItemBuilder: selectedItemBuilder, +// buttonStyleData: ButtonStyleData( +// height: buttonHeight ?? 40, +// width: buttonWidth ?? 140, +// padding: buttonPadding ?? const EdgeInsets.only(left: 14, right: 14), +// decoration: buttonDecoration ?? +// BoxDecoration( +// borderRadius: BorderRadius.circular(14), +// border: Border.all( +// color: Colors.black45, +// ), +// ), +// elevation: buttonElevation, +// ), +// iconStyleData: IconStyleData( +// icon: icon ?? const Icon(Icons.arrow_forward_ios_outlined), +// iconSize: iconSize ?? 12, +// iconEnabledColor: iconEnabledColor, +// iconDisabledColor: iconDisabledColor, +// ), +// dropdownStyleData: DropdownStyleData( +// //Max height for the dropdown menu & becoming scrollable if there are more items. If you pass Null it will take max height possible for the items. +// maxHeight: dropdownHeight ?? 200, +// width: dropdownWidth ?? 140, +// padding: dropdownPadding, +// decoration: dropdownDecoration ?? +// BoxDecoration( +// borderRadius: BorderRadius.circular(14), +// ), +// elevation: dropdownElevation ?? 8, +// //Null or Offset(0, 0) will open just under the button. You can edit as you want. +// offset: offset, +// //Default is false to show menu below button +// isOverButton: false, +// scrollbarTheme: ScrollbarThemeData( +// radius: scrollbarRadius ?? const Radius.circular(40), +// thickness: scrollbarThickness != null +// ? MaterialStateProperty.all(scrollbarThickness!) +// : null, +// thumbVisibility: scrollbarAlwaysShow != null +// ? MaterialStateProperty.all(scrollbarAlwaysShow!) +// : null, +// ), +// ), +// menuItemStyleData: MenuItemStyleData( +// height: itemHeight ?? 40, +// padding: itemPadding ?? const EdgeInsets.only(left: 14, right: 14), +// ), +// ), +// ); +// } +// } \ No newline at end of file diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index fb0fd90..bff5d0e 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -3,10 +3,10 @@ class Url { static Url get instance => _instance; String host() { - // // // return '192.168.10.221:3003'; + return '192.168.10.221:3003'; // return 'agusandelnorte.gov.ph'; // return "192.168.10.219:3000"; - return "devweb.agusandelnorte.gov.ph"; + // return "devweb.agusandelnorte.gov.ph"; // return 'devapi.agusandelnorte.gov.ph:3004'; } @@ -51,6 +51,13 @@ String getPositions(){ String getAgencies(){ return "/api/jobnet_app/agencies/"; } +String updateWorkHistories(){ + return "/api/jobnet_app/profile/pds/work/"; +} + +String addWorkHistory(){ + return "/api/jobnet_app/profile/pds/work/"; +} String getAgencyCategory(){ return "api/jobnet_app/agency_categories/"; } diff --git a/lib/utils/validators.dart b/lib/utils/validators.dart index fb4ab79..b2c05e6 100644 --- a/lib/utils/validators.dart +++ b/lib/utils/validators.dart @@ -16,3 +16,19 @@ final registerPasswordValidator = FormBuilderValidators.compose([ FormBuilderValidators.minLength(6, errorText: "Password must be equal or greater than 6 characters"), ]); + +final integerAndNumeric = + FormBuilderValidators + .compose([ + FormBuilderValidators + .integer( + radix: 10, + errorText: + "Please enter a number"), + FormBuilderValidators + .numeric( + errorText: + "Please enter a number") + ]); + + diff --git a/pubspec.lock b/pubspec.lock index f3b631c..2a90114 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -201,22 +201,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.6" - dropdown_button2: - dependency: "direct main" - description: - name: dropdown_button2 - sha256: "4458d81bfd24207f3d58f66f78097064e02f810f94cf1bc80bf20fe7685ebc80" - url: "https://pub.dev" - source: hosted - version: "2.0.0" - dropdown_plus: - dependency: "direct main" - description: - name: dropdown_plus - sha256: "707c364066dcfcd2f5b672116d5adee8e3454ede3ff6fc34f5b351bfbbfbecbe" - url: "https://pub.dev" - source: hosted - version: "0.0.9" easy_app_installer: dependency: "direct main" description: @@ -326,14 +310,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" - flutter_dropdown_search: - dependency: "direct main" - description: - name: flutter_dropdown_search - sha256: ead6f0e5dc67ae20822b211d26e617efe1d8922159de9b07a8a2168805b541d1 - url: "https://pub.dev" - source: hosted - version: "0.0.2" flutter_form_builder: dependency: "direct main" description: @@ -797,14 +773,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.3" - search_choices: - dependency: "direct main" - description: - name: search_choices - sha256: "4c379407ea642613669f788ec9b41cfb156f55800e8f69c727478c37db025c47" - url: "https://pub.dev" - source: hosted - version: "2.2.5" searchfield: dependency: "direct main" description: @@ -813,14 +781,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.5" - select2dot1: - dependency: "direct main" - description: - name: select2dot1 - sha256: bce3cef060d48b9c728924aa76f2fbcefb887fe7219e370391696902739cb2ed - url: "https://pub.dev" - source: hosted - version: "1.0.2" shared_preferences: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 3b0fda0..c2bbe76 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -70,11 +70,6 @@ dependencies: badges: ^3.0.2 app_popup_menu: ^1.0.0 modal_progress_hud_nsn: ^0.3.0 - search_choices: ^2.2.5 - dropdown_button2: ^2.0.0 - flutter_dropdown_search: ^0.0.2 - select2dot1: ^1.0.2 - dropdown_plus: ^0.0.9 searchfield: ^0.7.5 From 54aa42ec21d6de89910f842ebafeed8e3a58061f Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Tue, 21 Mar 2023 12:57:38 +0800 Subject: [PATCH 53/86] refactor login status alert and message --- lib/bloc/user/user_bloc.dart | 36 +++++++++++++-------- lib/bloc/user/user_state.dart | 4 ++- lib/screens/unit2/login/login.dart | 28 +++++++++------- lib/sevices/login_service/auth_service.dart | 14 +++++--- lib/utils/alerts.dart | 12 +++++++ lib/utils/urls.dart | 4 +-- 6 files changed, 66 insertions(+), 32 deletions(-) diff --git a/lib/bloc/user/user_bloc.dart b/lib/bloc/user/user_bloc.dart index 104b7dd..1042352 100644 --- a/lib/bloc/user/user_bloc.dart +++ b/lib/bloc/user/user_bloc.dart @@ -29,28 +29,38 @@ class UserBloc extends Bloc { _versionInfo = versionInfo; String apkVersion = await getAppVersion(); _apkVersion = apkVersion; - emit(VersionLoaded(versionInfo: _versionInfo,apkVersion: _apkVersion)); + emit(VersionLoaded(versionInfo: _versionInfo, apkVersion: _apkVersion)); } catch (e) { emit(UserError( message: e.toString(), )); } }); +//Loading the current version of the app on((event, emit) { - emit(VersionLoaded(versionInfo: _versionInfo,apkVersion: _apkVersion)); + emit(VersionLoaded(versionInfo: _versionInfo, apkVersion: _apkVersion)); }); + on((event, emit) async { try { - UserData? userData = await AuthService.instance + Map response = await AuthService.instance .webLogin(username: event.username, password: event.password); - _userData = userData; - emit(UserLoggedIn(userData: _userData)); - } on TimeoutException catch (_) { + if (response['status'] == true) { + UserData userData = UserData.fromJson(response['data']); + emit(UserLoggedIn( + userData: userData, + success: true, + message: response['message'])); + }else{ + emit(UserLoggedIn( + userData: null, + success: false, + message: response['message'])); + } + } on TimeoutException catch (_) { + emit(InternetTimeout(message: timeoutError)); + } on SocketException catch (_) { emit(InternetTimeout(message: timeoutError)); - }on SocketException catch (_){ - emit(InternetTimeout(message:timeoutError)); - }on Error catch(_){ - emit(InvalidCredentials(message: "Invalid username or password")); } }); on((event, emit) async { @@ -61,9 +71,9 @@ class UserBloc extends Bloc { emit(UserLoggedIn(userData: _userData)); } on TimeoutException catch (_) { emit(InternetTimeout(message: timeoutError)); - }on SocketException catch (_){ - emit(InternetTimeout(message:timeoutError)); - }on Error catch(_){ + } on SocketException catch (_) { + emit(InternetTimeout(message: timeoutError)); + } on Error catch (_) { emit(InvalidCredentials(message: "Invalid username or password")); } }); diff --git a/lib/bloc/user/user_state.dart b/lib/bloc/user/user_state.dart index 372f052..26d815f 100644 --- a/lib/bloc/user/user_state.dart +++ b/lib/bloc/user/user_state.dart @@ -32,7 +32,9 @@ class UserError extends UserState { } class UserLoggedIn extends UserState{ final UserData? userData; - UserLoggedIn({this.userData}); + final String? message; + final bool? success; + UserLoggedIn({this.userData,this.message,this.success}); } class VersionLoaded extends UserState { diff --git a/lib/screens/unit2/login/login.dart b/lib/screens/unit2/login/login.dart index d59d3d7..b5dc6b5 100644 --- a/lib/screens/unit2/login/login.dart +++ b/lib/screens/unit2/login/login.dart @@ -5,6 +5,7 @@ import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; +import 'package:fluttertoast/fluttertoast.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:unit2/screens/unit2/login/components/update_required.dart'; @@ -42,19 +43,23 @@ class _UniT2LoginState extends State { backgroundColor: Colors.black87, indicatorWidget: const SpinKitFadingCircle(color: Colors.white), child: BlocConsumer(listener: (context, state) { - if (state is UserLoggedIn || - state is UuidLoaded || - state is UserError || - state is InternetTimeout) { + if (state is UserLoggedIn || state is UuidLoaded) { final progress = ProgressHUD.of(context); progress!.dismiss(); - Navigator.pushReplacementNamed(context, '/module-screen'); + } - if (state is InvalidCredentials) { - final progress = ProgressHUD.of(context); - progress!.dismiss(); - errorAlert(context, "Error Login", state.message, () {}); - context.read().add(LoadVersion()); + if (state is UserLoggedIn) { + if (state.success == true) { + Fluttertoast.showToast(msg: state.message!,toastLength: Toast.LENGTH_LONG,gravity: ToastGravity.CENTER); + Navigator.pushReplacementNamed(context, '/module-screen'); + } else { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + errorAlert(context, "Error Login", state.message, () { + context.read().add(LoadVersion()); + Navigator.of(context).pop(); + }); + } } }, builder: (context, state) { if (state is VersionLoaded) { @@ -223,7 +228,8 @@ class _UniT2LoginState extends State { BlocProvider.of(context) .add(UserLogin( - username: "rjvincentlopeplopez", + username: + "rjvincentlopeplopez", password: "shesthequ33n", // username: _formKey // .currentState! diff --git a/lib/sevices/login_service/auth_service.dart b/lib/sevices/login_service/auth_service.dart index 477abe8..497e8f4 100644 --- a/lib/sevices/login_service/auth_service.dart +++ b/lib/sevices/login_service/auth_service.dart @@ -33,23 +33,27 @@ class AuthService { return versionInfo; } - Future webLogin({String? username, String? password})async{ + Future> webLogin({String? username, String? password})async{ Map body ={'username':username!,'password':password!}; Map baseHeaders = { 'Content-Type': 'application/json' }; + Map responseStatus = {}; String path = Url.instance.authentication(); UserData? userData; try{ http.Response response = await Request.instance.postRequest(path: path,param: {},headers: baseHeaders,body: body); - if(response.statusCode == 200){ + Map data = jsonDecode(response.body); - userData = UserData.fromJson(data['data']); - } + + responseStatus = data; + + + return responseStatus; }catch(e){ throw (e.toString()); } - return userData!; + } Future qrLogin({String? uuid, String? password})async{ Map body ={'uuid':uuid!,'password':password!}; diff --git a/lib/utils/alerts.dart b/lib/utils/alerts.dart index 3517fb7..a3e6f4e 100644 --- a/lib/utils/alerts.dart +++ b/lib/utils/alerts.dart @@ -61,3 +61,15 @@ successAlert(context, title, description,Function() func) { btnOk: SizedBox(height: 50,child: ElevatedButton(style: mainBtnStyle(success2, Colors.transparent, success), onPressed: func, child: const Text("OK")), ) ).show(); } +okAlert(context,title,description){ + AwesomeDialog( + width: blockSizeHorizontal * 90, + context: context, + dialogType: DialogType.error, + animType: AnimType.scale, + headerAnimationLoop: false, + title: title, + desc: description, + btnOkOnPress: () {}, + ).show(); +} diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index bff5d0e..e04f450 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -3,8 +3,8 @@ class Url { static Url get instance => _instance; String host() { - return '192.168.10.221:3003'; - // return 'agusandelnorte.gov.ph'; + // return '192.168.10.221:3003'; + return 'agusandelnorte.gov.ph'; // return "192.168.10.219:3000"; // return "devweb.agusandelnorte.gov.ph"; // return 'devapi.agusandelnorte.gov.ph:3004'; From 12bc51335d2e9bd538fdd3d4cb48f3fb80047872 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Fri, 24 Mar 2023 14:46:17 +0800 Subject: [PATCH 54/86] implemented crud operation API for personal references screen --- .../profile/references/references_bloc.dart | 194 +++++- .../profile/references/references_event.dart | 53 ++ .../profile/references/references_state.dart | 82 ++- .../components/eligibility/add_modal.dart | 40 +- .../components/eligibility_screen.dart | 6 +- .../components/reference/add_modal.dart | 548 +++++++++++++++ .../components/reference/edit_modal.dart | 637 ++++++++++++++++++ .../profile/components/references_screen.dart | 344 +++++++--- lib/sevices/profile/references_services.dart | 100 ++- .../profile/work_history_services.dart | 8 +- lib/utils/location_utilities.dart | 174 +++-- lib/utils/urls.dart | 23 +- 12 files changed, 2005 insertions(+), 204 deletions(-) create mode 100644 lib/screens/profile/components/reference/add_modal.dart create mode 100644 lib/screens/profile/components/reference/edit_modal.dart diff --git a/lib/bloc/profile/references/references_bloc.dart b/lib/bloc/profile/references/references_bloc.dart index 9041de3..78f089b 100644 --- a/lib/bloc/profile/references/references_bloc.dart +++ b/lib/bloc/profile/references/references_bloc.dart @@ -1,25 +1,209 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:unit2/model/location/address_category.dart'; +import 'package:unit2/model/location/barangay.dart'; +import 'package:unit2/model/location/city.dart'; +import 'package:unit2/model/location/country.dart'; +import 'package:unit2/model/location/region.dart'; import 'package:unit2/sevices/profile/references_services.dart'; +import '../../../model/location/provinces.dart'; import '../../../model/profile/references.dart'; +import '../../../utils/location_utilities.dart'; part 'references_event.dart'; part 'references_state.dart'; - class ReferencesBloc extends Bloc { List references = []; + List globalCountries = []; + List globalRegions = []; + List globalCategories = []; + ReferencesBloc() : super(ReferencesInitial()) { - on((event, emit) async{ + on((event, emit) async { emit(ReferencesLoadingState()); - try{ - List refs = await ReferencesServices.instace.getRefences(event.profileId, event.token); + try { + if(references.isEmpty){ + List refs = await ReferencesServices.instace + .getRefences(event.profileId, event.token); references = refs; emit(ReferencesLoadedState(references: references)); - }catch(e){ + }else{ + emit(ReferencesLoadedState(references: references)); + } + } catch (e) { ReferencesErrorState(message: e.toString()); } +////SHOW FORM FOR ADDING REFERENCES + }); + on((event, emit) async { + emit(ReferencesLoadingState()); + try { + if (globalRegions.isEmpty) { + List regions = await LocationUtils.instance.getRegions(); + globalRegions = regions; + } + if (globalCountries.isEmpty) { + List countries = await LocationUtils.instance.getCountries(); + globalCountries = countries; + } + if (globalCategories.isEmpty) { + List categories = + await LocationUtils.instance.getAddressCategory(); + globalCategories = categories; + } + emit(AddReferenceState( + countries: globalCountries, + regions: globalRegions, + categories: globalCategories)); + } catch (e) { + emit(ReferencesErrorState(message: e.toString())); + } + ////SHOW EDIT FORM + }); + on((event, emit) async { + Region? selectedRegion; + Province? selectedProvince; + CityMunicipality? selectedCity; + AddressCategory? selectedCategory; + Country selectedCountry; + Barangay? selectedBarangay; + try { + if (globalRegions.isEmpty) { + List regions = await LocationUtils.instance.getRegions(); + globalRegions = regions; + } + if (globalCountries.isEmpty) { + List countries = await LocationUtils.instance.getCountries(); + globalCountries = countries; + } + if (globalCategories.isEmpty) { + List categories = + await LocationUtils.instance.getAddressCategory(); + globalCategories = categories; + } +//// checck if address is overseas + bool overseas = + event.personalReference.address!.country!.id! != 175 ? true : false; + selectedCategory = globalCategories.firstWhere((AddressCategory element) => event.personalReference.address!.addressCategory!.id == element.id); + ////if address is not overseas set initial values for address + if (!overseas) { + selectedRegion = globalRegions.firstWhere((Region element) => + event.personalReference.address!.cityMunicipality!.province! + .region!.code == + element.code); + List provinces = await LocationUtils.instance + .getProvinces(regionCode: selectedRegion.code.toString()); + selectedProvince = provinces.firstWhere((Province province) => + event.personalReference.address!.cityMunicipality!.province! + .code == + province.code); + List cities = await LocationUtils.instance + .getCities(code: selectedProvince.code!); + selectedCity = cities.firstWhere((CityMunicipality city) => + event.personalReference.address!.cityMunicipality!.code == + city.code); + List barangays = await LocationUtils.instance.getBarangay(code: selectedCity.code.toString()); + selectedBarangay = barangays.firstWhere((Barangay barangay)=>event.personalReference.address!.barangay!.code == barangay.code); + emit(EditReferenceState( + selectedRegion: selectedRegion, + ref: event.personalReference, + countries: globalCountries, + regions: globalRegions, + barangays: barangays, + categories: globalCategories, + isOverseas: overseas, + provinces: provinces, + selectedProvince: selectedProvince, + cities: cities, + selectedCity: selectedCity, + selectedCategory: selectedCategory, + selectedBarangay: selectedBarangay + )); + } else { + //// if address is overseas set initial value for country + selectedCountry = globalCountries.firstWhere((Country element) => event.personalReference.address!.country!.id == element.id); + emit(EditReferenceState( + selectedCountry: selectedCountry, + selectedCategory: selectedCategory, + selectedRegion: null, + ref: event.personalReference, + countries: globalCountries, + regions: globalRegions, + categories: globalCategories, + isOverseas: overseas)); + } + } catch (e) { + emit(ReferencesErrorState(message: e.toString())); + } + }); + //// CALL THE ERROR STATE EVEN T + on((event, emit) async { + emit(const ReferencesErrorState( + message: "Something went wrong. Please try again")); + //// ADD REFERENCES EVENT + });on((event,emit)async{ + emit(ReferencesLoadingState()); + Map status =await ReferencesServices.instace.update(ref: event.reference, token: event.token, profileId: event.profileId); + if (status['success']) { + PersonalReference ref = PersonalReference.fromJson(status['data']); + references.removeWhere((PersonalReference element)=>element.id == event.reference.id); + references.add(ref); + emit( + ReferenceEditedState(personalRefs: references, response: status)); + } else { + emit( + ReferenceEditedState(personalRefs: references, response: status)); + } + + }); + +//// add reference event + on((event, emit) async { + try { + emit(ReferencesLoadingState()); + Map status = await ReferencesServices.instace + .addReference( + ref: event.reference, + token: event.token, + profileId: event.profileId); + if (status['success']) { + PersonalReference ref = PersonalReference.fromJson(status['data']); + references.add(ref); + emit( + ReferencesAddedState(personalRefs: references, response: status)); + } else { + emit( + ReferencesAddedState(personalRefs: references, response: status)); + } + } catch (e) { + emit(ReferencesErrorState(message: e.toString())); + } + }); + ////LOAD REFERENCE + on((event, emit) { + emit(ReferencesLoadingState()); + references = event.references; + emit(ReferencesLoadedState(references: references)); + + }); + ////DELETE REFERENCE + on((event, emit) async { + try { + final bool success = await ReferencesServices.instace.delete( + profileId: event.profileId, token: event.token, id: event.refId); + if (success) { + event.references.removeWhere( + (PersonalReference element) => element.id == event.refId); + references = event.references; + emit(DeleteReferenceState(references: references, success: success)); + } else { + emit(DeleteReferenceState(references: references, success: success)); + } + } catch (e) { + emit(ReferencesErrorState(message: e.toString())); + } }); } } diff --git a/lib/bloc/profile/references/references_event.dart b/lib/bloc/profile/references/references_event.dart index 4b6737a..584bebb 100644 --- a/lib/bloc/profile/references/references_event.dart +++ b/lib/bloc/profile/references/references_event.dart @@ -14,3 +14,56 @@ class GetReferences extends ReferencesEvent{ @override List get props => [profileId,token]; } + +class ShowAddReferenceForm extends ReferencesEvent{ + +} + +class ShowEditReferenceForm extends ReferencesEvent{ + final PersonalReference personalReference; + const ShowEditReferenceForm({required this.personalReference}); + @override + List get props => [personalReference]; + +} +class CallErrorState extends ReferencesEvent{ + +} + + +class AddReference extends ReferencesEvent{ + final PersonalReference reference; + final String token; + final int profileId; + const AddReference({required this.profileId, required this.reference,required this.token}); + @override + List get props => [profileId,token,reference]; +} +class EditReference extends ReferencesEvent{ + final PersonalReference reference; + final String token; + final int profileId; + const EditReference({required this.profileId, required this.reference,required this.token}); + @override + List get props => [profileId,token,reference]; +} + +class DeleteReference extends ReferencesEvent{ + final Listreferences; + final int profileId; + final String token; + final int refId; + const DeleteReference({required this.profileId, required this.refId, required this.references, required this.token}); + @override + List get props => [profileId,token,refId,references]; +} + +class LoadReferences extends ReferencesEvent{ + final List references; + const LoadReferences({required this.references}); + @override + List get props => [references]; +} + + + diff --git a/lib/bloc/profile/references/references_state.dart b/lib/bloc/profile/references/references_state.dart index 13c2284..207a3fe 100644 --- a/lib/bloc/profile/references/references_state.dart +++ b/lib/bloc/profile/references/references_state.dart @@ -2,28 +2,94 @@ part of 'references_bloc.dart'; abstract class ReferencesState extends Equatable { const ReferencesState(); - + @override List get props => []; } class ReferencesInitial extends ReferencesState {} -class ReferencesLoadedState extends ReferencesState{ +class ReferencesLoadedState extends ReferencesState { final List references; const ReferencesLoadedState({required this.references}); - + @override List get props => [references]; - } -class ReferencesErrorState extends ReferencesState{ + +class ReferencesErrorState extends ReferencesState { final String message; const ReferencesErrorState({required this.message}); - + @override List get props => [message]; } -class ReferencesLoadingState extends ReferencesState{ - + +class ReferencesLoadingState extends ReferencesState {} + +class ReferencesAddedState extends ReferencesState { + final List personalRefs; + final Map response; + const ReferencesAddedState( + {required this.personalRefs, required this.response}); + @override + List get props => [personalRefs, response]; +} +class ReferenceEditedState extends ReferencesState { + final List personalRefs; + final Map response; + const ReferenceEditedState( + {required this.personalRefs, required this.response}); + @override + List get props => [personalRefs, response]; +} + +class EditReferenceState extends ReferencesState { + final List regions; + final List countries; + final List categories; + final List? provinces; + final List? cities; + final List? barangays; + final PersonalReference ref; + final bool isOverseas; + final Region? selectedRegion; + final Province? selectedProvince; + final CityMunicipality? selectedCity; + final AddressCategory selectedCategory; + final Barangay? selectedBarangay; + final Country? selectedCountry; + const EditReferenceState( + { this.barangays, + this.selectedBarangay, + required this.selectedCategory, + this.cities, + this.selectedCity, + this.provinces, + this.selectedProvince, + this.selectedRegion, + this.selectedCountry, + required this.isOverseas, + required this.ref, + required this.categories, + required this.countries, + required this.regions}); + @override + List get props => [regions, countries, categories, isOverseas]; +} + +class AddReferenceState extends ReferencesState { + final List regions; + final List countries; + final List categories; + const AddReferenceState( + {required this.categories, + required this.countries, + required this.regions}); +} + +class DeleteReferenceState extends ReferencesState { + final List references; + final bool success; + const DeleteReferenceState({required this.references, required this.success}); } diff --git a/lib/screens/profile/components/eligibility/add_modal.dart b/lib/screens/profile/components/eligibility/add_modal.dart index 7e7e9fc..561c76b 100644 --- a/lib/screens/profile/components/eligibility/add_modal.dart +++ b/lib/screens/profile/components/eligibility/add_modal.dart @@ -32,7 +32,6 @@ class AddEligibilityScreen extends StatefulWidget { class _AddEligibilityScreenState extends State { final formKey = GlobalKey(); - final provinceKey = GlobalKey(); bool? overseas; DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); Region? selectedRegion; @@ -52,12 +51,12 @@ class _AddEligibilityScreenState extends State { String? license; @override Widget build(BuildContext context) { - //USERBLOC + ////USERBLOC return BlocBuilder( builder: (context, state) { - //LOGGED IN USER STATE + ////LOGGED IN USER STATE if (state is UserLoggedIn) { - //PROFIILE BLOC + ////PROFIILE BLOC token = state.userData!.user!.login!.token; profileId = state.userData!.user!.login!.user!.profileId.toString(); return BlocBuilder( @@ -69,7 +68,7 @@ class _AddEligibilityScreenState extends State { return false; }, builder: (context, state) { - //EDIT ELIGIBILITY STATE + ////ADD ELIGIBILITY STATE if (state is AddEligibilityState) { return ProgressHUD( child: Center( @@ -82,7 +81,7 @@ class _AddEligibilityScreenState extends State { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ - //ELIGIBILITIES DROPDOWN + ////ELIGIBILITIES DROPDOWN FormBuilderDropdown( onChanged: (Eligibility? eligibility) { selectedEligibility = eligibility; @@ -109,7 +108,7 @@ class _AddEligibilityScreenState extends State { width: screenWidth, child: Row( children: [ - //LICENSE NUMBER + ////LICENSE NUMBER Flexible( flex: 1, child: FormBuilderTextField( @@ -125,7 +124,7 @@ class _AddEligibilityScreenState extends State { const SizedBox( width: 12, ), - //RATING + ////RATING Flexible( flex: 1, child: FormBuilderTextField( @@ -149,7 +148,7 @@ class _AddEligibilityScreenState extends State { width: screenWidth, child: Row( children: [ - //EXAM DATE + ////EXAM DATE Flexible( flex: 1, child: DateTimePicker( @@ -175,7 +174,7 @@ class _AddEligibilityScreenState extends State { const SizedBox( width: 12, ), - //VALIDITY DATE + ////VALIDITY DATE Flexible( flex: 1, child: DateTimePicker( @@ -211,7 +210,7 @@ class _AddEligibilityScreenState extends State { const SizedBox( height: 12, ), - //OVERSEAS ADDRESS SWITCH + ////OVERSEAS ADDRESS SWITCH Column( children: [ FormBuilderSwitch( @@ -231,15 +230,12 @@ class _AddEligibilityScreenState extends State { const SizedBox( height: 20, ), - //COUNTRY DROPDOWN + ////COUNTRY DROPDOWN SizedBox( child: overseas == true ? FormBuilderDropdown( initialValue: null, - validator: (value) => - value == null - ? 'required' - : null, + validator: FormBuilderValidators.required(errorText: "This field is required"), items: state.countries.map< DropdownMenuItem< Country>>( @@ -264,16 +260,14 @@ class _AddEligibilityScreenState extends State { ) : Column( children: [ - //REGION DROPDOWN + ////REGION DROPDOWN FormBuilderDropdown< Region?>( autovalidateMode: AutovalidateMode .onUserInteraction, - validator: (value) => - value == null - ? 'required' - : null, + validator: FormBuilderValidators.required(errorText: "This field is required"), + //// region onchange onChanged: (Region? region) async { setState(() { @@ -304,7 +298,7 @@ class _AddEligibilityScreenState extends State { const SizedBox( height: 20, ), - //PROVINCE DROPDOWN + ////PROVINCE DROPDOWN SizedBox( height: 70, child: ModalProgressHUD( @@ -358,7 +352,7 @@ class _AddEligibilityScreenState extends State { ), ), - // CityMunicipalities dropdown + //// CityMunicipalities dropdown SizedBox( height: 70, child: ModalProgressHUD( diff --git a/lib/screens/profile/components/eligibility_screen.dart b/lib/screens/profile/components/eligibility_screen.dart index fec9dc2..96e43d5 100644 --- a/lib/screens/profile/components/eligibility_screen.dart +++ b/lib/screens/profile/components/eligibility_screen.dart @@ -86,7 +86,7 @@ class EligibiltyScreen extends StatelessWidget { final progress = ProgressHUD.of(context); progress!.dismiss(); } - //DELETED STATE + ////DELETED STATE if (state is DeletedState) { if (state.success) { successAlert(context, "Deletion Successfull", @@ -105,7 +105,7 @@ class EligibiltyScreen extends StatelessWidget { }); } } - //ADDED STATE + ////ADDED STATE if (state is EligibilityAddedState) { if (state.response['success']) { successAlert(context, "Adding Successfull!", @@ -124,7 +124,7 @@ class EligibiltyScreen extends StatelessWidget { }); } } - //UPDATED STATE + ////UPDATED STATE if (state is EligibilityEditedState) { if (state.response['success']) { successAlert(context, "Update Successfull!", diff --git a/lib/screens/profile/components/reference/add_modal.dart b/lib/screens/profile/components/reference/add_modal.dart new file mode 100644 index 0000000..0060bbc --- /dev/null +++ b/lib/screens/profile/components/reference/add_modal.dart @@ -0,0 +1,548 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; +import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/profile/references/references_bloc.dart'; +import 'package:unit2/bloc/user/user_bloc.dart'; +import 'package:unit2/model/location/address_category.dart'; +import 'package:unit2/model/location/barangay.dart'; +import 'package:unit2/model/profile/references.dart'; +import 'package:unit2/theme-data.dart/btn-style.dart'; +import 'package:unit2/theme-data.dart/form-style.dart'; +import '../../../../model/location/city.dart'; +import '../../../../model/location/country.dart'; +import '../../../../model/location/provinces.dart'; +import '../../../../model/location/region.dart'; +import '../../../../theme-data.dart/colors.dart'; +import '../../../../utils/location_utilities.dart'; +import '../../../../utils/text_container.dart'; + +class AddReferenceScreen extends StatefulWidget { + const AddReferenceScreen({super.key}); + + @override + State createState() => _AddReferenceScreenState(); +} + +class _AddReferenceScreenState extends State { + final formKey = GlobalKey(); + String? token; + String? profileId; + bool provinceCall = false; + bool cityCall = false; + bool barangayCall = false; + bool overseas = false; + List? provinces; + List? citymuns; + List? barangays; + List category = ['Permanent', "Residential", "Birthplace"]; + ////seletected + Region? selectedRegion; + Province? selectedProvince; + CityMunicipality? selectedMunicipality; + Barangay? selectedBarangay; + Country? selectedCountry; + AddressCategory? selectedCategory; + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is UserLoggedIn) { + token = state.userData!.user!.login!.token; + profileId = state.userData!.user!.login!.user!.profileId.toString(); + return BlocBuilder( + builder: (context, state) { + if (state is ProfileLoaded) { + return BlocBuilder( + builder: (context, state) { + if (state is AddReferenceState) { + return ProgressHUD( + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, horizontal: 18), + child: FormBuilder( + key: formKey, + child: Column( + children: [ + const SizedBox(height: 25), + Row( + children: [ + ////LAST NAME + Flexible( + flex: 1, + child: FormBuilderTextField( + decoration: normalTextFieldStyle( + "Last name *", "Last name *"), + name: "lastname", + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + ), + ), + const SizedBox( + width: 5, + ), + ////FIRST NAME + Flexible( + flex: 1, + child: FormBuilderTextField( + decoration: normalTextFieldStyle( + "First name *", "First name *"), + name: "firstname", + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + ), + ), + ], + ), + const SizedBox( + height: 8, + ), + Row( + children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + decoration: normalTextFieldStyle( + "Middle name *", "Midlle name *"), + name: "middlename", + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + ), + ), + const SizedBox( + width: 5, + ), + ////CATEGORY + Flexible( + flex: 1, + child: FormBuilderDropdown< + AddressCategory>( + name: 'category', + validator: + FormBuilderValidators.required( + errorText: ""), + decoration: normalTextFieldStyle( + "Category", "Category"), + items: state.categories.map< + DropdownMenuItem< + AddressCategory>>( + (AddressCategory cat) { + return DropdownMenuItem< + AddressCategory>( + value: cat, + child: Text(cat.name!), + ); + }).toList(), + onChanged: (value) { + setState(() { + selectedCategory = value; + }); + }, + ), + ), + ], + ), + const SizedBox( + height: 8, + ), + + ////OVERSEAS ADDRESS + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Overseas Address?"), + ), + SizedBox( + height: overseas == true ? 8 : 0, + ), + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + FormBuilderDropdown( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + onChanged: + (Region? region) async { + setState(() { + provinceCall = true; + }); + selectedRegion = region; + getProvinces(); + }, + initialValue: null, + decoration: + normalTextFieldStyle( + "Region*", "Region"), + name: 'region', + items: state.regions.map< + DropdownMenuItem< + Region>>( + (Region region) { + return DropdownMenuItem< + Region>( + value: region, + child: Text( + region.description!)); + }).toList(), + ), + const SizedBox( + height: 8, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + value: selectedProvince, + onChanged: + (Province? province) { + setState(() { + cityCall = true; + }); + selectedProvince = + province; + getCities(); + }, + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province + province) { + return DropdownMenuItem( + value: + province, + child: + FittedBox( + child: Text( + province + .description!), + )); + }).toList(), + decoration: + normalTextFieldStyle( + "Province*", + "Province")), + ), + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: + DropdownButtonFormField< + CityMunicipality>( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + isExpanded: true, + onChanged: + (CityMunicipality? + city) { + setState(() { + barangayCall = true; + }); + selectedMunicipality = + city; + getBarangays(); + }, + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality + c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), + ), + ), + ), + //// BARANGAY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: barangayCall, + child: + DropdownButtonFormField< + Barangay>( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + isExpanded: true, + onChanged: + (Barangay? baragay) { + selectedBarangay = + baragay; + }, + decoration: + normalTextFieldStyle( + "Barangay*", + "Barangay"), + value: selectedBarangay, + items: barangays == null + ? [] + : barangays!.map< + DropdownMenuItem< + Barangay>>( + (Barangay + barangay) { + return DropdownMenuItem( + value: barangay, + child: Text(barangay + .description!)); + }).toList(), + ), + ), + ), + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( + height: 60, + child: FormBuilderDropdown( + initialValue: null, + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + items: state.countries.map< + DropdownMenuItem< + Country>>( + (Country country) { + return DropdownMenuItem< + Country>( + value: country, + child: FittedBox( + child: Text( + country.name!))); + }).toList(), + name: 'country', + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) { + selectedCountry = value; + }, + ), + ), + ), + + FormBuilderTextField( + name: "mobile", + decoration: normalTextFieldStyle( + "Tel./Mobile *", "Tel./Mobile"), + validator: FormBuilderValidators.required( + errorText: "This field is required"), + ), + const Expanded( + child: SizedBox(), + ), + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + child: const Text(submit), + onPressed: () { + + PersonalReference? personalReference; + if (formKey.currentState! + .saveAndValidate()) { + String lastname = formKey + .currentState!.value['lastname']; + String firstname = formKey + .currentState!.value['firstname']; + String middlename = formKey + .currentState! + .value['middlename']; + String mobile = formKey + .currentState!.value['mobile']; + + + Region? region = selectedRegion; + Province? province = Province( + code: selectedProvince?.code, + description: + selectedProvince?.description, + region: region, + psgcCode: + selectedProvince?.psgcCode, + shortname: + selectedProvince?.shortname); + CityMunicipality? city = + CityMunicipality( + code: selectedMunicipality + ?.code, + description: + selectedMunicipality + ?.description, + province: province, + psgcCode: selectedMunicipality + ?.psgcCode, + zipcode: selectedMunicipality + ?.zipcode); + + Address address = Address( + id: null, + addressCategory: selectedCategory, + country: selectedCountry, + barangay: selectedBarangay, + addressClass: null, + cityMunicipality: city); + + if (selectedCountry != null) { + personalReference = + PersonalReference( + id: null, + address: Address( + id: null, + addressCategory: + selectedCategory, + country: selectedCountry, + barangay: null, + cityMunicipality: null, + addressClass: null), + lastName: lastname, + contactNo: mobile, + firstName: firstname, + middleName: middlename); + } else { + personalReference = + PersonalReference( + id: null, + address: address, + lastName: lastname, + contactNo: mobile, + firstName: firstname, + middleName: middlename); + } + + context.read().add( + AddReference( + profileId: + int.parse(profileId!), + reference: personalReference, + token: token!)); + } + }, + ), + ), + const SizedBox( + height: 20, + ), + ], + )), + ), + ); + } + return Container(); + }, + ); + } + return Container(); + }, + ); + } + return Container(); + }, + ); + } + + Future getProvinces() async { + try { + List newProvinces = await LocationUtils.instance + .getProvinces(regionCode: selectedRegion!.code.toString()); + setState(() { + provinces = newProvinces; + selectedProvince = provinces![0]; + provinceCall = false; + cityCall = true; + getCities(); + }); + } catch (e) { + context.read().add(CallErrorState()); + } + } + + Future getCities() async { + try { + List newCities = await LocationUtils.instance + .getCities(code: selectedProvince!.code.toString()); + citymuns = newCities; + setState(() { + selectedMunicipality = newCities[0]; + cityCall = false; + barangayCall = true; + getBarangays(); + }); + } catch (e) { + context.read().add(CallErrorState()); + } + } + + Future getBarangays() async { + try { + List newBarangays = await LocationUtils.instance + .getBarangay(code: selectedMunicipality!.code.toString()); + barangays = newBarangays; + setState(() { + selectedBarangay = newBarangays[0]; + barangayCall = false; + }); + } catch (e) { + context.read().add(CallErrorState()); + } + } +} diff --git a/lib/screens/profile/components/reference/edit_modal.dart b/lib/screens/profile/components/reference/edit_modal.dart new file mode 100644 index 0000000..6c02990 --- /dev/null +++ b/lib/screens/profile/components/reference/edit_modal.dart @@ -0,0 +1,637 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; +import '../../../../bloc/profile/profile_bloc.dart'; +import '../../../../bloc/profile/references/references_bloc.dart'; +import '../../../../bloc/user/user_bloc.dart'; +import '../../../../model/location/address_category.dart'; +import '../../../../model/location/barangay.dart'; +import '../../../../model/location/city.dart'; +import '../../../../model/location/country.dart'; +import '../../../../model/location/provinces.dart'; +import '../../../../model/location/region.dart'; +import '../../../../model/profile/references.dart'; +import '../../../../theme-data.dart/btn-style.dart'; +import '../../../../theme-data.dart/colors.dart'; +import '../../../../theme-data.dart/form-style.dart'; +import '../../../../utils/location_utilities.dart'; +import '../../../../utils/text_container.dart'; + +class EditReferenceScreen extends StatefulWidget { + const EditReferenceScreen({super.key}); + + @override + State createState() => _EditReferenceScreenState(); +} + +class _EditReferenceScreenState extends State { + final formKey = GlobalKey(); + String? token; + String? profileId; + bool provinceCall = false; + bool cityCall = false; + bool barangayCall = false; + bool overseas = false; + List? provinces; + List? citymuns; + List? barangays; + ////seletected + Region? selectedRegion; + Province? selectedProvince; + CityMunicipality? selectedMunicipality; + Barangay? selectedBarangay; + Country? selectedCountry; + AddressCategory? selectedCategory; + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is UserLoggedIn) { + token = state.userData!.user!.login!.token; + profileId = state.userData!.user!.login!.user!.profileId.toString(); + return BlocBuilder( + builder: (context, state) { + if (state is ProfileLoaded) { + return BlocBuilder( + buildWhen: (previous, current) => false, + builder: (context, state) { + if (state is EditReferenceState) { + overseas = state.isOverseas; + selectedCategory = state.selectedCategory; + ////if not overseas address + //// set initial values + if (!overseas) { + selectedRegion = state.selectedRegion; + provinces = state.provinces; + selectedProvince = state.selectedProvince; + citymuns = state.cities; + selectedMunicipality = state.selectedCity; + barangays = state.barangays; + selectedBarangay = state.selectedBarangay; + } else { + selectedCountry = state.selectedCountry; + } + + return ProgressHUD( + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, horizontal: 18), + child: FormBuilder( + key: formKey, + child: Column( + children: [ + const SizedBox(height: 25), + Row( + children: [ + ////LAST NAME + Flexible( + flex: 1, + child: FormBuilderTextField( + initialValue: state.ref.lastName, + decoration: normalTextFieldStyle( + "Last name *", "Last name *"), + name: "lastname", + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + ), + ), + const SizedBox( + width: 5, + ), + ////FIRST NAME + Flexible( + flex: 1, + child: FormBuilderTextField( + initialValue: state.ref.firstName, + decoration: normalTextFieldStyle( + "First name *", "First name *"), + name: "firstname", + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + ), + ), + ], + ), + const SizedBox( + height: 8, + ), + Row( + children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + initialValue: state.ref.middleName, + decoration: normalTextFieldStyle( + "Middle name *", "Midlle name *"), + name: "middlename", + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + ), + ), + const SizedBox( + width: 5, + ), + ////CATEGORY + Flexible( + flex: 1, + child: FormBuilderDropdown< + AddressCategory>( + name: 'category', + validator: + FormBuilderValidators.required( + errorText: ""), + decoration: normalTextFieldStyle( + "Category", "Category"), + items: state.categories.map< + DropdownMenuItem< + AddressCategory>>( + (AddressCategory cat) { + return DropdownMenuItem< + AddressCategory>( + value: cat, + child: Text(cat.name!), + ); + }).toList(), + initialValue: selectedCategory, + onChanged: (value) { + selectedCategory = value; + }, + ), + ), + ], + ), + const SizedBox( + height: 8, + ), + + ////OVERSEAS ADDRESS + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: + normalTextFieldStyle("", ''), + name: 'overseas', + title: + const Text("Overseas Address?"), + ), + SizedBox( + height: overseas == true ? 8 : 0, + ), + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + DropdownButtonFormField< + Region?>( + isExpanded: true, + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: + FormBuilderValidators + .required( + errorText: + "This field is required"), + onChanged: (Region? + region) async { + setState(() { + provinceCall = true; + + selectedRegion = + region; + }); + //// GET PROVINCES + provinces = await LocationUtils + .instance + .getProvinces( + regionCode: + selectedRegion! + .code + .toString()); + selectedProvince = + provinces![0]; + setState(() { + provinceCall = false; + cityCall = true; + }); + ////GET CITY MUNICIPALITY + citymuns = await LocationUtils + .instance + .getCities( + code: + selectedProvince! + .code!); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + barangayCall = true; + }); + //// GET BARANGAYS + barangays = + await LocationUtils + .instance + .getBarangay( + code: selectedMunicipality! + .code!); + selectedBarangay = + barangays![0]; + setState(() { + barangayCall = false; + }); + }, + value: selectedRegion, + decoration: + normalTextFieldStyle( + "Region*", + "Region"), + items: state.regions.map< + DropdownMenuItem< + Region>>( + (Region region) { + return DropdownMenuItem< + Region>( + value: region, + child: Text(region + .description!)); + }).toList(), + ), + const SizedBox( + height: 8, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: + Colors.transparent, + inAsyncCall: + provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + onChanged: (Province? + province) async { + selectedProvince = + province; + setState(() { + cityCall = true; + }); + //// GET CITIES + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code!); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = + false; + barangayCall = + true; + }); + //// GET BARANGAY + barangays = await LocationUtils + .instance + .getBarangay( + code: selectedMunicipality! + .code!); + selectedBarangay = + barangays![0]; + setState(() { + barangayCall = + false; + }); + }, + value: + selectedProvince, + items: provinces == + null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>((Province + province) { + return DropdownMenuItem( + value: + province, + child: + FittedBox( + child: + Text(province.description!), + )); + }).toList(), + decoration: + normalTextFieldStyle( + "Province*", + "Province")), + ), + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: + DropdownButtonFormField< + CityMunicipality>( + validator: + FormBuilderValidators + .required( + errorText: + "This field is required"), + isExpanded: true, + onChanged: + (CityMunicipality? + city) async { + setState(() { + barangayCall = + true; + }); + selectedMunicipality = + city; + //// GET BARANGAYS + barangays = await LocationUtils + .instance + .getBarangay( + code: selectedMunicipality! + .code!); + selectedBarangay = + barangays![0]; + setState(() { + barangayCall = + false; + }); + }, + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: + selectedMunicipality, + items: citymuns == + null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality + c) { + return DropdownMenuItem( + value: c, + child: Text( + c.description!)); + }).toList(), + ), + ), + ), + //// BARANGAY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: + barangayCall, + child: + DropdownButtonFormField< + Barangay>( + validator: + FormBuilderValidators + .required( + errorText: + "This field is required"), + isExpanded: true, + onChanged: (Barangay? + baragay) { + selectedBarangay = + baragay; + }, + decoration: + normalTextFieldStyle( + "Barangay*", + "Barangay"), + value: + selectedBarangay, + items: barangays == + null + ? [] + : barangays!.map< + DropdownMenuItem< + Barangay>>((Barangay + barangay) { + return DropdownMenuItem( + value: + barangay, + child: Text( + barangay + .description!)); + }).toList(), + ), + ), + ), + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( + height: 60, + child: + DropdownButtonFormField< + Country>( + isExpanded: true, + value: selectedCountry, + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + items: state.countries.map< + DropdownMenuItem< + Country>>( + (Country country) { + return DropdownMenuItem< + Country>( + value: country, + child: FittedBox( + child: Text( + country + .name!))); + }).toList(), + + decoration: + normalTextFieldStyle( + "Country*", + "Country"), + //// country dropdown + onChanged: + (Country? value) { + selectedCountry = value; + }, + ), + ), + ), + ], + ); + }), + + FormBuilderTextField( + initialValue: state.ref.contactNo, + name: "mobile", + decoration: normalTextFieldStyle( + "Tel./Mobile *", "Tel./Mobile"), + validator: FormBuilderValidators.required( + errorText: "This field is required"), + ), + const Expanded( + child: SizedBox(), + ), + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + child: const Text(submit), + onPressed: () { + PersonalReference? personalReference; + if (formKey.currentState! + .saveAndValidate()) { + String lastname = formKey + .currentState!.value['lastname']; + String firstname = formKey + .currentState!.value['firstname']; + String middlename = formKey + .currentState! + .value['middlename']; + String mobile = formKey + .currentState!.value['mobile']; + + Region? region = selectedRegion; + Province? province = Province( + code: selectedProvince?.code, + description: + selectedProvince?.description, + region: region, + psgcCode: + selectedProvince?.psgcCode, + shortname: + selectedProvince?.shortname); + CityMunicipality? city = + CityMunicipality( + code: selectedMunicipality + ?.code, + description: + selectedMunicipality + ?.description, + province: province, + psgcCode: selectedMunicipality + ?.psgcCode, + zipcode: selectedMunicipality + ?.zipcode); + + ////IF IS OVERSEAS + if (overseas) { + personalReference = + PersonalReference( + id: state.ref.id, + address: Address( + id: state + .ref.address!.id, + addressCategory: + selectedCategory, + country: + selectedCountry, + barangay: null, + cityMunicipality: null, + addressClass: null), + lastName: lastname, + contactNo: mobile, + firstName: firstname, + middleName: middlename); + } else { + //// IF NOT OVERSEAS + personalReference = + PersonalReference( + id: state.ref.id, + address: Address( + id: state + .ref.address!.id, + addressCategory: + selectedCategory, + country: Country( + id: 175, + code: null, + name: null), + barangay: + selectedBarangay, + cityMunicipality: city, + addressClass: state + .ref + .address + ?.addressClass), + lastName: lastname, + contactNo: mobile, + firstName: firstname, + middleName: middlename); + } + + context.read().add( + EditReference( + profileId: + int.parse(profileId!), + reference: personalReference, + token: token!)); + } + }, + ), + ), + const SizedBox( + height: 20, + ), + ], + )), + ), + ); + } + return Container(); + }, + ); + } + return Container(); + }, + ); + } + return Container(); + }, + ); + } +} diff --git a/lib/screens/profile/components/references_screen.dart b/lib/screens/profile/components/references_screen.dart index 1849d9b..b7eb2f4 100644 --- a/lib/screens/profile/components/references_screen.dart +++ b/lib/screens/profile/components/references_screen.dart @@ -1,40 +1,60 @@ +import 'package:app_popup_menu/app_popup_menu.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; -import 'package:unit2/model/profile/references.dart'; +import 'package:unit2/screens/profile/components/reference/add_modal.dart'; +import 'package:unit2/screens/profile/components/reference/edit_modal.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; -import 'package:unit2/widgets/empty_data.dart'; +import 'package:unit2/widgets/Leadings/close_leading.dart'; +import 'package:unit2/widgets/error_state.dart'; import '../../../bloc/profile/references/references_bloc.dart'; +import '../../../utils/alerts.dart'; class ReferencesScreen extends StatelessWidget { const ReferencesScreen({super.key}); + @override Widget build(BuildContext context) { + int? profileId; + String? token; return Scaffold( appBar: AppBar( - title: const Text(referencesScreenTitle), + title: context.watch().state is AddReferenceState?const Text("Add Personal Reference"):context.watch().state is EditReferenceState?const Text("Edit Personal Reference"):const Text("Personal References"), centerTitle: true, backgroundColor: primary, - actions: [AddLeading(onPressed: () {})], + ////if state is adding or editing state show close button + actions: (context.watch().state is AddReferenceState || context.watch().state is EditReferenceState)? + [ + //// close button + CloseLeading(onPressed: (){ + context.read().add(GetReferences(profileId: profileId!, token: token!)); + }), + ]: + //// if state is loaded state show add button + context.watch().state is ReferencesLoadedState?[ + AddLeading(onPressed: (){ + context.read().add(ShowAddReferenceForm()); + }), + ]:[] ), body: ProgressHUD( - padding: const EdgeInsets.all(24), - backgroundColor: Colors.black87, - indicatorWidget: const SpinKitFadingCircle( - color: Colors.white), + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), child: BlocBuilder( builder: (context, state) { //userbloc if (state is UserLoggedIn) { + token = state.userData!.user!.login!.token; + profileId = state.userData!.user!.login!.user!.profileId; return BlocBuilder( builder: (context, state) { //profilebloc @@ -42,13 +62,82 @@ class ReferencesScreen extends StatelessWidget { return BlocConsumer( //listener listener: (context, state) { - if(state is ReferencesLoadingState){ - final progress = ProgressHUD.of(context); - progress!.showWithText("Please wait..."); + if (state is ReferencesLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); } - if(state is ReferencesLoadedState || state is ReferencesErrorState){ - final progress = ProgressHUD.of(context); - progress!.dismiss(); + if (state is ReferencesLoadedState || + state is AddReferenceState || + state is ReferencesErrorState || + state is DeleteReferenceState || + state is ReferencesAddedState || + state is EditReferenceState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + ////ADDED STATE + if (state is ReferencesAddedState) { + if (state.response['success']) { + successAlert(context, "Adding Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context.read().add( + LoadReferences( + references: state.personalRefs)); + }); + } else { + errorAlert(context, "Adding Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context.read().add( + LoadReferences( + references: state.personalRefs)); + }); + } + } + ////EDIT REFERENCE STATE + if (state is ReferenceEditedState) { + if (state.response['success']) { + successAlert(context, "Update Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context.read().add( + LoadReferences( + references: state.personalRefs)); + }); + } else { + errorAlert(context, "Update Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context.read().add( + LoadReferences( + references: state.personalRefs)); + }); + } + } + + ////DELETED STATE + if (state is DeleteReferenceState) { + if (state.success) { + successAlert(context, "Deletion Successfull", + "Eligibility has been deleted successfully", + () { + Navigator.of(context).pop(); + context.read().add( + LoadReferences( + references: state.references)); + }); + } else { + errorAlert(context, "Deletion Failed", + "Error deleting eligibility", () { + Navigator.of(context).pop(); + context.read().add( + LoadReferences( + references: state.references)); + }); + } } }, //builder @@ -56,72 +145,147 @@ class ReferencesScreen extends StatelessWidget { //references bloc if (state is ReferencesLoadedState) { if (state.references.isNotEmpty) { - return ListView.builder( - padding: - const EdgeInsets.symmetric(vertical: 8, horizontal: 10), - itemCount: state.references.length, - itemBuilder: (BuildContext context, int index) { - String fullname = - "${state.references[index].firstName} ${state.references[index].middleName} ${state.references[index].lastName}"; - String addres = - "${state.references[index].address!.cityMunicipality!.description}, ${state.references[index].address!.cityMunicipality!.province!.description}, ${state.references[0].address!.cityMunicipality!.province!.region!.description}"; - String mobile = state.references[index].contactNo.toString(); - return Column( - children: [ - Container( - padding: const EdgeInsets.symmetric( - horizontal: 12, vertical: 8), - decoration: box1(), - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(fullname, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith( - fontWeight: FontWeight.w500)), - const Divider(), - const SizedBox( - height: 5, - ), - Text(addres, - style: Theme.of(context) - .textTheme - .titleSmall! - .copyWith( - fontWeight: FontWeight.w500)), - const SizedBox( - height: 8, - ), - Text("${mobileOrPhone.toUpperCase()} : $mobile", - style: Theme.of(context) - .textTheme - .labelMedium!), - ], - ), - ), - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - )) - ], - ), - ), - const SizedBox( - height: 5, - ), - ], - ); - }); + return ListView.builder( + padding: const EdgeInsets.symmetric( + vertical: 8, horizontal: 10), + itemCount: state.references.length, + itemBuilder: + (BuildContext context, int index) { + String fullname = + "${state.references[index].firstName} ${state.references[index].middleName} ${state.references[index].lastName}"; + String addres = state.references[index] + .address!.country?.id != + 175 + ? "COUNTRY: ${state.references[index].address!.country!.name!.toUpperCase()}" + : "${state.references[index].address?.cityMunicipality?.description}, ${state.references[index].address?.cityMunicipality?.province?.description}, ${state.references[index].address?.cityMunicipality?.province?.region?.description}"; + + String mobile = state + .references[index].contactNo + .toString(); + return Column( + children: [ + Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + decoration: box1(), + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text(fullname.toUpperCase(), + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight + .w500)), + const Divider(), + const SizedBox( + height: 5, + ), + Text(addres, + style: Theme.of(context) + .textTheme + .titleSmall! + .copyWith( + fontWeight: + FontWeight + .w500)), + const SizedBox( + height: 8, + ), + Text( + "${mobileOrPhone.toUpperCase()} : $mobile", + style: Theme.of(context) + .textTheme + .labelMedium!), + ], + ), + ), + AppPopupMenu( + offset: const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + final progress = + ProgressHUD.of(context); + progress!.showWithText( + "Loading..."); + ////delete eligibilty-= = = = = = = = =>> + if (value == 2) { + confirmAlert(context, () { + context + .read< + ReferencesBloc>() + .add(DeleteReference( + profileId: + profileId!, + refId: state + .references[ + index] + .id!, + references: state + .references, + token: token!)); + }, "Delete?", + "Confirm Delete?"); + } + if (value == 1) { + ////edit reference-= = = = = = = = =>> + context + .read() + .add(ShowEditReferenceForm( + personalReference: + state.references[ + index])); + } + }, + menuItems: [ + popMenuItem( + text: "Edit", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Delete", + value: 2, + icon: Icons.delete), + popMenuItem( + text: "Attachment", + value: 3, + icon: FontAwesome.attach) + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ) + ], + ), + ), + const SizedBox( + height: 5, + ), + ], + ); + }); } } + if (state is AddReferenceState) { + return const AddReferenceScreen(); + } + if (state is EditReferenceState) { + return const EditReferenceScreen(); + } + if (state is ReferencesErrorState) { + return SomethingWentWrong( + message: state.message, onpressed: () {}); + } return Container(); }, ); @@ -135,5 +299,23 @@ class ReferencesScreen extends StatelessWidget { ), )); } -} + PopupMenuItem popMenuItem({String? text, int? value, IconData? icon}) { + return PopupMenuItem( + value: value, + child: Row( + children: [ + Icon( + icon, + ), + const SizedBox( + width: 10, + ), + Text( + text!, + ), + ], + ), + ); + } +} diff --git a/lib/sevices/profile/references_services.dart b/lib/sevices/profile/references_services.dart index f425f0d..ee215ba 100644 --- a/lib/sevices/profile/references_services.dart +++ b/lib/sevices/profile/references_services.dart @@ -16,7 +16,7 @@ class ReferencesServices{ List references = []; String authToken = "Token $token"; - String path = "${Url.instance.getRefences()}$profileId/"; + String path = "${Url.instance.reference()}$profileId/"; Map headers = { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': authToken @@ -37,4 +37,100 @@ List references = []; } return references; } -} \ No newline at end of file + Future>addReference({required PersonalReference ref, required String token, required int profileId})async{ + String authToken = "Token $token"; + String path = "${Url.instance.reference()}$profileId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + Map responseStatus ={}; + bool overseas = ref.address!.country !=null?true:false; + Map body = { + "first_name": ref.firstName, + "middle_name": ref.middleName, + "last_name": ref.lastName, + "contact_no":ref.contactNo, + "_addressCatId": ref.address!.addressCategory!.id, + "_areaClass": "Village", + "_citymunCode": overseas?null: ref.address?.cityMunicipality?.code, + "_brgyCode": overseas?null:ref.address?.barangay?.code, + "_countryId": overseas?ref.address!.country!.id:175 + }; + try{ + http.Response response = await Request.instance.postRequest(path: path,body: body,param: {},headers: headers); + if(response.statusCode == 201){ + Map data = jsonDecode(response.body); + responseStatus = data; + }else{ + responseStatus.addAll({'success':false}); + } + return responseStatus; + }catch(e){ + throw e.toString(); + } +} +Future> update({required PersonalReference ref,required String token, required int profileId})async{ + String authToken = "Token $token"; + String path = "${Url.instance.reference()}$profileId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + Map responseStatus ={}; + bool overseas = ref.address!.country!.id != 175; + Map body = { + "id":ref.id, + "related_person_id": profileId, + "first_name": ref.firstName, + "middle_name": ref.middleName, + "last_name": ref.lastName, + "contact_no":ref.contactNo, + "_addressCatId": ref.address!.addressCategory!.id, + "_areaClass": "Village", + "_citymunCode": overseas?null: ref.address?.cityMunicipality?.code, + "_brgyCode": overseas?null:ref.address?.barangay?.code, + "_countryId": overseas?ref.address!.country!.id:175 + }; + // try{ + http.Response response = await Request.instance.putRequest(path: path,body: body,param: {},headers: headers); + if(response.statusCode == 200){ + Map data = jsonDecode(response.body); + responseStatus = data; + }else{ + responseStatus.addAll({'success':false}); + } + return responseStatus; + // }catch(e){ + // throw e.toString(); + // } +} + +Futuredelete({required int profileId, required String token, required int id })async{ + bool? success; + String authtoken = "Token $token"; + String path = "${Url.instance.reference()}$profileId/"; + Map params = {"force_mode": "true"}; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authtoken + }; + try{ + http.Response response = await Request.instance.deleteRequest(path: path, headers: headers, body: {"id":id}, param: params); + if(response.statusCode == 200){ + Map data = jsonDecode(response.body); + success=data['success']; + }else{ + success = false; + } + return success!; + }catch(e){ + throw e.toString(); + } + +} + +} + + + diff --git a/lib/sevices/profile/work_history_services.dart b/lib/sevices/profile/work_history_services.dart index bb43770..92906f7 100644 --- a/lib/sevices/profile/work_history_services.dart +++ b/lib/sevices/profile/work_history_services.dart @@ -24,7 +24,7 @@ class WorkHistoryService { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': authToken }; - String path = Url.instance.getWorkHistories() + profileId.toString(); + String path = Url.instance.workhistory() + profileId.toString(); try { http.Response response = await Request.instance .getRequest(path: path, headers: headers, param: {}); @@ -75,7 +75,7 @@ class WorkHistoryService { bool? success; Map params = {"force_mode": "true"}; String authToken = "Token $token"; - String path = "${Url.instance.deleteWorkHistory()}$profileId/"; + String path = "${Url.instance.workhistory()}$profileId/"; Map body = { "id": work.id, "position_id": work.position!.id, @@ -136,7 +136,7 @@ class WorkHistoryService { Future> update({required WorkHistory oldWorkHistory, required WorkHistory newWorkHistory, required String token, required String profileId})async{ Map? statusResponse={}; String authtoken = "Token $token"; - String path = '${Url.instance.updateWorkHistories()}$profileId/'; + String path = '${Url.instance.workhistory()}$profileId/'; Map headers = { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': authtoken @@ -176,7 +176,7 @@ class WorkHistoryService { //Add work history Future>add({required WorkHistory workHistory, required String token, required int profileId , required bool isPrivate})async{ String authtoken = "Token $token"; - String path = '${Url.instance.addWorkHistory()}$profileId/'; + String path = '${Url.instance.workhistory()}$profileId/'; Map headers = { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': authtoken diff --git a/lib/utils/location_utilities.dart b/lib/utils/location_utilities.dart index a5678f6..e52c10b 100644 --- a/lib/utils/location_utilities.dart +++ b/lib/utils/location_utilities.dart @@ -1,5 +1,7 @@ import 'dart:convert'; +import 'package:unit2/model/location/address_category.dart'; +import 'package:unit2/model/location/barangay.dart'; import 'package:unit2/model/location/city.dart'; import 'package:unit2/model/location/country.dart'; import 'package:http/http.dart' as http; @@ -8,95 +10,133 @@ import 'package:unit2/utils/request.dart'; import 'package:unit2/utils/urls.dart'; import '../model/location/region.dart'; + class LocationUtils { static final LocationUtils _instance = LocationUtils(); static LocationUtils get instance => _instance; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; - Map headers = { - 'Content-Type': 'application/json; charset=UTF-8', - }; - - Future>getCountries()async{ - List countries=[]; + Future> getCountries() async { + List countries = []; String path = Url.instance.getCounties(); - - - // try{ - http.Response response = await Request.instance.getRequest(path: path, param: {},headers: headers); - if(response.statusCode == 200){ - Map data = jsonDecode(response.body); - if(data['data'] != null){ - data['data'].forEach((var country){ - Country newCOuntry = Country.fromJson(country); - countries.add(newCOuntry); - }); - } - } - // }catch(e){ - // throw(e.toString()); - // } - return countries; - } - - -Future>getRegions()async{ - List regions=[]; - String path = Url.instance.getRegions(); - - - // try{ - http.Response response = await Request.instance.getRequest(path: path, param: {},headers: headers); - if(response.statusCode == 200){ - Map data = jsonDecode(response.body); - if(data['data'] != null){ - data['data'].forEach((var region){ - Region newRegion = Region.fromJson(region); - regions.add(newRegion); - }); - } - } - // }catch(e){ - // throw(e.toString()); - // } - return regions; - } - - Future>getProvinces({required String regionCode})async{ - List provinces = []; - String path = Url.instance.getProvinces()+regionCode; try{ - http.Response response = await Request.instance.getRequest(path: path,param:{},headers: headers); + http.Response response = await Request.instance + .getRequest(path: path, param: {}, headers: headers); + if (response.statusCode == 200) { Map data = jsonDecode(response.body); - if(data['data'] != null){ - data['data'].forEach((var province){ + if (data['data'] != null) { + data['data'].forEach((var country) { + Country newCOuntry = Country.fromJson(country); + countries.add(newCOuntry); + }); + } + } + }catch(e){ + throw(e.toString()); + } + return countries; + } + + Future> getRegions() async { + List regions = []; + String path = Url.instance.getRegions(); + + try{ + http.Response response = await Request.instance + .getRequest(path: path, param: {}, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((var region) { + Region newRegion = Region.fromJson(region); + regions.add(newRegion); + }); + } + } + }catch(e){ + throw(e.toString()); + } + return regions; + } + + Future> getProvinces({required String regionCode}) async { + List provinces = []; + String path = Url.instance.getProvinces() + regionCode; + + try { + http.Response response = await Request.instance + .getRequest(path: path, param: {}, headers: headers); + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((var province) { Province newProvince = Province.fromJson(province); provinces.add(newProvince); }); } - - }catch(e){ - throw(e.toString()); + } catch (e) { + throw (e.toString()); } - return provinces; + return provinces; } - Future>getCities({required String code})async{ + Future> getCities({required String code}) async { List cities = []; - String path = Url.instance.getCities()+code; - try{ - http.Response response = await Request.instance.getRequest(path: path, param: {},headers: headers); + String path = Url.instance.getCities() + code; + try { + http.Response response = await Request.instance + .getRequest(path: path, param: {}, headers: headers); Map data = jsonDecode(response.body); - if(data['data'] != null){ - data['data'].forEach((var city){ + if (data['data'] != null) { + data['data'].forEach((var city) { CityMunicipality cityMun = CityMunicipality.fromJson(city); cities.add(cityMun); }); } - }catch(e){ - throw(e.toString()); + } catch (e) { + throw (e.toString()); } return cities; } -} \ No newline at end of file + + Future> getBarangay({required String code}) async { + List barangays = []; + String path = Url.instance.getBarangays() + code; + try { + http.Response response = await Request.instance + .getRequest(path: path, param: {}, headers: headers); + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((var city) { + Barangay barangay = Barangay.fromJson(city); + barangays.add(barangay); + }); + } + } catch (e) { + throw (e.toString()); + } + return barangays; + } + Future>getAddressCategory()async{ + List categories = []; + String path = Url.instance.getAddressCategory(); + try{ + http.Response response = await Request.instance.getRequest(path: path,param: {},headers:headers ); + Map data = jsonDecode(response.body); + if(data['data'] != null){ + data['data'].forEach((var cat){ + categories.add(AddressCategory.fromJson(cat)); + }); + } + categories; + return categories; + }catch(e){ + throw e.toString(); + } + } + + +} diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index e04f450..a7972ea 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -42,7 +42,7 @@ String updateEligibility(){ return "/api/jobnet_app/profile/pds/eligibility/"; } //// work history paths -String getWorkHistories(){ +String workhistory(){ return "/api/jobnet_app/profile/pds/work/"; } String getPositions(){ @@ -51,19 +51,13 @@ String getPositions(){ String getAgencies(){ return "/api/jobnet_app/agencies/"; } -String updateWorkHistories(){ - return "/api/jobnet_app/profile/pds/work/"; -} -String addWorkHistory(){ - return "/api/jobnet_app/profile/pds/work/"; -} + + String getAgencyCategory(){ return "api/jobnet_app/agency_categories/"; } -String deleteWorkHistory(){ - return "/api/jobnet_app/profile/pds/work/"; -} + ////educational background paths String getEducationalBackgrounds(){ @@ -77,7 +71,7 @@ String getLearningAndDevelopments(){ } //// references paths -String getRefences(){ +String reference(){ return "/api/jobnet_app/profile/pds/personal_reference/"; } @@ -106,6 +100,7 @@ String getFamilies(){ return "/api/jobnet_app/profile/pds/family/"; } + // location utils path String getCounties(){ return "/api/jobnet_app/countries/"; @@ -119,4 +114,10 @@ String getFamilies(){ String getCities(){ return "/api/web_app/location/citymun/"; } + String getBarangays(){ + return "/api/web_app/location/barangay/"; + } + String getAddressCategory(){ + return "/api/jobnet_app/address_categories/"; + } } \ No newline at end of file From 58e1167613536650a07f27b9d3942b0fb5723454 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Mon, 27 Mar 2023 16:59:08 +0800 Subject: [PATCH 55/86] Implemented crud operation API for organization membership screen --- .../non_academic_recognition_bloc.dart | 27 ++ .../non_academic_recognition_event.dart | 38 +- .../non_academic_recognition_state.dart | 33 ++ .../organization_membership_bloc.dart | 64 ++- .../organization_membership_event.dart | 35 +- .../organization_membership_state.dart | 20 + .../profile/workHistory/workHistory_bloc.dart | 10 +- .../non_acedimic_recognition.dart | 33 +- .../non_academic/add_modal.dart | 343 ++++++++++++++++ .../non_academic_recognition_screen.dart | 13 +- .../org_membership/add_modal.dart | 373 ++++++++++++++++++ .../org_membership_screen.dart | 131 +++++- .../components/work_history/add_modal.dart | 2 +- .../profile/non_academic_services.dart | 72 +++- .../profile/orgmembership_services.dart | 53 +++ .../profile/work_history_services.dart | 48 --- lib/utils/profile_utilities.dart | 55 +++ lib/utils/urls.dart | 4 +- 18 files changed, 1250 insertions(+), 104 deletions(-) create mode 100644 lib/screens/profile/components/other_information/non_academic/add_modal.dart create mode 100644 lib/screens/profile/components/other_information/org_membership/add_modal.dart diff --git a/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_bloc.dart b/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_bloc.dart index 690015a..c4728f8 100644 --- a/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_bloc.dart +++ b/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_bloc.dart @@ -3,6 +3,9 @@ import 'package:equatable/equatable.dart'; import 'package:unit2/sevices/profile/non_academic_services.dart'; import '../../../../model/profile/other_information/non_acedimic_recognition.dart'; +import '../../../../model/utils/agency.dart'; +import '../../../../model/utils/category.dart'; +import '../../../../utils/profile_utilities.dart'; part 'non_academic_recognition_event.dart'; @@ -11,6 +14,9 @@ part 'non_academic_recognition_state.dart'; class NonAcademicRecognitionBloc extends Bloc { NonAcademicRecognitionBloc() : super(NonAcademicRecognitionInitial()) { List nonAcademicRecognitions = []; + List agencies = []; + List agencyCategory = []; + ////GET on((event, emit)async { emit(NonAcademicRecognitionLoadingState()); try{ @@ -21,5 +27,26 @@ class NonAcademicRecognitionBloc extends Bloc((event, emit)async{ + emit(NonAcademicRecognitionLoadingState()); + try{ + if(agencies.isEmpty){ + List newAgencies = await ProfileUtilities.instance.getAgecies(); + agencies = newAgencies; + } + if(agencyCategory.isEmpty){ + ListnewAgencyCategories = await ProfileUtilities.instance.agencyCategory(); + agencyCategory = newAgencyCategories; + } + emit(AddNonAcademeRecognitionState(agencies: agencies, agencyCategories: agencyCategory)); + }catch(e){ + emit(NonAcademicRecognitionErrorState(message: e.toString())); + } + },); + ////ADD + on((event,emit){ + emit(NonAcademicRecognitionLoadingState()); + }); } } diff --git a/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_event.dart b/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_event.dart index c262400..793ddd3 100644 --- a/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_event.dart +++ b/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_event.dart @@ -6,7 +6,7 @@ abstract class NonAcademicRecognitionEvent extends Equatable { @override List get props => []; } - +//// GET EVENT class GetNonAcademicRecognition extends NonAcademicRecognitionEvent{ final int profileId; final String token; @@ -14,3 +14,39 @@ class GetNonAcademicRecognition extends NonAcademicRecognitionEvent{ @override List get props => [profileId,token]; } +////LOAD EVENT +class LoadNonAcademeRecognition extends NonAcademicRecognitionEvent{ + final List nonAcademicRecognitions; + const LoadNonAcademeRecognition({required this.nonAcademicRecognitions}); + @override + List get props => [nonAcademicRecognitions]; +} + +////SHOW ADD FORM EVENT +class ShowAddNonAcademeRecognitionForm extends NonAcademicRecognitionEvent{ + +} + +//// ADD EVENT +class AddNonAcademeRecognition extends NonAcademicRecognitionEvent{ + final int profileId; + final String token; + final NonAcademicRecognition nonAcademicRecognition; + const AddNonAcademeRecognition({required this.nonAcademicRecognition, required this.profileId, required this.token}); + @override + List get props => [nonAcademicRecognition,profileId,token]; + +} + +//// DELETE EVENT +class DeleteNonAcademeRecognition extends NonAcademicRecognitionEvent{ + final int profileId; + final String token; + final int id; + final String title; + const DeleteNonAcademeRecognition({required this.id,required this.profileId, required this.title,required this.token}); + @override + List get props => [profileId,token,id,title]; + +} + diff --git a/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_state.dart b/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_state.dart index 7d4ada6..dbb737b 100644 --- a/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_state.dart +++ b/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_state.dart @@ -9,16 +9,49 @@ abstract class NonAcademicRecognitionState extends Equatable { class NonAcademicRecognitionInitial extends NonAcademicRecognitionState {} + +////LOADING STATE class NonAcademicRecognitionLoadingState extends NonAcademicRecognitionState{ } +////LOADED STATE class NonAcademicRecognitionLoadedState extends NonAcademicRecognitionState{ final List nonAcademicRecognition; const NonAcademicRecognitionLoadedState({required this.nonAcademicRecognition}); @override List get props => []; } +////DELETED STATE +class NonAcademeRecognitionDeletedState extends NonAcademicRecognitionState{ + final List nonAcademicRecognition; + final bool success; + const NonAcademeRecognitionDeletedState({required this.nonAcademicRecognition, required this.success}); + @override + List get props => [nonAcademicRecognition,success]; +} +////ADDED STATE +class NonAcademeRecognitionAddedState extends NonAcademicRecognitionState{ + final List nonAcademicRecognition; + final Map response; + const NonAcademeRecognitionAddedState({required this.nonAcademicRecognition, required this.response}); + @override + List get props => [nonAcademicRecognition,response]; +} + +////ADDING STATE +class AddNonAcademeRecognitionState extends NonAcademicRecognitionState{ + final List agencies; +final List agencyCategories; +const AddNonAcademeRecognitionState({required this.agencies, required this.agencyCategories}); + @override + List get props => [agencies,agencyCategories]; + +} + + + +////ERROR STATE class NonAcademicRecognitionErrorState extends NonAcademicRecognitionState{ final String message; const NonAcademicRecognitionErrorState({required this.message}); diff --git a/lib/bloc/profile/other_information/org_membership/organization_membership_bloc.dart b/lib/bloc/profile/other_information/org_membership/organization_membership_bloc.dart index cb27502..41da2ed 100644 --- a/lib/bloc/profile/other_information/org_membership/organization_membership_bloc.dart +++ b/lib/bloc/profile/other_information/org_membership/organization_membership_bloc.dart @@ -1,6 +1,10 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:unit2/model/profile/work_history.dart'; +import 'package:unit2/model/utils/agency.dart'; +import 'package:unit2/model/utils/category.dart'; import 'package:unit2/sevices/profile/orgmembership_services.dart'; +import 'package:unit2/utils/profile_utilities.dart'; import '../../../../model/profile/other_information/organization_memberships.dart'; @@ -8,17 +12,75 @@ part 'organization_membership_event.dart'; part 'organization_membership_state.dart'; class OrganizationMembershipBloc extends Bloc { + List agencies = []; + List agencyCategory = []; OrganizationMembershipBloc() : super(OrganizationMembershipInitial()) { List organizationMemberships=[]; on((event, emit) async{ emit(OrgmembershipLoadingState()); try{ - List orgs = await OrganizationMembershipServices.instance.getOrgMemberships(event.profileId, event.token); + if(organizationMemberships.isEmpty){ + List orgs = await OrganizationMembershipServices.instance.getOrgMemberships(event.profileId!, event.token!); organizationMemberships = orgs; + } + emit(OrganizationMembershipLoaded(orgMemberships: organizationMemberships)); }catch(e){ OrganizationMembershipErrorState(message: e.toString()); } }); + on((event,emit){ + emit(OrganizationMembershipLoaded(orgMemberships: event.organizationMemberships)); + }); + + ////SHOW ADD ORG MEMBERSHIP FORM + on((event,emit)async{ + emit(OrgmembershipLoadingState()); + try{ + if(agencies.isEmpty){ + List newAgencies = await ProfileUtilities.instance.getAgecies(); + agencies = newAgencies; + } + if(agencyCategory.isEmpty){ + ListnewAgencyCategories = await ProfileUtilities.instance.agencyCategory(); + agencyCategory = newAgencyCategories; + } + emit(AddOrgMembershipState(agencies: agencies, agencyCategories: agencyCategory)); + }catch(e){ + emit(OrganizationMembershipErrorState(message: e.toString())); + } + }); + //// ADD ORGMEMBERSHIP + on((event,emit)async{ + emit(OrgmembershipLoadingState()); + try{ + Map status= await OrganizationMembershipServices.instance.add(agency: event.agency, token: event.token, profileId: event.profileId.toString()); + if(status["success"]){ + OrganizationMembership organizationMembership = OrganizationMembership.fromJson(status["data"]); + organizationMemberships.add(organizationMembership); + emit(OrgMembershipAddedState(orgMemberships: organizationMemberships, response: status)); + }else{ + emit(OrgMembershipAddedState(orgMemberships: organizationMemberships, response: status)); + } + }catch(e){ + emit(OrganizationMembershipErrorState(message: e.toString())); + } + }); + ////DELETE ORGMEMBERSHIP + on((event,emit)async{ + emit(OrgmembershipLoadingState()); + try{ + final bool success = await OrganizationMembershipServices.instance.delete(agency: event.org.agency!, profileId: event.profileId, token: event.token); + if(success){ + event.organizationMemberships.removeWhere((element) => element.agency!.id == event.org.agency!.id ); + List orgmemberships = event.organizationMemberships; + emit(OrgMembershipDeletedState(organizationMemberships: orgmemberships, success: success)); + }else{ + emit(OrgMembershipDeletedState(organizationMemberships: organizationMemberships, success: success)); + } + }catch(e){ + emit(OrganizationMembershipErrorState(message: e.toString())); + } + }); } } diff --git a/lib/bloc/profile/other_information/org_membership/organization_membership_event.dart b/lib/bloc/profile/other_information/org_membership/organization_membership_event.dart index 9375837..2be3627 100644 --- a/lib/bloc/profile/other_information/org_membership/organization_membership_event.dart +++ b/lib/bloc/profile/other_information/org_membership/organization_membership_event.dart @@ -7,10 +7,39 @@ abstract class OrganizationMembershipEvent extends Equatable { List get props => []; } +class LoadOrganizationMemberships extends OrganizationMembershipEvent{ + final List organizationMemberships; + const LoadOrganizationMemberships({required this.organizationMemberships}); + + @override + List get props => [organizationMemberships]; +} + class GetOrganizationMembership extends OrganizationMembershipEvent{ + final int? profileId; + final String? token; + const GetOrganizationMembership({ this.profileId, this.token}); + +} +class ShowAddOrgMembershipForm extends OrganizationMembershipEvent{ + +} +class AddOrgMembership extends OrganizationMembershipEvent{ final int profileId; final String token; - const GetOrganizationMembership({required this.profileId, required this.token}); - @override - List get props => [profileId,token]; + final Agency agency; + const AddOrgMembership({required this.agency, required this.profileId, required this.token}); + @override + List get props => [profileId,token,agency]; +} + +class DeleteOrgMemberShip extends OrganizationMembershipEvent{ + final int profileId; + final String token; + final OrganizationMembership org; + final List organizationMemberships; + const DeleteOrgMemberShip({required this.profileId, required this.token, required this.org, required this.organizationMemberships}); + @override + List get props => [profileId,token,org,organizationMemberships]; + } diff --git a/lib/bloc/profile/other_information/org_membership/organization_membership_state.dart b/lib/bloc/profile/other_information/org_membership/organization_membership_state.dart index c922624..f84adb7 100644 --- a/lib/bloc/profile/other_information/org_membership/organization_membership_state.dart +++ b/lib/bloc/profile/other_information/org_membership/organization_membership_state.dart @@ -26,3 +26,23 @@ class OrganizationMembershipErrorState extends OrganizationMembershipState{ class OrgmembershipLoadingState extends OrganizationMembershipState{ } +class OrgMembershipDeletedState extends OrganizationMembershipState{ + final List organizationMemberships; + final bool success; + const OrgMembershipDeletedState({required this.organizationMemberships, required this.success}); + @override + List get props => [organizationMemberships,success]; +} +class OrgMembershipAddedState extends OrganizationMembershipState{ + final List orgMemberships; + final Map response; + const OrgMembershipAddedState({required this.orgMemberships, required this.response}); + @override + List get props => [orgMemberships,response]; +} + +class AddOrgMembershipState extends OrganizationMembershipState{ +final List agencies; +final List agencyCategories; +const AddOrgMembershipState({required this.agencies, required this.agencyCategories}); +} diff --git a/lib/bloc/profile/workHistory/workHistory_bloc.dart b/lib/bloc/profile/workHistory/workHistory_bloc.dart index c645ad0..4f42748 100644 --- a/lib/bloc/profile/workHistory/workHistory_bloc.dart +++ b/lib/bloc/profile/workHistory/workHistory_bloc.dart @@ -6,6 +6,7 @@ import 'package:unit2/model/utils/agency.dart'; import 'package:unit2/model/utils/agency_position.dart'; import 'package:unit2/model/utils/position.dart'; import 'package:unit2/sevices/profile/work_history_services.dart'; +import 'package:unit2/utils/profile_utilities.dart'; import '../../../model/utils/category.dart'; @@ -37,6 +38,7 @@ class WorkHistoryBloc extends Bloc { workExperiences = event.workHistories; emit(WorkHistoryLoaded(workExperiences: workExperiences)); }); + ////DELETE on((event, emit) async { emit(WorkHistoryLoadingState()); try { @@ -116,14 +118,14 @@ on((event, emit)async{ /////AGENCIES------------------------------------------ if (agencies.isEmpty) { List newAgencies = - await WorkHistoryService.instance.getAgecies(); + await ProfileUtilities.instance.getAgecies(); agencies = newAgencies; } /////Category Agency------------------------------------------ if (agencyCategory.isEmpty) { List categoryAgencies = - await WorkHistoryService.instance.agencyCategory(); + await ProfileUtilities.instance.agencyCategory(); agencyCategory = categoryAgencies; } /////////------------------------------------- @@ -154,12 +156,12 @@ on((event, emit)async{ /////AGENCIES------------------------------------------ List newAgencies = - await WorkHistoryService.instance.getAgecies(); + await ProfileUtilities.instance.getAgecies(); agencies = newAgencies; /////Category Agency------------------------------------------ List categoryAgencies = - await WorkHistoryService.instance.agencyCategory(); + await ProfileUtilities.instance.agencyCategory(); agencyCategory = categoryAgencies; /////////------------------------------------- List status = diff --git a/lib/model/profile/other_information/non_acedimic_recognition.dart b/lib/model/profile/other_information/non_acedimic_recognition.dart index 9e4b6a7..7d277e0 100644 --- a/lib/model/profile/other_information/non_acedimic_recognition.dart +++ b/lib/model/profile/other_information/non_acedimic_recognition.dart @@ -4,7 +4,7 @@ import 'dart:convert'; -import '../../utils/category.dart'; +import 'package:unit2/model/utils/agency.dart'; NonAcademicRecognition nonAcademicRecognitionFromJson(String str) => NonAcademicRecognition.fromJson(json.decode(str)); @@ -19,12 +19,12 @@ class NonAcademicRecognition { final int? id; final String? title; - final Presenter? presenter; + final Agency? presenter; factory NonAcademicRecognition.fromJson(Map json) => NonAcademicRecognition( id: json["id"], title: json["title"], - presenter: json["presenter"] == null ? null : Presenter.fromJson(json["presenter"]), + presenter: json["presenter"] == null ? null : Agency.fromJson(json["presenter"]), ); Map toJson() => { @@ -34,31 +34,4 @@ class NonAcademicRecognition { }; } -class Presenter { - Presenter({ - this.id, - this.name, - this.category, - this.privateEntity, - }); - - final int? id; - final String? name; - final Category? category; - final bool? privateEntity; - - factory Presenter.fromJson(Map json) => Presenter( - id: json["id"], - name: json["name"], - category: json["category"] == null ? null : Category.fromJson(json["category"]), - privateEntity: json["private_entity"], - ); - - Map toJson() => { - "id": id, - "name": name, - "category": category?.toJson(), - "private_entity": privateEntity, - }; -} diff --git a/lib/screens/profile/components/other_information/non_academic/add_modal.dart b/lib/screens/profile/components/other_information/non_academic/add_modal.dart new file mode 100644 index 0000000..856f105 --- /dev/null +++ b/lib/screens/profile/components/other_information/non_academic/add_modal.dart @@ -0,0 +1,343 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:searchfield/searchfield.dart'; +import 'package:unit2/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_bloc.dart'; +import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/user/user_bloc.dart'; +import 'package:unit2/model/profile/other_information/non_acedimic_recognition.dart'; +import 'package:unit2/theme-data.dart/form-style.dart'; +import 'package:unit2/utils/global.dart'; + +import '../../../../../model/utils/agency.dart'; +import '../../../../../model/utils/category.dart'; +import '../../../../../theme-data.dart/box_shadow.dart'; +import '../../../../../theme-data.dart/btn-style.dart'; +import '../../../../../theme-data.dart/colors.dart'; + +class AddNonAcademicRecognitionScreen extends StatefulWidget { + const AddNonAcademicRecognitionScreen({super.key}); + + @override + State createState() => + _AddNonAcademicRecognitionScreenState(); +} + +class _AddNonAcademicRecognitionScreenState + extends State { + bool showAgencyCategory = false; + final agencyFocusNode = FocusNode(); + + final agencyCategoryFocusNode = FocusNode(); + final addAgencyController = TextEditingController(); + Agency? selectedAgency; + Category? selectedCategory; + Agency? newAgency; + bool showIsPrivateRadio = false; + bool? isPrivate = false; + NonAcademicRecognition? nonAcademicRecognition; + final _formKey = GlobalKey(); + int? profileId; + String? token; + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is UserLoggedIn) { + return BlocBuilder( + builder: (context, state) { + if (state is ProfileLoaded) { + return BlocBuilder( + builder: (context, state) { + if (state is AddNonAcademeRecognitionState) { + return SizedBox( + height: blockSizeVertical * 90, + child: SingleChildScrollView( + child: FormBuilder(child: Padding( + padding: const EdgeInsets.symmetric(vertical: 25,horizontal: 18), + child: Column( + children: [ + FormBuilderTextField(name: 'title', + decoration: normalTextFieldStyle("Recognition / Award Title *", "Recognition / Award Title"), + validator: FormBuilderValidators.required(errorText: "this field is required"), + ), + const SizedBox(height: 12,), + StatefulBuilder( + builder: (context, setState) { + //// AGENCY SEARCHFIELD + return Column( + children: [ + SearchField( + itemHeight: 70, + suggestions: state.agencies + .map((Agency agency) => + SearchFieldListItem( + agency.name!, + item: agency, + child: ListTile( + title: Text( + agency.name! + .toUpperCase(), + overflow: + TextOverflow + .ellipsis, + ), + subtitle: Text(agency + .privateEntity == + true + ? "Private" + : agency.privateEntity == + false + ? "Government" + : ""), + ))) + .toList(), + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + focusNode: agencyFocusNode, + searchInputDecoration: + normalTextFieldStyle( + "Agency *", "") + .copyWith( + suffixIcon: + const Icon(Icons + .arrow_drop_down)), + ////agency suggestion tap + onSuggestionTap: (agency) { + setState(() { + selectedAgency = agency.item; + agencyFocusNode.unfocus(); + if (selectedAgency + ?.category == + null) { + showAgencyCategory = true; + showIsPrivateRadio = true; + } else { + showAgencyCategory = false; + showIsPrivateRadio = false; + } + }); + }, + emptyWidget: Container( + decoration: box1(), + height: 100, + child: Column( + mainAxisAlignment: + MainAxisAlignment + .center, + crossAxisAlignment: + CrossAxisAlignment + .center, + children: [ + const SizedBox( + height: 20, + ), + const Text( + "No result found..."), + const SizedBox( + height: 10, + ), + TextButton( + //// Add agency onpressed + onPressed: () { + showDialog( + context: + context, + builder: + (BuildContext + context) { + return AlertDialog( + title: const Text( + "Add Agency?"), + content: + SizedBox( + height: + 130, + child: + Column( + children: [ + TextFormField( + controller: + addAgencyController, + decoration: + normalTextFieldStyle("", ""), + ), + const SizedBox( + height: + 12, + ), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + style: mainBtnStyle(primary, Colors.transparent, second), + //// onpressed + onPressed: () { + setState(() { + newAgency = Agency(id: null, name: addAgencyController.text.toUpperCase(), category: null, privateEntity: null); + state.agencies.insert(0, newAgency!); + addAgencyController.clear(); + Navigator.pop(context); + }); + }, + child: const Text("Add"))), + ], + ), + ), + ); + }); + }, + child: const Text( + "Add position")) + ]), + ), + ), + const SizedBox( + height: 8, + ), + SizedBox( + child: showAgencyCategory + ? SearchField( + focusNode: + agencyCategoryFocusNode, + itemHeight: 70, + suggestions: state + .agencyCategories + .map((Category + category) => + SearchFieldListItem( + category + .name!, + item: + category, + child: + ListTile( + title: Text( + category + .name!), + subtitle: Text(category + .industryClass! + .name!), + ))) + .toList(), + emptyWidget: Container( + height: 100, + decoration: box1(), + child: const Center( + child: Text( + "No result found ...")), + ), + ////agency controller suggestion tap + onSuggestionTap: + (agencyCategory) { + setState(() { + selectedCategory = + agencyCategory + .item; + + agencyCategoryFocusNode + .unfocus(); + }); + }, + searchInputDecoration: + normalTextFieldStyle( + "Category *", + "") + .copyWith( + suffixIcon: + const Icon( + Icons + .arrow_drop_down)), + validator: + FormBuilderValidators + .required( + errorText: + "This field is required"), + ) + : const SizedBox(), + ), + + ////PRVIATE SECTOR + SizedBox( + child: showIsPrivateRadio + ? FormBuilderRadioGroup( + decoration: + InputDecoration( + border: + InputBorder.none, + label: Row( + children: [ + Text( + "Is this private sector? ", + style: Theme.of( + context) + .textTheme + .headlineSmall! + .copyWith( + fontSize: + 24), + ), + const Icon(FontAwesome + .help_circled) + ], + ), + ), + + ////onvhange private sector + onChanged: (value) { + setState(() { + if (value + .toString() == + "YES") { + isPrivate = true; + } else { + isPrivate = false; + } + }); + }, + + name: 'isPrivate', + validator: + FormBuilderValidators + .required( + errorText: + "This field is required"), + options: ["YES", "NO"] + .map((lang) => + FormBuilderFieldOption( + value: + lang)) + .toList( + growable: + false), + ) + : const SizedBox()), + ], + ); + }) + ], + ), + )), + ) + ); + } + return Container(); + }, + ); + } + return Container(); + }, + ); + } + return Container(); + }, + ); + } +} diff --git a/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart b/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart index e4cb52e..06e4b9b 100644 --- a/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart +++ b/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart @@ -4,6 +4,7 @@ import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; +import 'package:unit2/screens/profile/components/other_information/non_academic/add_modal.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; @@ -20,12 +21,16 @@ class NonAcademicRecognitionScreen extends StatelessWidget { @override Widget build(BuildContext context) { + int? profileId; + String? token; return Scaffold( appBar: AppBar( title: const Text(nonAcademicRecTitle), centerTitle: true, backgroundColor: primary, - actions: [AddLeading(onPressed: () {})], + actions: [AddLeading(onPressed: () { + context.read().add(ShowAddNonAcademeRecognitionForm()); + })], ), body: ProgressHUD( padding: const EdgeInsets.all(24), @@ -34,6 +39,8 @@ class NonAcademicRecognitionScreen extends StatelessWidget { child: BlocBuilder( builder: (context, state) { if (state is UserLoggedIn) { + token = state.userData!.user!.login!.token; + profileId = state.userData!.user!.login!.user!.profileId!; return BlocBuilder( builder: (context, state) { if (state is ProfileLoaded) { @@ -45,7 +52,7 @@ class NonAcademicRecognitionScreen extends StatelessWidget { progress!.showWithText("Please wait..."); } if (state is NonAcademicRecognitionLoadedState || - state is NonAcademicRecognitionErrorState) { + state is NonAcademicRecognitionErrorState || state is AddNonAcademeRecognitionState) { final progress = ProgressHUD.of(context); progress!.dismiss(); } @@ -116,6 +123,8 @@ class NonAcademicRecognitionScreen extends StatelessWidget { message: "You don't have any Non Academic Recognition added. Please click + to add"); } + }if(state is AddNonAcademeRecognitionState){ + return const AddNonAcademicRecognitionScreen(); } return Container(); }, diff --git a/lib/screens/profile/components/other_information/org_membership/add_modal.dart b/lib/screens/profile/components/other_information/org_membership/add_modal.dart new file mode 100644 index 0000000..0e019f7 --- /dev/null +++ b/lib/screens/profile/components/other_information/org_membership/add_modal.dart @@ -0,0 +1,373 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:searchfield/searchfield.dart'; +import 'package:unit2/bloc/profile/other_information/org_membership/organization_membership_bloc.dart'; +import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/user/user_bloc.dart'; +import 'package:unit2/model/utils/agency.dart'; +import 'package:unit2/utils/text_container.dart'; + +import '../../../../../model/utils/category.dart'; +import '../../../../../theme-data.dart/box_shadow.dart'; +import '../../../../../theme-data.dart/btn-style.dart'; +import '../../../../../theme-data.dart/colors.dart'; +import '../../../../../theme-data.dart/form-style.dart'; + +class AddOrgMemberShipScreen extends StatefulWidget { + const AddOrgMemberShipScreen({super.key}); + + @override + State createState() => _AddOrgMemberShipScreenState(); +} + +bool showAgencyCategory = false; +final agencyFocusNode = FocusNode(); + +final agencyCategoryFocusNode = FocusNode(); +final addAgencyController = TextEditingController(); +Agency? selectedAgency; +Category? selectedCategory; +Agency? newAgency; +bool showIsPrivateRadio = false; +bool? isPrivate = false; +final _formKey = GlobalKey(); + int? profileId; + String? token; +class _AddOrgMemberShipScreenState extends State { + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is UserLoggedIn) { + token = state.userData!.user!.login!.token; + profileId = state.userData!.user!.login!.user!.profileId; + return BlocBuilder( + builder: (context, state) { + if (state is ProfileLoaded) { + return BlocBuilder( + builder: (context, state) { + if (state is AddOrgMembershipState) { + return SingleChildScrollView( + child: FormBuilder( + key: _formKey, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, horizontal: 18), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.center, + children: [ + const SizedBox( + height: 100, + ), + StatefulBuilder( + builder: (context, setState) { + //// AGENCY SEARCHFIELD + return Column( + children: [ + SearchField( + itemHeight: 70, + suggestions: state.agencies + .map((Agency agency) => + SearchFieldListItem( + agency.name!, + item: agency, + child: ListTile( + title: Text( + agency.name! + .toUpperCase(), + overflow: + TextOverflow + .ellipsis, + ), + subtitle: Text(agency + .privateEntity == + true + ? "Private" + : agency.privateEntity == + false + ? "Government" + : ""), + ))) + .toList(), + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + focusNode: agencyFocusNode, + searchInputDecoration: + normalTextFieldStyle( + "Agency *", "") + .copyWith( + suffixIcon: + const Icon(Icons + .arrow_drop_down)), + ////agency suggestion tap + onSuggestionTap: (agency) { + setState(() { + selectedAgency = agency.item; + agencyFocusNode.unfocus(); + if (selectedAgency + ?.category == + null) { + showAgencyCategory = true; + showIsPrivateRadio = true; + } else { + showAgencyCategory = false; + showIsPrivateRadio = false; + } + }); + }, + emptyWidget: Container( + decoration: box1(), + height: 100, + child: Column( + mainAxisAlignment: + MainAxisAlignment + .center, + crossAxisAlignment: + CrossAxisAlignment + .center, + children: [ + const SizedBox( + height: 20, + ), + const Text( + "No result found..."), + const SizedBox( + height: 10, + ), + TextButton( + //// Add agency onpressed + onPressed: () { + showDialog( + context: + context, + builder: + (BuildContext + context) { + return AlertDialog( + title: const Text( + "Add Agency?"), + content: + SizedBox( + height: + 130, + child: + Column( + children: [ + TextFormField( + controller: + addAgencyController, + decoration: + normalTextFieldStyle("", ""), + ), + const SizedBox( + height: + 12, + ), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + style: mainBtnStyle(primary, Colors.transparent, second), + //// onpressed + onPressed: () { + setState(() { + newAgency = Agency(id: null, name: addAgencyController.text.toUpperCase(), category: null, privateEntity: null); + state.agencies.insert(0, newAgency!); + addAgencyController.clear(); + Navigator.pop(context); + }); + }, + child: const Text("Add"))), + ], + ), + ), + ); + }); + }, + child: const Text( + "Add position")) + ]), + ), + ), + const SizedBox( + height: 8, + ), + SizedBox( + child: showAgencyCategory + ? SearchField( + focusNode: + agencyCategoryFocusNode, + itemHeight: 70, + suggestions: state + .agencyCategories + .map((Category + category) => + SearchFieldListItem( + category + .name!, + item: + category, + child: + ListTile( + title: Text( + category + .name!), + subtitle: Text(category + .industryClass! + .name!), + ))) + .toList(), + emptyWidget: Container( + height: 100, + decoration: box1(), + child: const Center( + child: Text( + "No result found ...")), + ), + ////agency controller suggestion tap + onSuggestionTap: + (agencyCategory) { + setState(() { + selectedCategory = + agencyCategory + .item; + + agencyCategoryFocusNode + .unfocus(); + }); + }, + searchInputDecoration: + normalTextFieldStyle( + "Category *", + "") + .copyWith( + suffixIcon: + const Icon( + Icons + .arrow_drop_down)), + validator: + FormBuilderValidators + .required( + errorText: + "This field is required"), + ) + : const SizedBox(), + ), + + ////PRVIATE SECTOR + SizedBox( + child: showIsPrivateRadio + ? FormBuilderRadioGroup( + decoration: + InputDecoration( + border: + InputBorder.none, + label: Row( + children: [ + Text( + "Is this private sector? ", + style: Theme.of( + context) + .textTheme + .headlineSmall! + .copyWith( + fontSize: + 24), + ), + const Icon(FontAwesome + .help_circled) + ], + ), + ), + + ////onvhange private sector + onChanged: (value) { + setState(() { + if (value + .toString() == + "YES") { + isPrivate = true; + } else { + isPrivate = false; + } + }); + }, + + name: 'isPrivate', + validator: + FormBuilderValidators + .required( + errorText: + "This field is required"), + options: ["YES", "NO"] + .map((lang) => + FormBuilderFieldOption( + value: + lang)) + .toList( + growable: + false), + ) + : const SizedBox()), + ], + ); + }), + ////SHOW CATEGORY AGENCY + + const SizedBox( + height: 24, + ), + SizedBox( + height: 60, + width: double.infinity, + child: ElevatedButton( + style: mainBtnStyle(primary, + Colors.transparent, second), + onPressed: () { + if (_formKey.currentState! + .saveAndValidate()) { + if(selectedAgency?.privateEntity != null){ + newAgency = selectedAgency; + }else{ + newAgency = Agency( + id: selectedAgency?.id, + name: selectedAgency!.name, + category: + selectedCategory, + privateEntity: isPrivate); + } + + context.read().add(AddOrgMembership(agency: newAgency!, profileId: profileId!, token: token!)); + setState(() { + showAgencyCategory = false; + showIsPrivateRadio = false; + }); + } + }, + child: const Text(submit)), + ) + ]), + )), + ); + } + return Container(); + }, + ); + } + return Container(); + }, + ); + } + return const Placeholder(); + }, + ); + } +} diff --git a/lib/screens/profile/components/other_information/org_membership_screen.dart b/lib/screens/profile/components/other_information/org_membership_screen.dart index df8cc9e..7ade0c0 100644 --- a/lib/screens/profile/components/other_information/org_membership_screen.dart +++ b/lib/screens/profile/components/other_information/org_membership_screen.dart @@ -1,16 +1,21 @@ +import 'package:app_popup_menu/app_popup_menu.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/other_information/organization_memberships.dart'; +import 'package:unit2/screens/profile/components/other_information/org_membership/add_modal.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; +import 'package:unit2/widgets/Leadings/close_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; import '../../../../bloc/profile/other_information/org_membership/organization_membership_bloc.dart'; +import '../../../../utils/alerts.dart'; import '../../../../utils/global.dart'; class OrgMembershipsScreen extends StatelessWidget { @@ -18,12 +23,22 @@ class OrgMembershipsScreen extends StatelessWidget { @override Widget build(BuildContext context) { + String? token; + int profileId; return Scaffold( appBar: AppBar( title: const Text(orgMembershipTitle), backgroundColor: primary, centerTitle: true, - actions: [AddLeading(onPressed: () {})], + actions: context.watch().state is OrganizationMembershipLoaded?[ + AddLeading(onPressed: () { + context + .read() + .add(ShowAddOrgMembershipForm()); + }) + ]: context.watch().state is AddOrgMembershipState ?[CloseLeading(onPressed: (){ + context.read().add(const GetOrganizationMembership()); + })]:[] ), body: ProgressHUD( padding: const EdgeInsets.all(24), @@ -32,20 +47,68 @@ class OrgMembershipsScreen extends StatelessWidget { child: BlocBuilder( builder: (context, state) { if (state is UserLoggedIn) { + token = state.userData!.user!.login!.token; + profileId = state.userData!.user!.login!.user!.profileId!; return BlocBuilder( builder: (context, state) { if (state is ProfileLoaded) { return BlocConsumer( listener: (context, state) { - if(state is OrgmembershipLoadingState){ + if (state is OrgmembershipLoadingState) { final progress = ProgressHUD.of(context); progress!.showWithText("Please wait..."); - } - if(state is OrganizationMembershipLoaded || state is OrganizationMembershipErrorState){ + } + if (state is OrganizationMembershipLoaded || + state is OrganizationMembershipErrorState || + state is AddOrgMembershipState || state is OrgMembershipDeletedState) { final progress = ProgressHUD.of(context); progress!.dismiss(); - } + } + ////ADDED STATE + if (state is OrgMembershipAddedState) { + if (state.response['success']) { + successAlert(context, "Adding Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context.read().add( + LoadOrganizationMemberships( + organizationMemberships: + state.orgMemberships)); + }); + } else { + errorAlert(context, "Adding Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context.read().add( + LoadOrganizationMemberships( + organizationMemberships: + state.orgMemberships)); + }); + } + } + ////DELETED STATE + if (state is OrgMembershipDeletedState) { + if (state.success) { + successAlert(context, "Deletion Successfull", + "Work has been deleted successfully", () { + Navigator.of(context).pop(); + context.read().add( + LoadOrganizationMemberships( + organizationMemberships: state.organizationMemberships)); + }); + } else { + errorAlert(context, "Deletion Failed", + "Error deleting Work History", () { + Navigator.of(context).pop(); + context.read().add( + LoadOrganizationMemberships( + organizationMemberships: state.organizationMemberships)); + }); + } + } + }, builder: (context, state) { if (state is OrganizationMembershipLoaded) { @@ -55,8 +118,7 @@ class OrgMembershipsScreen extends StatelessWidget { vertical: 8, horizontal: 10), itemBuilder: (BuildContext context, int index) { String entity = state.orgMemberships[index] - .agency! - .privateEntity == + .agency!.privateEntity == false ? governmentText.toUpperCase() : privateText.toUpperCase(); @@ -95,12 +157,37 @@ class OrgMembershipsScreen extends StatelessWidget { ), ], )), - IconButton( - onPressed: () {}, + AppPopupMenu( + offset: const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + final progress = + ProgressHUD.of(context); + progress! + .showWithText("Loading..."); + ////delete orgmembership-= = = = = = = = =>> + if (value == 1) { + confirmAlert(context, () { + context.read().add(DeleteOrgMemberShip(profileId: profileId, token: token!, org: state.orgMemberships[index], organizationMemberships: state.orgMemberships)); + }, "Delete?", + "Confirm Delete?"); + } + + }, + menuItems: [ + + popMenuItem( + text: "Delete", + value: 1, + icon: Icons.delete), + + ], icon: const Icon( Icons.more_vert, color: Colors.grey, - )) + ), + tooltip: "Options", + ) ]), ), const SizedBox( @@ -110,6 +197,11 @@ class OrgMembershipsScreen extends StatelessWidget { ); }); } + if (state is AddOrgMembershipState) { + return const AddOrgMemberShipScreen(); + }if(state is OrganizationMembershipErrorState){ + return Container(child: Text(state.message),); + } return Container(); }, ); @@ -124,5 +216,24 @@ class OrgMembershipsScreen extends StatelessWidget { )); } } +PopupMenuItem popMenuItem({String? text, int? value, IconData? icon}) { + return PopupMenuItem( + value: value, + child: Row( + children: [ + Icon( + icon, + ), + const SizedBox( + width: 10, + ), + Text( + text!, + ), + ], + ), + ); +} + // const EmptyData(message: "You don't have any Organization Membership added. Please click + to add."), diff --git a/lib/screens/profile/components/work_history/add_modal.dart b/lib/screens/profile/components/work_history/add_modal.dart index d8382e3..3a9be0b 100644 --- a/lib/screens/profile/components/work_history/add_modal.dart +++ b/lib/screens/profile/components/work_history/add_modal.dart @@ -256,7 +256,7 @@ class _AddWorkHistoryScreenState extends State { agency.privateEntity == true ? "Private" - : "Government"), + : agency.privateEntity == false? "Government":""), ))) .toList(), searchInputDecoration: diff --git a/lib/sevices/profile/non_academic_services.dart b/lib/sevices/profile/non_academic_services.dart index 88812c7..46f2b32 100644 --- a/lib/sevices/profile/non_academic_services.dart +++ b/lib/sevices/profile/non_academic_services.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:http/http.dart' as http; +import 'package:unit2/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_bloc.dart'; import 'package:unit2/utils/request.dart'; import '../../model/profile/other_information/non_acedimic_recognition.dart'; @@ -10,7 +11,7 @@ class NonAcademicRecognitionServices { static final NonAcademicRecognitionServices _instance = NonAcademicRecognitionServices(); static NonAcademicRecognitionServices get instance => _instance; - +////GET Future> getNonAcademicRecognition( int profileId, String token) async { List nonAcademicRecognitions = []; @@ -35,7 +36,74 @@ class NonAcademicRecognitionServices { } } catch (e) { throw e.toString(); - } +} return nonAcademicRecognitions; } + +////ADD + Future> add( + {required String token, + required int profileId, + required NonAcademicRecognition nonAcademicRecognition}) async { + String authToken = "Token $token"; + String path = "${Url.instance.getNonAcademicRecognition()}$profileId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + Map body = { + "title": nonAcademicRecognition.title, + "presenter_id": nonAcademicRecognition.presenter?.id, + "_presenterName": nonAcademicRecognition.presenter!.name, + "_presenterCatId": nonAcademicRecognition.presenter!.category!.id, + "_privateEntity": nonAcademicRecognition.presenter!.privateEntity, + }; + + Map statusResponse = {}; + try { + http.Response response = await Request.instance + .postRequest(path: path, body: body, param: {}, headers: headers); + if (response.statusCode == 201) { + Map data = jsonDecode(response.body); + statusResponse = data; + } else { + statusResponse.addAll({"success": false}); + } + } catch (e) { + throw e.toString(); + } + return statusResponse; + } + +////DELETE + + Future delete( + {required String title, + required int id, + required String token, + required int profileId}) async { + String authToken = "Token $token"; + Map params = {"force_mode": "true"}; + String path = "${Url.instance.getNonAcademicRecognition()}$profileId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + bool success = false; + Map body = { + "id": id, + "title": title, + }; + try { + http.Response response = await Request.instance.deleteRequest( + path: path, headers: headers, body: body, param: params); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + success = data['success']; + } + } catch (e) { + throw e.toString(); + } + return success; + } } diff --git a/lib/sevices/profile/orgmembership_services.dart b/lib/sevices/profile/orgmembership_services.dart index 6bf8fd4..424646e 100644 --- a/lib/sevices/profile/orgmembership_services.dart +++ b/lib/sevices/profile/orgmembership_services.dart @@ -5,6 +5,7 @@ import 'package:unit2/utils/request.dart'; import '../../model/profile/other_information/organization_memberships.dart'; import 'package:http/http.dart' as http; +import '../../model/utils/agency.dart'; import '../../utils/urls.dart'; class OrganizationMembershipServices { @@ -39,4 +40,56 @@ class OrganizationMembershipServices { } return orgMemberships; } + Future>add({required Agency? agency,required String token, required String profileId})async{ + String authToken = "Token $token"; + String path = "${Url.instance.getOrgMemberShips()}$profileId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + Map statusResponse = {}; + Map body = { + "agency_id": agency?.id, + "_agencyName": agency!.name, + "_agencyCatId": agency.category!.id, + "_privateEntity": agency.privateEntity + }; + try{ + http.Response response = await Request.instance.postRequest(path: path,body: body,headers: headers,param: {}); + if(response.statusCode == 201){ + Map data = jsonDecode(response.body); + statusResponse = data; + }else{ + statusResponse.addAll({"success":false}); + } + }catch(e){ + throw e.toString(); + } + return statusResponse; + + } + + Future delete({required Agency agency, required int profileId, required String token})async{ + bool success = false; + Map params = {"force_mode": "true"}; + String authToken = "Token $token"; + String path = "${Url.instance.getOrgMemberShips()}$profileId/"; + Map body ={ + "agency_id": agency.id + }; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + try{ + http.Response response = await Request.instance.deleteRequest(path: path, headers: headers, body: body, param: params); + if(response.statusCode == 200){ + Map data = jsonDecode(response.body); + success = data["success"]; + } + }catch(e){ + throw e.toString(); + } + return success; + } } diff --git a/lib/sevices/profile/work_history_services.dart b/lib/sevices/profile/work_history_services.dart index 92906f7..bd0f595 100644 --- a/lib/sevices/profile/work_history_services.dart +++ b/lib/sevices/profile/work_history_services.dart @@ -42,30 +42,6 @@ class WorkHistoryService { return workExperiences; } -//get agencies - Future> getAgecies() async { - List agencies = []; - String path = Url.instance.getAgencies(); - Map headers = { - 'Content-Type': 'application/json; charset=UTF-8', - }; - try { - http.Response response = await Request.instance - .getRequest(path: path, param: {}, headers: headers); - if (response.statusCode == 200) { - Map data = jsonDecode(response.body); - if (data['data'] != null) { - data['data'].forEach((var agency) { - Agency newAgency = Agency.fromJson(agency); - agencies.add(newAgency); - }); - } - } - } catch (e) { - throw e.toString(); - } - return agencies; - } //delete workhistory Future delete( @@ -107,30 +83,6 @@ class WorkHistoryService { return success!; } -//get agency category - Future> agencyCategory() async { - List agencyCategory = []; - String path = Url.instance.getAgencyCategory(); - Map headers = { - 'Content-Type': 'application/json; charset=UTF-8', - }; - try { - http.Response response = await Request.instance - .getRequest(param: {}, path: path, headers: headers); - if (response.statusCode == 200) { - Map data = jsonDecode(response.body); - if (data['data'] != null) { - data['data'].forEach((var agency) { - Category category = Category.fromJson(agency); - agencyCategory.add(category); - }); - } - } - } catch (e) { - throw e.toString(); - } - return agencyCategory; - } //edit work history Future> update({required WorkHistory oldWorkHistory, required WorkHistory newWorkHistory, required String token, required String profileId})async{ diff --git a/lib/utils/profile_utilities.dart b/lib/utils/profile_utilities.dart index 68ef45b..50ec5d3 100644 --- a/lib/utils/profile_utilities.dart +++ b/lib/utils/profile_utilities.dart @@ -7,6 +7,9 @@ import 'package:unit2/model/location/region.dart'; import 'package:unit2/model/utils/eligibility.dart'; import 'package:unit2/utils/request.dart'; import 'package:unit2/utils/urls.dart'; + +import '../model/utils/agency.dart'; +import '../model/utils/category.dart'; class ProfileUtilities { static final ProfileUtilities _instance = ProfileUtilities(); static ProfileUtilities get instance => _instance; @@ -35,4 +38,56 @@ class ProfileUtilities { return eligibilities; } + //get agencies + Future> getAgecies() async { + List agencies = []; + String path = Url.instance.getAgencies(); + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + try { + http.Response response = await Request.instance + .getRequest(path: path, param: {}, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((var agency) { + Agency newAgency = Agency.fromJson(agency); + agencies.add(newAgency); + }); + } + } + } catch (e) { + throw e.toString(); + } + return agencies; + } + + +//get agency category + Future> agencyCategory() async { + List agencyCategory = []; + String path = Url.instance.getAgencyCategory(); + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + try { + http.Response response = await Request.instance + .getRequest(param: {}, path: path, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((var agency) { + Category category = Category.fromJson(agency); + agencyCategory.add(category); + }); + } + } + } catch (e) { + throw e.toString(); + } + return agencyCategory; + } + + } \ No newline at end of file diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index a7972ea..578ab1b 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -4,10 +4,10 @@ class Url { String host() { // return '192.168.10.221:3003'; - return 'agusandelnorte.gov.ph'; + // return 'agusandelnorte.gov.ph'; // return "192.168.10.219:3000"; // return "devweb.agusandelnorte.gov.ph"; - // return 'devapi.agusandelnorte.gov.ph:3004'; + return 'devapi.agusandelnorte.gov.ph:3004'; } String authentication() { From 36bec39fb6037197469dd63bc6702964a44dfab7 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Tue, 28 Mar 2023 16:32:29 +0800 Subject: [PATCH 56/86] Implemented crud operation API for NonAcademic Recognition screen --- .../non_academic_recognition_bloc.dart | 180 ++++++-- .../non_academic_recognition_event.dart | 33 +- .../non_academic_recognition_state.dart | 108 +++-- .../profile/references/references_bloc.dart | 2 +- .../non_academic/add_modal.dart | 43 +- .../non_academic/edit_modal.dart | 410 ++++++++++++++++++ .../non_academic_recognition_screen.dart | 160 ++++++- .../profile/components/references_screen.dart | 5 +- .../profile/non_academic_services.dart | 38 +- pubspec.lock | 8 + pubspec.yaml | 1 + 11 files changed, 880 insertions(+), 108 deletions(-) create mode 100644 lib/screens/profile/components/other_information/non_academic/edit_modal.dart diff --git a/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_bloc.dart b/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_bloc.dart index c4728f8..83a5c1e 100644 --- a/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_bloc.dart +++ b/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_bloc.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:unit2/sevices/profile/non_academic_services.dart'; @@ -7,46 +9,162 @@ import '../../../../model/utils/agency.dart'; import '../../../../model/utils/category.dart'; import '../../../../utils/profile_utilities.dart'; - part 'non_academic_recognition_event.dart'; part 'non_academic_recognition_state.dart'; -class NonAcademicRecognitionBloc extends Bloc { +class NonAcademicRecognitionBloc + extends Bloc { NonAcademicRecognitionBloc() : super(NonAcademicRecognitionInitial()) { - List nonAcademicRecognitions = []; - List agencies = []; - List agencyCategory = []; - ////GET - on((event, emit)async { - emit(NonAcademicRecognitionLoadingState()); - try{ - List recognitions = await NonAcademicRecognitionServices.instance.getNonAcademicRecognition(event.profileId, event.token); - nonAcademicRecognitions = recognitions; - emit(NonAcademicRecognitionLoadedState(nonAcademicRecognition: nonAcademicRecognitions)); - }catch(e){ - emit(NonAcademicRecognitionErrorState(message: e.toString())); - } + List nonAcademicRecognitions = []; + List agencies = []; + List agencyCategory = []; + ////GET + on((event, emit) async { + emit(NonAcademicRecognitionLoadingState()); + try { + if (nonAcademicRecognitions.isEmpty) { + List recognitions = + await NonAcademicRecognitionServices.instance + .getNonAcademicRecognition(event.profileId!, event.token!); + nonAcademicRecognitions = recognitions; + emit(NonAcademicRecognitionLoadedState( + nonAcademicRecognition: nonAcademicRecognitions)); + } else { + emit(NonAcademicRecognitionLoadedState( + nonAcademicRecognition: nonAcademicRecognitions)); + } + } catch (e) { + emit(NonAcademicRecognitionErrorState(message: e.toString())); + } + }); + + ////LOAD + on((event,emit){ + nonAcademicRecognitions = event.nonAcademicRecognitions; + emit(NonAcademicRecognitionLoadedState(nonAcademicRecognition: nonAcademicRecognitions)); }); ////SHOW ADD FORM - on((event, emit)async{ + on( + (event, emit) async { emit(NonAcademicRecognitionLoadingState()); - try{ - if(agencies.isEmpty){ - List newAgencies = await ProfileUtilities.instance.getAgecies(); - agencies = newAgencies; + try { + if (agencies.isEmpty) { + List newAgencies = + await ProfileUtilities.instance.getAgecies(); + agencies = newAgencies; } - if(agencyCategory.isEmpty){ - ListnewAgencyCategories = await ProfileUtilities.instance.agencyCategory(); - agencyCategory = newAgencyCategories; - } - emit(AddNonAcademeRecognitionState(agencies: agencies, agencyCategories: agencyCategory)); - }catch(e){ + if (agencyCategory.isEmpty) { + List newAgencyCategories = + await ProfileUtilities.instance.agencyCategory(); + agencyCategory = newAgencyCategories; + } + emit(AddNonAcademeRecognitionState( + agencies: agencies, agencyCategories: agencyCategory)); + } catch (e) { emit(NonAcademicRecognitionErrorState(message: e.toString())); } - },); - ////ADD - on((event,emit){ - emit(NonAcademicRecognitionLoadingState()); - }); + }, + ); + ////SHOW EDIT FORM + on((event, emit) async { + emit(NonAcademicRecognitionLoadingState()); + try { + if (agencies.isEmpty) { + List newAgencies = + await ProfileUtilities.instance.getAgecies(); + agencies = newAgencies; + } + if (agencyCategory.isEmpty) { + List newAgencyCategories = + await ProfileUtilities.instance.agencyCategory(); + agencyCategory = newAgencyCategories; + } + emit(EditNonAcademeRecognitionState( + agencies: agencies, + agencyCategories: agencyCategory, + nonAcademicRecognition: event.nonAcademicRecognition)); + } catch (e) { + emit(NonAcademicRecognitionErrorState(message: e.toString())); + } + }); + ////ADD + on((event, emit) async { + emit(NonAcademicRecognitionLoadingState()); + try { + Map status = + await NonAcademicRecognitionServices.instance.add( + token: event.token, + profileId: event.profileId, + nonAcademicRecognition: event.nonAcademicRecognition); + if (status['success']) { + NonAcademicRecognition nonAcademicRecognition = + NonAcademicRecognition.fromJson(status['data']); + nonAcademicRecognitions.add(nonAcademicRecognition); + emit(NonAcademeRecognitionAddedState( + response: status, + nonAcademicRecognition: nonAcademicRecognitions)); + } else { + emit(NonAcademeRecognitionAddedState( + response: status, + nonAcademicRecognition: nonAcademicRecognitions)); + } + } catch (e) { + emit(NonAcademicRecognitionErrorState(message: e.toString())); + } + }); +////EDIT + on((event, emit) async { + emit(NonAcademicRecognitionLoadingState()); + try { + Map status = + await NonAcademicRecognitionServices.instance.update( + nonAcademicRecognition: event.nonAcademicRecognition, + profileId: event.profileId, + token: event.token); + if (status['success']) { + NonAcademicRecognition newNonAcademeRecognition = + NonAcademicRecognition.fromJson(status['data']); + nonAcademicRecognitions.removeWhere( + (element) => element.id == event.nonAcademicRecognition.id); + nonAcademicRecognitions.add(newNonAcademeRecognition); + emit(NonAcademeRecognitionEditedState( + nonAcademicRecognitions: nonAcademicRecognitions, + response: status)); + }else{ + emit(NonAcademeRecognitionEditedState( + nonAcademicRecognitions: nonAcademicRecognitions, + response: status)); + } + } catch (e) { + emit(NonAcademicRecognitionErrorState(message: e.toString())); + } + }); + + ////DELETE + on((event, emit) async { + emit(NonAcademicRecognitionLoadingState()); + try { + final bool success = await NonAcademicRecognitionServices.instance + .delete( + title: event.nonAcademicRecognition.title!, + id: event.nonAcademicRecognition.id!, + token: event.token, + profileId: event.profileId); + if (success) { + event.nonAcademicRecognitions.removeWhere( + (element) => element.id == event.nonAcademicRecognition.id); + nonAcademicRecognitions = event.nonAcademicRecognitions; + emit(NonAcademeRecognitionDeletedState( + nonAcademicRecognitions: nonAcademicRecognitions, + success: success)); + } else { + emit(NonAcademeRecognitionDeletedState( + nonAcademicRecognitions: nonAcademicRecognitions, + success: success)); + } + } catch (e) { + emit(NonAcademicRecognitionErrorState(message: e.toString())); + } + }); } } diff --git a/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_event.dart b/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_event.dart index 793ddd3..fdb2c6d 100644 --- a/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_event.dart +++ b/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_event.dart @@ -8,11 +8,9 @@ abstract class NonAcademicRecognitionEvent extends Equatable { } //// GET EVENT class GetNonAcademicRecognition extends NonAcademicRecognitionEvent{ - final int profileId; - final String token; - const GetNonAcademicRecognition({required this.profileId, required this.token}); - @override - List get props => [profileId,token]; + final int? profileId; + final String? token; + const GetNonAcademicRecognition({ this.profileId, this.token}); } ////LOAD EVENT class LoadNonAcademeRecognition extends NonAcademicRecognitionEvent{ @@ -27,6 +25,14 @@ class ShowAddNonAcademeRecognitionForm extends NonAcademicRecognitionEvent{ } +//// SHOW EDIT FORM +class ShowEditNonAcademicRecognitionForm extends NonAcademicRecognitionEvent{ + final NonAcademicRecognition nonAcademicRecognition; + const ShowEditNonAcademicRecognitionForm({required this.nonAcademicRecognition}); + @override + List get props => [nonAcademicRecognition]; +} + //// ADD EVENT class AddNonAcademeRecognition extends NonAcademicRecognitionEvent{ final int profileId; @@ -35,18 +41,27 @@ class AddNonAcademeRecognition extends NonAcademicRecognitionEvent{ const AddNonAcademeRecognition({required this.nonAcademicRecognition, required this.profileId, required this.token}); @override List get props => [nonAcademicRecognition,profileId,token]; +} +//// EDIT EVENT +class EditNonAcademeRecognition extends NonAcademicRecognitionEvent{ + final NonAcademicRecognition nonAcademicRecognition; + final String token; + final int profileId; + const EditNonAcademeRecognition({required this.nonAcademicRecognition, required this.profileId, required this.token}); + @override + List get props => [nonAcademicRecognition,profileId,token]; } //// DELETE EVENT class DeleteNonAcademeRecognition extends NonAcademicRecognitionEvent{ final int profileId; final String token; - final int id; - final String title; - const DeleteNonAcademeRecognition({required this.id,required this.profileId, required this.title,required this.token}); + final List nonAcademicRecognitions; + final NonAcademicRecognition nonAcademicRecognition; + const DeleteNonAcademeRecognition({required this.nonAcademicRecognitions, required this.nonAcademicRecognition,required this.profileId,required this.token}); @override - List get props => [profileId,token,id,title]; + List get props => [profileId,token,nonAcademicRecognition,nonAcademicRecognitions]; } diff --git a/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_state.dart b/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_state.dart index dbb737b..f09e17e 100644 --- a/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_state.dart +++ b/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_state.dart @@ -2,60 +2,78 @@ part of 'non_academic_recognition_bloc.dart'; abstract class NonAcademicRecognitionState extends Equatable { const NonAcademicRecognitionState(); - + @override List get props => []; } class NonAcademicRecognitionInitial extends NonAcademicRecognitionState {} - ////LOADING STATE -class NonAcademicRecognitionLoadingState extends NonAcademicRecognitionState{ +class NonAcademicRecognitionLoadingState extends NonAcademicRecognitionState {} -} ////LOADED STATE -class NonAcademicRecognitionLoadedState extends NonAcademicRecognitionState{ - final List nonAcademicRecognition; - const NonAcademicRecognitionLoadedState({required this.nonAcademicRecognition}); - @override - List get props => []; -} -////DELETED STATE -class NonAcademeRecognitionDeletedState extends NonAcademicRecognitionState{ - final List nonAcademicRecognition; - final bool success; - const NonAcademeRecognitionDeletedState({required this.nonAcademicRecognition, required this.success}); - @override - List get props => [nonAcademicRecognition,success]; -} - -////ADDED STATE -class NonAcademeRecognitionAddedState extends NonAcademicRecognitionState{ - final List nonAcademicRecognition; - final Map response; - const NonAcademeRecognitionAddedState({required this.nonAcademicRecognition, required this.response}); - @override - List get props => [nonAcademicRecognition,response]; -} - -////ADDING STATE -class AddNonAcademeRecognitionState extends NonAcademicRecognitionState{ - final List agencies; -final List agencyCategories; -const AddNonAcademeRecognitionState({required this.agencies, required this.agencyCategories}); - @override - List get props => [agencies,agencyCategories]; - -} - - - -////ERROR STATE -class NonAcademicRecognitionErrorState extends NonAcademicRecognitionState{ - final String message; - const NonAcademicRecognitionErrorState({required this.message}); - +class NonAcademicRecognitionLoadedState extends NonAcademicRecognitionState { + final List nonAcademicRecognition; + const NonAcademicRecognitionLoadedState( + {required this.nonAcademicRecognition}); + @override + List get props => []; +} + +////DELETED STATE +class NonAcademeRecognitionDeletedState extends NonAcademicRecognitionState { + final List nonAcademicRecognitions; + final bool success; + const NonAcademeRecognitionDeletedState( + {required this.nonAcademicRecognitions, required this.success}); + @override + List get props => [nonAcademicRecognitions, success]; +} + +class NonAcademeRecognitionEditedState extends NonAcademicRecognitionState{ + final List nonAcademicRecognitions; + final Map response; + const NonAcademeRecognitionEditedState( + {required this.nonAcademicRecognitions, required this.response}); + @override + List get props => [nonAcademicRecognitions, response]; +} + +////ADDED STATE +class NonAcademeRecognitionAddedState extends NonAcademicRecognitionState { + final List nonAcademicRecognition; + final Map response; + const NonAcademeRecognitionAddedState( + {required this.nonAcademicRecognition, required this.response}); + @override + List get props => [nonAcademicRecognition, response]; +} + +////ADDING STATE +class AddNonAcademeRecognitionState extends NonAcademicRecognitionState { + final List agencies; + final List agencyCategories; + const AddNonAcademeRecognitionState( + {required this.agencies, required this.agencyCategories}); + @override + List get props => [agencies, agencyCategories]; +} + +class EditNonAcademeRecognitionState extends NonAcademicRecognitionState { + final List agencies; + final List agencyCategories; + final NonAcademicRecognition nonAcademicRecognition; + const EditNonAcademeRecognitionState({required this.agencies, required this.agencyCategories,required this.nonAcademicRecognition}); + @override + List get props => [agencies, agencyCategories,nonAcademicRecognition]; +} + +////ERROR STATE +class NonAcademicRecognitionErrorState extends NonAcademicRecognitionState { + final String message; + const NonAcademicRecognitionErrorState({required this.message}); + @override List get props => []; } diff --git a/lib/bloc/profile/references/references_bloc.dart b/lib/bloc/profile/references/references_bloc.dart index 78f089b..5c2b2f8 100644 --- a/lib/bloc/profile/references/references_bloc.dart +++ b/lib/bloc/profile/references/references_bloc.dart @@ -142,7 +142,7 @@ class ReferencesBloc extends Bloc { on((event, emit) async { emit(const ReferencesErrorState( message: "Something went wrong. Please try again")); - //// ADD REFERENCES EVENT + //// EDIT REFERENCES EVENT });on((event,emit)async{ emit(ReferencesLoadingState()); Map status =await ReferencesServices.instace.update(ref: event.reference, token: event.token, profileId: event.profileId); diff --git a/lib/screens/profile/components/other_information/non_academic/add_modal.dart b/lib/screens/profile/components/other_information/non_academic/add_modal.dart index 856f105..08793bd 100644 --- a/lib/screens/profile/components/other_information/non_academic/add_modal.dart +++ b/lib/screens/profile/components/other_information/non_academic/add_modal.dart @@ -1,6 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:fluttericon/font_awesome_icons.dart'; @@ -18,6 +16,7 @@ import '../../../../../model/utils/category.dart'; import '../../../../../theme-data.dart/box_shadow.dart'; import '../../../../../theme-data.dart/btn-style.dart'; import '../../../../../theme-data.dart/colors.dart'; +import '../../../../../utils/text_container.dart'; class AddNonAcademicRecognitionScreen extends StatefulWidget { const AddNonAcademicRecognitionScreen({super.key}); @@ -48,9 +47,12 @@ class _AddNonAcademicRecognitionScreenState return BlocBuilder( builder: (context, state) { if (state is UserLoggedIn) { + token = state.userData!.user!.login!.token; + profileId = state.userData!.user!.login!.user!.profileId!; return BlocBuilder( builder: (context, state) { if (state is ProfileLoaded) { + return BlocBuilder( builder: (context, state) { @@ -58,7 +60,9 @@ class _AddNonAcademicRecognitionScreenState return SizedBox( height: blockSizeVertical * 90, child: SingleChildScrollView( - child: FormBuilder(child: Padding( + child: FormBuilder( + key: _formKey, + child: Padding( padding: const EdgeInsets.symmetric(vertical: 25,horizontal: 18), child: Column( children: [ @@ -321,7 +325,38 @@ class _AddNonAcademicRecognitionScreenState : const SizedBox()), ], ); - }) + }), + const SizedBox( + height: 24, + ), + SizedBox( + height: 60, + width: double.infinity, + child: ElevatedButton( + style: mainBtnStyle(primary, + Colors.transparent, second), + onPressed: () { + + if (_formKey.currentState! + .saveAndValidate()) { + String title = _formKey.currentState!.value['title']; + + if(selectedAgency?.privateEntity != null){ + newAgency = selectedAgency; + }else{ + newAgency = Agency( + id: selectedAgency?.id, + name: selectedAgency!.name, + category: + selectedCategory, + privateEntity: isPrivate); + } + nonAcademicRecognition = NonAcademicRecognition(id: null, title:title,presenter: newAgency ); + context.read().add(AddNonAcademeRecognition(nonAcademicRecognition: nonAcademicRecognition!, profileId: profileId!, token: token!)); + } + }, + child: const Text(submit)), + ) ], ), )), diff --git a/lib/screens/profile/components/other_information/non_academic/edit_modal.dart b/lib/screens/profile/components/other_information/non_academic/edit_modal.dart new file mode 100644 index 0000000..3d43d3c --- /dev/null +++ b/lib/screens/profile/components/other_information/non_academic/edit_modal.dart @@ -0,0 +1,410 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:searchfield/searchfield.dart'; +import 'package:unit2/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_bloc.dart'; +import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/user/user_bloc.dart'; +import 'package:unit2/model/profile/other_information/non_acedimic_recognition.dart'; +import 'package:unit2/theme-data.dart/form-style.dart'; +import 'package:unit2/utils/global.dart'; + +import '../../../../../model/utils/agency.dart'; +import '../../../../../model/utils/category.dart'; +import '../../../../../theme-data.dart/box_shadow.dart'; +import '../../../../../theme-data.dart/btn-style.dart'; +import '../../../../../theme-data.dart/colors.dart'; +import '../../../../../utils/text_container.dart'; + +class EditNonAcademicRecognitionScreen extends StatefulWidget { + const EditNonAcademicRecognitionScreen({super.key}); + + @override + State createState() => + _EditNonAcademicRecognitionScreenState(); +} + +class _EditNonAcademicRecognitionScreenState + extends State { + bool showAgencyCategory = false; + final agencyFocusNode = FocusNode(); + + final agencyCategoryFocusNode = FocusNode(); + final addAgencyController = TextEditingController(); + Agency? selectedAgency; + Category? selectedCategory; + Agency? newAgency; + bool showIsPrivateRadio = false; + bool? isPrivate = false; + NonAcademicRecognition? nonAcademicRecognition; + final oldAgencyController = TextEditingController(); + final _formKey = GlobalKey(); + int? profileId; + String? token; + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is UserLoggedIn) { + token = state.userData!.user!.login!.token; + profileId = state.userData!.user!.login!.user!.profileId!; + return BlocBuilder( + builder: (context, state) { + if (state is ProfileLoaded) { + return BlocBuilder( + builder: (context, state) { + if (state is EditNonAcademeRecognitionState) { + oldAgencyController.text = + state.nonAcademicRecognition.presenter!.name!; + selectedAgency = state.nonAcademicRecognition.presenter; + selectedCategory = + state.nonAcademicRecognition.presenter!.category; + return SizedBox( + height: blockSizeVertical * 90, + child: SingleChildScrollView( + child: FormBuilder( + key: _formKey, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, horizontal: 18), + child: Column( + children: [ + FormBuilderTextField( + name: 'title', + initialValue: + state.nonAcademicRecognition.title, + decoration: normalTextFieldStyle( + "Recognition / Award Title *", + "Recognition / Award Title"), + validator: + FormBuilderValidators.required( + errorText: + "this field is required"), + ), + const SizedBox( + height: 12, + ), + StatefulBuilder( + builder: (context, setState) { + //// AGENCY SEARCHFIELD + return Column( + children: [ + SearchField( + controller: oldAgencyController, + itemHeight: 70, + suggestions: state.agencies + .map((Agency agency) => + SearchFieldListItem( + agency.name!, + item: agency, + child: ListTile( + title: Text( + agency.name! + .toUpperCase(), + overflow: + TextOverflow + .ellipsis, + ), + subtitle: Text(agency + .privateEntity == + true + ? "Private" + : agency.privateEntity == + false + ? "Government" + : ""), + ))) + .toList(), + + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + focusNode: agencyFocusNode, + searchInputDecoration: + normalTextFieldStyle( + "Agency *", "") + .copyWith( + suffixIcon: + const Icon(Icons + .arrow_drop_down)), + ////agency suggestion tap + onSuggestionTap: (agency) { + setState(() { + selectedAgency = agency.item; + agencyFocusNode.unfocus(); + if (selectedAgency + ?.category == + null) { + showAgencyCategory = true; + showIsPrivateRadio = true; + } else { + showAgencyCategory = false; + showIsPrivateRadio = false; + } + }); + }, + emptyWidget: Container( + decoration: box1(), + height: 100, + child: Column( + mainAxisAlignment: + MainAxisAlignment + .center, + crossAxisAlignment: + CrossAxisAlignment + .center, + children: [ + const SizedBox( + height: 20, + ), + const Text( + "No result found..."), + const SizedBox( + height: 10, + ), + TextButton( + //// Add agency onpressed + onPressed: () { + showDialog( + context: + context, + builder: + (BuildContext + context) { + return AlertDialog( + title: const Text( + "Add Agency?"), + content: + SizedBox( + height: + 130, + child: + Column( + children: [ + TextFormField( + controller: + addAgencyController, + decoration: + normalTextFieldStyle("", ""), + ), + const SizedBox( + height: + 12, + ), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + style: mainBtnStyle(primary, Colors.transparent, second), + //// onpressed + onPressed: () { + setState(() { + newAgency = Agency(id: null, name: addAgencyController.text.toUpperCase(), category: null, privateEntity: null); + state.agencies.insert(0, newAgency!); + addAgencyController.clear(); + Navigator.pop(context); + }); + }, + child: const Text("Add"))), + ], + ), + ), + ); + }); + }, + child: const Text( + "Add position")) + ]), + ), + ), + const SizedBox( + height: 8, + ), + SizedBox( + child: showAgencyCategory + ? SearchField( + focusNode: + agencyCategoryFocusNode, + itemHeight: 70, + suggestions: state + .agencyCategories + .map((Category + category) => + SearchFieldListItem( + category + .name!, + item: + category, + child: + ListTile( + title: Text( + category + .name!), + subtitle: Text(category + .industryClass! + .name!), + ))) + .toList(), + emptyWidget: Container( + height: 100, + decoration: box1(), + child: const Center( + child: Text( + "No result found ...")), + ), + ////agency controller suggestion tap + onSuggestionTap: + (agencyCategory) { + setState(() { + selectedCategory = + agencyCategory + .item; + + agencyCategoryFocusNode + .unfocus(); + }); + }, + searchInputDecoration: + normalTextFieldStyle( + "Category *", + "") + .copyWith( + suffixIcon: + const Icon( + Icons + .arrow_drop_down)), + validator: + FormBuilderValidators + .required( + errorText: + "This field is required"), + ) + : const SizedBox(), + ), + + ////PRVIATE SECTOR + SizedBox( + child: showIsPrivateRadio + ? FormBuilderRadioGroup( + decoration: + InputDecoration( + border: + InputBorder.none, + label: Row( + children: [ + Text( + "Is this private sector? ", + style: Theme.of( + context) + .textTheme + .headlineSmall! + .copyWith( + fontSize: + 24), + ), + const Icon(FontAwesome + .help_circled) + ], + ), + ), + + ////onvhange private sector + onChanged: (value) { + setState(() { + if (value + .toString() == + "YES") { + isPrivate = true; + } else { + isPrivate = false; + } + }); + }, + + name: 'isPrivate', + validator: + FormBuilderValidators + .required( + errorText: + "This field is required"), + options: ["YES", "NO"] + .map((lang) => + FormBuilderFieldOption( + value: + lang)) + .toList( + growable: + false), + ) + : const SizedBox()), + ], + ); + }), + const SizedBox( + height: 24, + ), + SizedBox( + height: 60, + width: double.infinity, + child: ElevatedButton( + style: mainBtnStyle(primary, + Colors.transparent, second), + onPressed: () { + if (_formKey.currentState! + .saveAndValidate()) { + String title = _formKey + .currentState! + .value['title']; + + if (selectedAgency + ?.privateEntity != + null) { + newAgency = selectedAgency; + } else { + newAgency = Agency( + id: selectedAgency?.id, + name: + selectedAgency!.name, + category: + selectedCategory, + privateEntity: isPrivate); + } + nonAcademicRecognition = + NonAcademicRecognition( + id: state.nonAcademicRecognition.id, + title: title, + presenter: newAgency); + context + .read< + NonAcademicRecognitionBloc>() + .add(EditNonAcademeRecognition( + nonAcademicRecognition: + nonAcademicRecognition!, + profileId: profileId!, + token: token!)); + } + }, + child: const Text(submit)), + ) + ], + ), + )), + )); + } + return Container(); + }, + ); + } + return Container(); + }, + ); + } + return Container(); + }, + ); + } +} diff --git a/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart b/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart index 06e4b9b..56bac7f 100644 --- a/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart +++ b/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart @@ -1,3 +1,4 @@ +import 'package:app_popup_menu/app_popup_menu.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; @@ -5,6 +6,7 @@ import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/screens/profile/components/other_information/non_academic/add_modal.dart'; +import 'package:unit2/screens/profile/components/other_information/non_academic/edit_modal.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; @@ -13,6 +15,7 @@ import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; import '../../../../bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_bloc.dart'; +import '../../../../utils/alerts.dart'; class NonAcademicRecognitionScreen extends StatelessWidget { const NonAcademicRecognitionScreen({ @@ -28,9 +31,13 @@ class NonAcademicRecognitionScreen extends StatelessWidget { title: const Text(nonAcademicRecTitle), centerTitle: true, backgroundColor: primary, - actions: [AddLeading(onPressed: () { - context.read().add(ShowAddNonAcademeRecognitionForm()); - })], + actions: [ + AddLeading(onPressed: () { + context + .read() + .add(ShowAddNonAcademeRecognitionForm()); + }) + ], ), body: ProgressHUD( padding: const EdgeInsets.all(24), @@ -39,7 +46,7 @@ class NonAcademicRecognitionScreen extends StatelessWidget { child: BlocBuilder( builder: (context, state) { if (state is UserLoggedIn) { - token = state.userData!.user!.login!.token; + token = state.userData!.user!.login!.token; profileId = state.userData!.user!.login!.user!.profileId!; return BlocBuilder( builder: (context, state) { @@ -52,10 +59,72 @@ class NonAcademicRecognitionScreen extends StatelessWidget { progress!.showWithText("Please wait..."); } if (state is NonAcademicRecognitionLoadedState || - state is NonAcademicRecognitionErrorState || state is AddNonAcademeRecognitionState) { + state is NonAcademicRecognitionErrorState || + state is AddNonAcademeRecognitionState || state is EditNonAcademeRecognitionState || state is NonAcademeRecognitionEditedState) { final progress = ProgressHUD.of(context); progress!.dismiss(); } + ////ADDED STATE + if (state is NonAcademeRecognitionAddedState) { + if (state.response['success']) { + successAlert(context, "Adding Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context + .read() + .add(const GetNonAcademicRecognition()); + }); + } else { + errorAlert(context, "Adding Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context + .read() + .add(const GetNonAcademicRecognition()); + }); + } + } + ////DELETED STATE + if (state is NonAcademeRecognitionDeletedState) { + if (state.success) { + successAlert(context, "Deletion Successfull", + "Work has been deleted successfully", () { + Navigator.of(context).pop(); + context + .read() + .add(const GetNonAcademicRecognition()); + }); + } else { + errorAlert(context, "Deletion Failed", + "Error deleting Work History", () { + Navigator.of(context).pop(); + context + .read() + .add(const GetNonAcademicRecognition()); + }); + } + } + ////EDITED STATE + if (state is NonAcademeRecognitionEditedState) { + if (state.response['success']) { + successAlert(context, "Update Successfull", + state.response['message'], () { + Navigator.of(context).pop(); + context + .read() + .add( LoadNonAcademeRecognition(nonAcademicRecognitions: state.nonAcademicRecognitions)); + }); + } else { + errorAlert(context, "Update Failed", + state.response['message'], () { + Navigator.of(context).pop(); + context + .read() + .add(LoadNonAcademeRecognition(nonAcademicRecognitions: state.nonAcademicRecognitions)); + }); + } + } }, builder: (context, state) { if (state is NonAcademicRecognitionLoadedState) { @@ -103,12 +172,57 @@ class NonAcademicRecognitionScreen extends StatelessWidget { Text(presenter), ], )), - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - )) + AppPopupMenu( + offset: const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + + ////delete non academic recognition-= = = = = = = = =>> + if (value == 1) { + confirmAlert(context, () { + context + .read< + NonAcademicRecognitionBloc>() + .add(DeleteNonAcademeRecognition( + nonAcademicRecognition: + state + .nonAcademicRecognition[ + index], + profileId: + profileId!, + nonAcademicRecognitions: + state + .nonAcademicRecognition, + token: token!)); + }, "Delete?", + "Confirm Delete?"); + } + if (value == 2) { + context + .read< + NonAcademicRecognitionBloc>() + .add(ShowEditNonAcademicRecognitionForm( + nonAcademicRecognition: + state.nonAcademicRecognition[ + index])); + } + }, + menuItems: [ + popMenuItem( + text: "Delete", + value: 1, + icon: Icons.delete), + popMenuItem( + text: "Edit", + value: 2, + icon: Icons.delete), + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ) ], ), ), @@ -123,8 +237,11 @@ class NonAcademicRecognitionScreen extends StatelessWidget { message: "You don't have any Non Academic Recognition added. Please click + to add"); } - }if(state is AddNonAcademeRecognitionState){ + } + if (state is AddNonAcademeRecognitionState) { return const AddNonAcademicRecognitionScreen(); + }if(state is EditNonAcademeRecognitionState){ + return const EditNonAcademicRecognitionScreen(); } return Container(); }, @@ -140,3 +257,22 @@ class NonAcademicRecognitionScreen extends StatelessWidget { )); } } + +PopupMenuItem popMenuItem({String? text, int? value, IconData? icon}) { + return PopupMenuItem( + value: value, + child: Row( + children: [ + Icon( + icon, + ), + const SizedBox( + width: 10, + ), + Text( + text!, + ), + ], + ), + ); +} diff --git a/lib/screens/profile/components/references_screen.dart b/lib/screens/profile/components/references_screen.dart index b7eb2f4..495bf3a 100644 --- a/lib/screens/profile/components/references_screen.dart +++ b/lib/screens/profile/components/references_screen.dart @@ -212,10 +212,7 @@ class ReferencesScreen extends StatelessWidget { offset: const Offset(-10, -10), elevation: 3, onSelected: (value) { - final progress = - ProgressHUD.of(context); - progress!.showWithText( - "Loading..."); + ////delete eligibilty-= = = = = = = = =>> if (value == 2) { confirmAlert(context, () { diff --git a/lib/sevices/profile/non_academic_services.dart b/lib/sevices/profile/non_academic_services.dart index 46f2b32..fb08bf8 100644 --- a/lib/sevices/profile/non_academic_services.dart +++ b/lib/sevices/profile/non_academic_services.dart @@ -36,7 +36,7 @@ class NonAcademicRecognitionServices { } } catch (e) { throw e.toString(); -} + } return nonAcademicRecognitions; } @@ -75,8 +75,42 @@ class NonAcademicRecognitionServices { return statusResponse; } -////DELETE + ////UPDATE + Future> update( + {required NonAcademicRecognition nonAcademicRecognition, + required int profileId, + required String token}) async { + String authToken = "Token $token"; + String path = "${Url.instance.getNonAcademicRecognition()}$profileId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + Map statusResponse = {}; + Map body = { + "id": nonAcademicRecognition.id, + "title": nonAcademicRecognition.title, + "presenter_id": nonAcademicRecognition.presenter?.id, + "_presenterName": nonAcademicRecognition.presenter!.name, + "_presenterCatId": nonAcademicRecognition.presenter!.category!.id, + "_privateEntity": nonAcademicRecognition.presenter!.privateEntity + }; + try { + http.Response response = await Request.instance + .putRequest(path: path, headers: headers, body: body, param: {}); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + statusResponse = data; + } else { + statusResponse.addAll({'success': false}); + } + } catch (e) { + throw e.toString(); + } + return statusResponse; + } +////DELETE Future delete( {required String title, required int id, diff --git a/pubspec.lock b/pubspec.lock index 2a90114..e2e4eda 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -257,6 +257,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + filter_list: + dependency: "direct main" + description: + name: filter_list + sha256: "2d80d6d19beb7847c1176e8bf6fe06d302b23eb7d1bf48c231dd730409ff9b4d" + url: "https://pub.dev" + source: hosted + version: "1.0.2" fixnum: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index c2bbe76..ddcc03c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -71,6 +71,7 @@ dependencies: app_popup_menu: ^1.0.0 modal_progress_hud_nsn: ^0.3.0 searchfield: ^0.7.5 + filter_list: ^1.0.2 dev_dependencies: From e686cd1f9f5a8dc0f061f12a7baf959301a7b2ad Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Thu, 30 Mar 2023 14:22:16 +0800 Subject: [PATCH 57/86] Implemented crud API for skills and hobbies screen --- .../hobbies/hoobies_bloc.dart | 109 +++++++- .../hobbies/hoobies_event.dart | 36 +++ .../hobbies/hoobies_state.dart | 26 ++ .../skills_and_hobbies_screen.dart | 234 ++++++++++++++++-- .../skills_hobbies/add_modal.dart | 73 ++++++ .../profile/work_history_services.dart | 14 +- lib/sevices/skillshobbies_services.dart | 128 ++++++++-- lib/utils/urls.dart | 9 +- pubspec.lock | 8 + pubspec.yaml | 1 + 10 files changed, 577 insertions(+), 61 deletions(-) create mode 100644 lib/screens/profile/components/other_information/skills_hobbies/add_modal.dart diff --git a/lib/bloc/profile/other_information/hobbies/hoobies_bloc.dart b/lib/bloc/profile/other_information/hobbies/hoobies_bloc.dart index 3ad0c7e..b612e61 100644 --- a/lib/bloc/profile/other_information/hobbies/hoobies_bloc.dart +++ b/lib/bloc/profile/other_information/hobbies/hoobies_bloc.dart @@ -10,15 +10,106 @@ part 'hoobies_state.dart'; class HoobiesBloc extends Bloc { HoobiesBloc() : super(HoobiesInitial()) { List skillsAndHobbies = []; - on((event, emit)async { - emit(HobbiesLoadingState()); - try{ - List hobbies = await SkillsHobbiesServices.instance.getSkillsHobbies(event.profileId, event.token); - skillsAndHobbies = hobbies; - emit(HobbiesLoadedState(skillsAndHobbies: skillsAndHobbies)); - }catch(e){ - emit(HobbiesErrorState(message: e.toString())); - } + List allSkillsAndHobbies = []; + List mySkillsAndHobbies = []; + on((event, emit) async { + emit(HobbiesLoadingState()); + try { + List hobbies = await SkillsHobbiesServices.instance + .getSkillsHobbies(event.profileId, event.token); + skillsAndHobbies = hobbies; + emit(HobbiesLoadedState(skillsAndHobbies: skillsAndHobbies)); + } catch (e) { + emit(HobbiesErrorState(message: e.toString())); + } + }); + on((event,emit){ + skillsAndHobbies = event.skillsHobbies; + emit(HobbiesLoadedState(skillsAndHobbies: skillsAndHobbies)); + }); + ////SHOW ADD FORM + on((event, emit) async { + emit(HobbiesLoadingState()); + try { + if (allSkillsAndHobbies.isEmpty) { + allSkillsAndHobbies = + await SkillsHobbiesServices.instance.getAllSkillsHobbies(); + } + for (var element in skillsAndHobbies) { + mySkillsAndHobbies.add(element.name!); + } + allSkillsAndHobbies.sort((a, b) => a.name!.compareTo(b.name!)); + emit(AddHobbySkillState( + mySkillsAndHobbiesString: mySkillsAndHobbies, + allSkillsAndHobbies: allSkillsAndHobbies, + mySkillsAndHobbiesObject: skillsAndHobbies)); + } catch (e) { + emit(HobbiesErrorState(message: e.toString())); + } + }); + ////GET ADDED SKILLS HOBBIES + on((event, emit) { + emit(HobbiesLoadingState()); + List added = event.addedHobbiesSkills.split(","); + + for (var element in added) { + if (element.isNotEmpty) { + SkillsHobbies newSkillsHobbies = + SkillsHobbies(id: null, name: element.toUpperCase()); + skillsAndHobbies.add(newSkillsHobbies); + allSkillsAndHobbies.add(newSkillsHobbies); + } + } + for (var element in skillsAndHobbies) { + mySkillsAndHobbies.add(element.name!); + } + allSkillsAndHobbies.sort((a, b) => a.name!.compareTo(b.name!)); + emit(AddHobbySkillState( + mySkillsAndHobbiesString: mySkillsAndHobbies, + allSkillsAndHobbies: allSkillsAndHobbies, + mySkillsAndHobbiesObject: skillsAndHobbies)); + }); + ////SHOW ADD MODAL + on((event, emit) { + emit(ShowAddModalState()); + }); + + //// ADD + on((event, emit) async { + emit(HobbiesLoadingState()); + Map response = await SkillsHobbiesServices.instance.add( + skillsHobbies: event.skillsHobbies, + profileId: event.profileId, + token: event.token); + List newSkillsHobbies = []; + if (response['success']) { + if (response['data']['skill_hobby'] != null) { + for (var element in response['data']['skill_hobby']) { + newSkillsHobbies.add(SkillsHobbies.fromJson(element)); + } + } + skillsAndHobbies = newSkillsHobbies; + emit(HobbiesAndSkillsAddedState( + mySkillsAndHobbies: skillsAndHobbies, status: response)); + } else { + emit(HobbiesAndSkillsAddedState( + mySkillsAndHobbies: skillsAndHobbies, status: response)); + } + }); + ////DELETE + on((event,emit)async{ + emit(HobbiesLoadingState()); + try{ + bool success = await SkillsHobbiesServices.instance.delete(profileId: event.profileId, token: event.token, skillsHobbies: event.skillsHobbies); + if(success){ + skillsAndHobbies.removeWhere((element) => element.id == event.skillsHobbies[0].id); + emit(HobbiesAndSkillsDeletedState(skillsHobbies: skillsAndHobbies, success: success)); + }else{ + emit(HobbiesAndSkillsDeletedState(skillsHobbies: skillsAndHobbies, success: success)); + } + }catch(e){ + emit (HobbiesErrorState(message: e.toString())); + } }); } } diff --git a/lib/bloc/profile/other_information/hobbies/hoobies_event.dart b/lib/bloc/profile/other_information/hobbies/hoobies_event.dart index a78f0f9..5a718af 100644 --- a/lib/bloc/profile/other_information/hobbies/hoobies_event.dart +++ b/lib/bloc/profile/other_information/hobbies/hoobies_event.dart @@ -13,4 +13,40 @@ class GetSkillsHobbies extends HobbiesEvent{ const GetSkillsHobbies({required this.profileId, required this.token}); @override List get props => [profileId,token]; +} +class ShowHobbySkillAddForm extends HobbiesEvent{ + final List mySkillsAndHobbies; + const ShowHobbySkillAddForm({required this.mySkillsAndHobbies}); + +} +class AddHobbyAndSkills extends HobbiesEvent{ + final int profileId; + final String token; + final List skillsHobbies; + const AddHobbyAndSkills({required this.profileId,required this.token, required this.skillsHobbies}); + @override + List get props => [profileId,token]; +} + +class GetAddedHobbiesSkills extends HobbiesEvent{ + final String addedHobbiesSkills; + + + const GetAddedHobbiesSkills({required this.addedHobbiesSkills}); + @override + List get props => [addedHobbiesSkills]; +} +class ShowAddModal extends HobbiesEvent{ + +} + +class LoadHobbiesSkills extends HobbiesEvent{ + final List skillsHobbies; + const LoadHobbiesSkills({required this.skillsHobbies}); +} +class DeleteSkillHobbies extends HobbiesEvent{ + final int profileId; + final String token; + final List skillsHobbies; + const DeleteSkillHobbies({required this.profileId, required this.skillsHobbies, required this.token}); } \ No newline at end of file diff --git a/lib/bloc/profile/other_information/hobbies/hoobies_state.dart b/lib/bloc/profile/other_information/hobbies/hoobies_state.dart index c374892..a2d39ac 100644 --- a/lib/bloc/profile/other_information/hobbies/hoobies_state.dart +++ b/lib/bloc/profile/other_information/hobbies/hoobies_state.dart @@ -23,7 +23,33 @@ class HobbiesErrorState extends HobbiesState{ List get props => [message]; } +class AddHobbySkillState extends HobbiesState{ + final List mySkillsAndHobbiesString; + final List allSkillsAndHobbies; + final List mySkillsAndHobbiesObject; + + const AddHobbySkillState({required this.mySkillsAndHobbiesString,required this.allSkillsAndHobbies,required this.mySkillsAndHobbiesObject}); + @override + List get props => [mySkillsAndHobbiesString,allSkillsAndHobbies,mySkillsAndHobbiesObject]; +} class HobbiesLoadingState extends HobbiesState{ } +class ShowAddModalState extends HobbiesState{ + +} +class HobbiesAndSkillsAddedState extends HobbiesState{ + final List mySkillsAndHobbies; + final Map status; + const HobbiesAndSkillsAddedState({required this.mySkillsAndHobbies, required this.status}); + @override + List get props => [mySkillsAndHobbies,status]; +} +class HobbiesAndSkillsDeletedState extends HobbiesState{ + final bool success; + final List skillsHobbies; + const HobbiesAndSkillsDeletedState({required this.skillsHobbies,required this.success}); +} + + diff --git a/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart b/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart index 1ea1de4..7096b37 100644 --- a/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart +++ b/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart @@ -4,9 +4,12 @@ import 'package:flutter/src/widgets/placeholder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:simple_chips_input/simple_chips_input.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/other_information/skills_and_hobbies.dart'; +import 'package:unit2/screens/profile/components/other_information/skills_hobbies/add_modal.dart'; +import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; @@ -14,19 +17,35 @@ import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; import '../../../../bloc/profile/other_information/hobbies/hoobies_bloc.dart'; +import '../../../../theme-data.dart/btn-style.dart'; +import '../../../../utils/alerts.dart'; class SkillHobbiesScreen extends StatelessWidget { const SkillHobbiesScreen({super.key}); @override Widget build(BuildContext context) { + String token; + int profileId; + final bloc = BlocProvider.of(context); + List? mySkillsAndHobbies; return Scaffold( appBar: AppBar( - title: const Text(skillAndHobbiesTitle), - backgroundColor: primary, - centerTitle: true, - actions: [AddLeading(onPressed: () {})], - ), + title: const Text(skillAndHobbiesTitle), + backgroundColor: primary, + centerTitle: true, + actions: context.watch().state is AddHobbySkillState + ? [ + AddLeading(onPressed: () { + context.read().add(ShowAddModal()); + }) + ] + : [ + AddLeading(onPressed: () { + context.read().add(ShowHobbySkillAddForm( + mySkillsAndHobbies: mySkillsAndHobbies!)); + }) + ]), body: ProgressHUD( padding: const EdgeInsets.all(24), backgroundColor: Colors.black87, @@ -34,6 +53,8 @@ class SkillHobbiesScreen extends StatelessWidget { child: BlocBuilder( builder: (context, state) { if (state is UserLoggedIn) { + token = state.userData!.user!.login!.token!; + profileId = state.userData!.user!.login!.user!.profileId!; return BlocBuilder( builder: (context, state) { if (state is ProfileLoaded) { @@ -42,16 +63,65 @@ class SkillHobbiesScreen extends StatelessWidget { if (state is HobbiesLoadingState) { final progress = ProgressHUD.of(context); progress!.showWithText("Please wait..."); - }if(state is HobbiesLoadedState || state is HobbiesErrorState){ - final progress = ProgressHUD.of(context); - progress!.dismiss(); + } + if (state is HobbiesLoadedState || + state is HobbiesErrorState || + state is AddHobbySkillState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + ////ADDED STATE + if (state is HobbiesAndSkillsAddedState) { + if (state.status['success']) { + successAlert(context, "Adding Successfull!", + state.status['message'], () { + Navigator.of(context).pop(); + context.read().add( + LoadHobbiesSkills( + skillsHobbies: + state.mySkillsAndHobbies)); + }); + } else { + errorAlert(context, "Adding Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context.read().add( + LoadHobbiesSkills( + skillsHobbies: + state.mySkillsAndHobbies)); + }); + } + } + //// DELETED STATE + + if (state is HobbiesAndSkillsDeletedState) { + if (state.success) { + successAlert(context, "Delete Successfull!", + "Skill/Hobby Deleted Successfully", () { + Navigator.of(context).pop(); + context.read().add( + LoadHobbiesSkills( + skillsHobbies: state.skillsHobbies)); + }); + } else { + errorAlert(context, "Deletion Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context.read().add( + LoadHobbiesSkills( + skillsHobbies: state.skillsHobbies)); + }); + } } }, builder: (context, state) { if (state is HobbiesLoadedState) { + mySkillsAndHobbies = state.skillsAndHobbies; if (state.skillsAndHobbies.isNotEmpty) { return Padding( - padding: const EdgeInsets.all(24), + padding: const EdgeInsets.all(12), child: Wrap( spacing: 8, runSpacing: 8, @@ -61,17 +131,51 @@ class SkillHobbiesScreen extends StatelessWidget { crossAxisAlignment: WrapCrossAlignment.start, direction: Axis.horizontal, - children: - state.skillsAndHobbies.map((SkillsHobbies sh) { - return FittedBox( - child: Row( - children: [ - Text(sh.name!), - IconButton( - onPressed: () {}, - icon: const Icon(Icons.close)), - ], - ), + children: state.skillsAndHobbies + .map((SkillsHobbies sh) { + return Wrap( + children: [ + Container( + padding: const EdgeInsets.only( + left: 6), + + child: Wrap( + clipBehavior: Clip.antiAlias, + alignment: WrapAlignment.center, + crossAxisAlignment: + WrapCrossAlignment.center, + runAlignment: WrapAlignment.center, + children: [ + Text( + sh.name!, + style: Theme.of(context) + .textTheme + .labelMedium, + ), + IconButton( + onPressed: () { + confirmAlert( + context, + () => context.read().add(DeleteSkillHobbies( + profileId: + profileId, + skillsHobbies: [ + sh + ], + token: + token)), + "Delete", + "Confirm Delete"); + + }, + icon:const Icon( + Icons.delete, + size: 16, + color: second, + )) + ]), + ) + ], ); }).toList()), ); @@ -81,6 +185,15 @@ class SkillHobbiesScreen extends StatelessWidget { "You don't have any Skills and Hobbies added. Please click + to add"); } } + if (state is AddHobbySkillState) { + return AddHobbiesAndSkillsScreen( + profileId: profileId, + token: token, + ); + } + if (state is ShowAddModalState) { + return AddModal(bloc: bloc); + } return Container(); }, ); @@ -95,3 +208,84 @@ class SkillHobbiesScreen extends StatelessWidget { )); } } + +class AddModal extends StatefulWidget { + final HoobiesBloc bloc; + const AddModal({super.key, required this.bloc}); + + @override + State createState() => _AddModalState(); +} + +String output = ''; +String? deletedChip, deletedChipIndex; +final keySimpleChipsInput = GlobalKey(); +final FocusNode focusNode = FocusNode(); + +class _AddModalState extends State { + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + return AlertDialog( + title: const Text("Add Skills and Hobbies"), + content: SimpleChipsInput( + separatorCharacter: ",", + createCharacter: ",", + focusNode: focusNode, + validateInput: true, + autoFocus: true, + formKey: keySimpleChipsInput, + onSubmitted: (p0) { + setState(() { + output = p0; + }); + }, + onChipDeleted: (p0, p1) { + setState(() { + deletedChip = p0; + deletedChipIndex = p1.toString(); + }); + }, + onSaved: ((p0) { + setState(() { + output = p0; + }); + }), + chipTextStyle: const TextStyle( + color: Colors.white, + fontSize: 16, + ), + deleteIcon: const Icon( + Icons.delete, + size: 14.0, + color: second, + ), + widgetContainerDecoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16.0), + border: Border.all(color: Colors.blue[100]!), + ), + chipContainerDecoration: BoxDecoration( + color: second, + borderRadius: BorderRadius.circular(50), + ), + placeChipsSectionAbove: false, + ), + actions: [ + ElevatedButton( + onPressed: () { + keySimpleChipsInput.currentState!.save(); + context + .read() + .add(GetAddedHobbiesSkills(addedHobbiesSkills: output)); + }, + style: mainBtnStyle(primary, Colors.transparent, second), + child: const Text(submit), + ) + ], + ); + }, + ); + } +} diff --git a/lib/screens/profile/components/other_information/skills_hobbies/add_modal.dart b/lib/screens/profile/components/other_information/skills_hobbies/add_modal.dart new file mode 100644 index 0000000..cbcdc47 --- /dev/null +++ b/lib/screens/profile/components/other_information/skills_hobbies/add_modal.dart @@ -0,0 +1,73 @@ +import 'dart:math'; + +import 'package:filter_list/filter_list.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:unit2/bloc/profile/other_information/hobbies/hoobies_bloc.dart'; +import 'package:unit2/model/profile/other_information/skills_and_hobbies.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; + +class AddHobbiesAndSkillsScreen extends StatefulWidget { +final int profileId; +final String token; + const AddHobbiesAndSkillsScreen({super.key,required this.profileId, required this.token}); + + @override + State createState() => + _AddHobbiesAndSkillsScreenState(); +} + +class _AddHobbiesAndSkillsScreenState extends State { + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if(state is AddHobbySkillState){ + final List selectedList= state.mySkillsAndHobbiesString.map((var element){ + return state.allSkillsAndHobbies.firstWhere((var data) => data.name == element ); + }).toList(); + return FilterListWidget( + themeData: FilterListThemeData(context,choiceChipTheme: const ChoiceChipThemeData(selectedBackgroundColor: primary)), + hideSelectedTextCount: true, + listData: state.allSkillsAndHobbies, + selectedListData: selectedList, + onApplyButtonClick: (list) { + // Navigator.pop(context, list); + context.read().add(AddHobbyAndSkills(profileId: widget.profileId, token: widget.token, skillsHobbies: selectedList)); + }, + choiceChipLabel: (item) { + + return item!.name; + }, + // choiceChipBuilder: (context, item, isSelected) { + // return Container( + // padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12), + // margin: EdgeInsets.symmetric(horizontal: 10, vertical: 10), + // decoration: BoxDecoration( + // border: Border.all( + // color: isSelected! ? Colors.blue[300]! : Colors.grey[300]!, + // )), + // child: Text(item.name), + // ); + // }, + validateSelectedItem: (list, val) { + /// identify if item is selected or not + return list!.contains(val); + }, + onItemSearch: (user, query) { + /// When search query change in search bar then this method will be called + /// + /// Check if items contains query + return user.name!.toLowerCase().contains(query.toLowerCase()); + }, + + ); + } + return Container(); + }, + ); + } +} diff --git a/lib/sevices/profile/work_history_services.dart b/lib/sevices/profile/work_history_services.dart index bd0f595..8e45820 100644 --- a/lib/sevices/profile/work_history_services.dart +++ b/lib/sevices/profile/work_history_services.dart @@ -43,7 +43,7 @@ class WorkHistoryService { } -//delete workhistory +////delete workhistory Future delete( {required int profileId, required String token, @@ -84,7 +84,7 @@ class WorkHistoryService { } - //edit work history + ////edit work history Future> update({required WorkHistory oldWorkHistory, required WorkHistory newWorkHistory, required String token, required String profileId})async{ Map? statusResponse={}; String authtoken = "Token $token"; @@ -111,7 +111,7 @@ class WorkHistoryService { "_oldAgencyId":oldWorkHistory.agency!.id, "oldFromDate":oldWorkHistory.fromDate?.toString(), }; - // try{ + try{ http.Response response = await Request.instance.putRequest(path: path, headers: headers, body: body, param: {}); if(response.statusCode == 200 ){ Map data = jsonDecode(response.body); @@ -120,12 +120,12 @@ class WorkHistoryService { statusResponse.addAll({'success':false}); } return statusResponse; - // }catch(e){ - // throw e.toString(); - // } + }catch(e){ + throw e.toString(); + } } - //Add work history + ////Add work history Future>add({required WorkHistory workHistory, required String token, required int profileId , required bool isPrivate})async{ String authtoken = "Token $token"; String path = '${Url.instance.workhistory()}$profileId/'; diff --git a/lib/sevices/skillshobbies_services.dart b/lib/sevices/skillshobbies_services.dart index 2666033..c09d67f 100644 --- a/lib/sevices/skillshobbies_services.dart +++ b/lib/sevices/skillshobbies_services.dart @@ -1,5 +1,3 @@ - - import 'dart:convert'; import 'package:unit2/utils/request.dart'; @@ -7,34 +5,120 @@ import 'package:unit2/utils/request.dart'; import '../model/profile/other_information/skills_and_hobbies.dart'; import '../utils/urls.dart'; import 'package:http/http.dart' as http; -class SkillsHobbiesServices{ + +class SkillsHobbiesServices { static final SkillsHobbiesServices _instance = SkillsHobbiesServices(); static SkillsHobbiesServices get instance => _instance; - - Future> getSkillsHobbies(int profileId, String token)async{ - - List skillsAndHobbies = []; - String authToken = "Token $token"; - String path = "${Url.instance.getSkillsHobbies()}$profileId/"; +////GET + Future> getSkillsHobbies( + int profileId, String token) async { + List skillsAndHobbies = []; + String authToken = "Token $token"; + String path = "${Url.instance.skillsHobbies()}$profileId/"; Map headers = { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': authToken }; - try{ - http.Response response = await Request.instance.getRequest(path: path,param: {},headers: headers); - if(response.statusCode == 200){ - Map data = jsonDecode(response.body); - if(data['data']['skill_hobby'] != null){ - data['data']['skill_hobby'].forEach((var hobby){ - SkillsHobbies skillsHobby = SkillsHobbies.fromJson(hobby); - skillsAndHobbies.add(skillsHobby); - }); + try { + http.Response response = await Request.instance + .getRequest(path: path, param: {}, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data']['skill_hobby'] != null) { + data['data']['skill_hobby'].forEach((var hobby) { + SkillsHobbies skillsHobby = SkillsHobbies.fromJson(hobby); + skillsAndHobbies.add(skillsHobby); + }); + } } + } catch (e) { + throw e.toString(); } - }catch(e){ - throw e.toString(); + return skillsAndHobbies; } - return skillsAndHobbies; + +////ADD + Future> add( + {required List skillsHobbies, + required int profileId, + required String token}) async { + String authToken = "Token $token"; + String path = "${Url.instance.skillsHobbies()}$profileId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + Map body = {"skill_hobby": skillsHobbies}; + Map statusResponse = {}; + try { + http.Response response = await Request.instance + .postRequest(path: path, param: {}, headers: headers, body: body); + if (response.statusCode == 201) { + Map data = jsonDecode(response.body); + statusResponse = data; + } else { + statusResponse.addAll({'success': false}); + } + } catch (e) { + throw e.toString(); + } + return statusResponse; + } + + Future delete( + {required int profileId, + required String token, + required List skillsHobbies}) async { + String authToken = "Token $token"; + bool success = false; + String path = "${Url.instance.skillsHobbies()}$profileId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + + + Map body = { + "skill_hobby": [skillsHobbies] + }; + try { + http.Response response = await Request.instance.deleteRequest( + path: path, headers: headers, body: body, param: {}); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + success = data['success']; + } + } catch (e) { + throw e.toString(); + } + return success; + } + + ////GET ALL + Future> getAllSkillsHobbies() async { + List skillsAndHobbies = []; + String path = Url.instance.getAllSkillsHobbies(); + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + try { + http.Response response = await Request.instance.getRequest( + param: {}, + path: path, + headers: headers, + ); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((var element) { + SkillsHobbies skillsHobbies = SkillsHobbies.fromJson(element); + skillsAndHobbies.add(skillsHobbies); + }); + } + } + } catch (e) { + throw e.toString(); + } + return skillsAndHobbies; } } - diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index 578ab1b..ef2d994 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -5,9 +5,9 @@ class Url { String host() { // return '192.168.10.221:3003'; // return 'agusandelnorte.gov.ph'; - // return "192.168.10.219:3000"; + return "192.168.10.219:3000"; // return "devweb.agusandelnorte.gov.ph"; - return 'devapi.agusandelnorte.gov.ph:3004'; + // return 'devapi.agusandelnorte.gov.ph:3004'; } String authentication() { @@ -82,9 +82,12 @@ String getVoluntaryWorks(){ } //// skills hobbies -String getSkillsHobbies(){ +String skillsHobbies(){ return "/api/jobnet_app/profile/pds/other/skill_hobby/"; } +String getAllSkillsHobbies(){ + return "/api/jobnet_app/skill_hobby/"; +} //// orgmemberships String getOrgMemberShips(){ return "/api/jobnet_app/profile/pds/other/org_membership/"; diff --git a/pubspec.lock b/pubspec.lock index e2e4eda..f35808f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -853,6 +853,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.3.0" + simple_chips_input: + dependency: "direct main" + description: + name: simple_chips_input + sha256: "522b2e715fe67f325693e003acfd09fc0b8ab25a2c0c87fb8e5ce5b23a8a2ec1" + url: "https://pub.dev" + source: hosted + version: "1.0.0" sky_engine: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index ddcc03c..bc3e3a5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -72,6 +72,7 @@ dependencies: modal_progress_hud_nsn: ^0.3.0 searchfield: ^0.7.5 filter_list: ^1.0.2 + simple_chips_input: ^1.0.0 dev_dependencies: From e921eaf7bdd72b84506bb180cef38ffe9492f4d1 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Fri, 31 Mar 2023 10:27:45 +0800 Subject: [PATCH 58/86] refactor skills hobbies bloc and screen --- .../hobbies/hoobies_bloc.dart | 9 +- .../hobbies/hoobies_event.dart | 4 +- .../hobbies/hoobies_state.dart | 3 + .../skills_and_hobbies_screen.dart | 281 +++++++++++------- .../skills_hobbies/add_modal.dart | 2 +- lib/sevices/skillshobbies_services.dart | 21 +- lib/utils/urls.dart | 4 +- 7 files changed, 203 insertions(+), 121 deletions(-) diff --git a/lib/bloc/profile/other_information/hobbies/hoobies_bloc.dart b/lib/bloc/profile/other_information/hobbies/hoobies_bloc.dart index b612e61..fd8d923 100644 --- a/lib/bloc/profile/other_information/hobbies/hoobies_bloc.dart +++ b/lib/bloc/profile/other_information/hobbies/hoobies_bloc.dart @@ -31,6 +31,7 @@ class HoobiesBloc extends Bloc { on((event, emit) async { emit(HobbiesLoadingState()); try { + ////get all skills and hobbies if (allSkillsAndHobbies.isEmpty) { allSkillsAndHobbies = await SkillsHobbiesServices.instance.getAllSkillsHobbies(); @@ -99,7 +100,7 @@ class HoobiesBloc extends Bloc { ////DELETE on((event,emit)async{ emit(HobbiesLoadingState()); - try{ + // try{ bool success = await SkillsHobbiesServices.instance.delete(profileId: event.profileId, token: event.token, skillsHobbies: event.skillsHobbies); if(success){ skillsAndHobbies.removeWhere((element) => element.id == event.skillsHobbies[0].id); @@ -107,9 +108,9 @@ class HoobiesBloc extends Bloc { }else{ emit(HobbiesAndSkillsDeletedState(skillsHobbies: skillsAndHobbies, success: success)); } - }catch(e){ - emit (HobbiesErrorState(message: e.toString())); - } + // }catch(e){ + // emit (HobbiesErrorState(message: e.toString())); + // } }); } } diff --git a/lib/bloc/profile/other_information/hobbies/hoobies_event.dart b/lib/bloc/profile/other_information/hobbies/hoobies_event.dart index 5a718af..7a9abb5 100644 --- a/lib/bloc/profile/other_information/hobbies/hoobies_event.dart +++ b/lib/bloc/profile/other_information/hobbies/hoobies_event.dart @@ -15,8 +15,8 @@ class GetSkillsHobbies extends HobbiesEvent{ List get props => [profileId,token]; } class ShowHobbySkillAddForm extends HobbiesEvent{ - final List mySkillsAndHobbies; - const ShowHobbySkillAddForm({required this.mySkillsAndHobbies}); + + const ShowHobbySkillAddForm(); } class AddHobbyAndSkills extends HobbiesEvent{ diff --git a/lib/bloc/profile/other_information/hobbies/hoobies_state.dart b/lib/bloc/profile/other_information/hobbies/hoobies_state.dart index a2d39ac..858fd3a 100644 --- a/lib/bloc/profile/other_information/hobbies/hoobies_state.dart +++ b/lib/bloc/profile/other_information/hobbies/hoobies_state.dart @@ -36,9 +36,12 @@ class AddHobbySkillState extends HobbiesState{ class HobbiesLoadingState extends HobbiesState{ } + +//// show add modal for adding new skills and hobbies class ShowAddModalState extends HobbiesState{ } +//// hobbies and skills already added class HobbiesAndSkillsAddedState extends HobbiesState{ final List mySkillsAndHobbies; final Map status; diff --git a/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart b/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart index 7096b37..727b909 100644 --- a/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart +++ b/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart @@ -20,15 +20,23 @@ import '../../../../bloc/profile/other_information/hobbies/hoobies_bloc.dart'; import '../../../../theme-data.dart/btn-style.dart'; import '../../../../utils/alerts.dart'; -class SkillHobbiesScreen extends StatelessWidget { +class SkillHobbiesScreen extends StatefulWidget { const SkillHobbiesScreen({super.key}); + @override + State createState() => _SkillHobbiesScreenState(); +} + +class _SkillHobbiesScreenState extends State { @override Widget build(BuildContext context) { String token; int profileId; final bloc = BlocProvider.of(context); - List? mySkillsAndHobbies; + String output = ''; + String? deletedChip, deletedChipIndex; + final keySimpleChipsInput = GlobalKey(); + final FocusNode focusNode = FocusNode(); return Scaffold( appBar: AppBar( title: const Text(skillAndHobbiesTitle), @@ -37,13 +45,78 @@ class SkillHobbiesScreen extends StatelessWidget { actions: context.watch().state is AddHobbySkillState ? [ AddLeading(onPressed: () { - context.read().add(ShowAddModal()); + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text("Add Skills and Hobbies"), + content: SimpleChipsInput( + separatorCharacter: ",", + createCharacter: ",", + focusNode: focusNode, + validateInput: true, + autoFocus: true, + formKey: keySimpleChipsInput, + onSubmitted: (p0) { + setState(() { + output = p0; + }); + }, + onChipDeleted: (p0, p1) { + setState(() { + deletedChip = p0; + deletedChipIndex = p1.toString(); + }); + }, + onSaved: ((p0) { + setState(() { + output = p0; + }); + }), + chipTextStyle: const TextStyle( + color: Colors.white, + fontSize: 16, + ), + deleteIcon: const Icon( + Icons.delete, + size: 14.0, + color: second, + ), + widgetContainerDecoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16.0), + border: Border.all(color: Colors.blue[100]!), + ), + chipContainerDecoration: BoxDecoration( + color: second, + borderRadius: BorderRadius.circular(50), + ), + placeChipsSectionAbove: false, + ), + actions: [ + ElevatedButton( + onPressed: () { + keySimpleChipsInput.currentState!.save(); + Navigator.of(context, rootNavigator: true).pop('dialog'); + bloc.add( + GetAddedHobbiesSkills( + addedHobbiesSkills: output)); + + }, + style: mainBtnStyle( + primary, Colors.transparent, second), + child: const Text(submit), + ) + ], + ); + }); }) ] : [ AddLeading(onPressed: () { - context.read().add(ShowHobbySkillAddForm( - mySkillsAndHobbies: mySkillsAndHobbies!)); + bloc.add(const ShowHobbySkillAddForm( + )); + }) ]), body: ProgressHUD( @@ -51,6 +124,7 @@ class SkillHobbiesScreen extends StatelessWidget { backgroundColor: Colors.black87, indicatorWidget: const SpinKitFadingCircle(color: Colors.white), child: BlocBuilder( + buildWhen: (previous, current) => false, builder: (context, state) { if (state is UserLoggedIn) { token = state.userData!.user!.login!.token!; @@ -118,7 +192,7 @@ class SkillHobbiesScreen extends StatelessWidget { }, builder: (context, state) { if (state is HobbiesLoadedState) { - mySkillsAndHobbies = state.skillsAndHobbies; + if (state.skillsAndHobbies.isNotEmpty) { return Padding( padding: const EdgeInsets.all(12), @@ -136,15 +210,15 @@ class SkillHobbiesScreen extends StatelessWidget { return Wrap( children: [ Container( - padding: const EdgeInsets.only( - left: 6), - + padding: + const EdgeInsets.only(left: 6), child: Wrap( clipBehavior: Clip.antiAlias, alignment: WrapAlignment.center, crossAxisAlignment: WrapCrossAlignment.center, - runAlignment: WrapAlignment.center, + runAlignment: + WrapAlignment.center, children: [ Text( sh.name!, @@ -154,21 +228,23 @@ class SkillHobbiesScreen extends StatelessWidget { ), IconButton( onPressed: () { - confirmAlert( - context, - () => context.read().add(DeleteSkillHobbies( - profileId: - profileId, - skillsHobbies: [ - sh - ], - token: - token)), - "Delete", - "Confirm Delete"); - + confirmAlert( + context, + () => context + .read< + HoobiesBloc>() + .add(DeleteSkillHobbies( + profileId: + profileId, + skillsHobbies: [ + sh + ], + token: + token)), + "Delete", + "Confirm Delete"); }, - icon:const Icon( + icon: const Icon( Icons.delete, size: 16, color: second, @@ -191,9 +267,9 @@ class SkillHobbiesScreen extends StatelessWidget { token: token, ); } - if (state is ShowAddModalState) { - return AddModal(bloc: bloc); - } + // if (state is ShowAddModalState) { + // return AddModal(bloc: bloc); + // } return Container(); }, ); @@ -209,83 +285,80 @@ class SkillHobbiesScreen extends StatelessWidget { } } -class AddModal extends StatefulWidget { - final HoobiesBloc bloc; - const AddModal({super.key, required this.bloc}); +// class AddModal extends StatefulWidget { +// final HoobiesBloc bloc; +// const AddModal({super.key, required this.bloc}); - @override - State createState() => _AddModalState(); -} +// @override +// State createState() => _AddModalState(); +// } -String output = ''; -String? deletedChip, deletedChipIndex; -final keySimpleChipsInput = GlobalKey(); -final FocusNode focusNode = FocusNode(); -class _AddModalState extends State { - @override - Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, state) { - return AlertDialog( - title: const Text("Add Skills and Hobbies"), - content: SimpleChipsInput( - separatorCharacter: ",", - createCharacter: ",", - focusNode: focusNode, - validateInput: true, - autoFocus: true, - formKey: keySimpleChipsInput, - onSubmitted: (p0) { - setState(() { - output = p0; - }); - }, - onChipDeleted: (p0, p1) { - setState(() { - deletedChip = p0; - deletedChipIndex = p1.toString(); - }); - }, - onSaved: ((p0) { - setState(() { - output = p0; - }); - }), - chipTextStyle: const TextStyle( - color: Colors.white, - fontSize: 16, - ), - deleteIcon: const Icon( - Icons.delete, - size: 14.0, - color: second, - ), - widgetContainerDecoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(16.0), - border: Border.all(color: Colors.blue[100]!), - ), - chipContainerDecoration: BoxDecoration( - color: second, - borderRadius: BorderRadius.circular(50), - ), - placeChipsSectionAbove: false, - ), - actions: [ - ElevatedButton( - onPressed: () { - keySimpleChipsInput.currentState!.save(); - context - .read() - .add(GetAddedHobbiesSkills(addedHobbiesSkills: output)); - }, - style: mainBtnStyle(primary, Colors.transparent, second), - child: const Text(submit), - ) - ], - ); - }, - ); - } -} + +// class _AddModalState extends State { +// @override +// Widget build(BuildContext context) { +// return BlocBuilder( +// builder: (context, state) { +// return AlertDialog( +// title: const Text("Add Skills and Hobbies"), +// content: SimpleChipsInput( +// separatorCharacter: ",", +// createCharacter: ",", +// focusNode: focusNode, +// validateInput: true, +// autoFocus: true, +// formKey: keySimpleChipsInput, +// onSubmitted: (p0) { +// setState(() { +// output = p0; +// }); +// }, +// onChipDeleted: (p0, p1) { +// setState(() { +// deletedChip = p0; +// deletedChipIndex = p1.toString(); +// }); +// }, +// onSaved: ((p0) { +// setState(() { +// output = p0; +// }); +// }), +// chipTextStyle: const TextStyle( +// color: Colors.white, +// fontSize: 16, +// ), +// deleteIcon: const Icon( +// Icons.delete, +// size: 14.0, +// color: second, +// ), +// widgetContainerDecoration: BoxDecoration( +// color: Colors.white, +// borderRadius: BorderRadius.circular(16.0), +// border: Border.all(color: Colors.blue[100]!), +// ), +// chipContainerDecoration: BoxDecoration( +// color: second, +// borderRadius: BorderRadius.circular(50), +// ), +// placeChipsSectionAbove: false, +// ), +// actions: [ +// ElevatedButton( +// onPressed: () { +// keySimpleChipsInput.currentState!.save(); +// context +// .read() +// .add(GetAddedHobbiesSkills(addedHobbiesSkills: output)); +// }, +// style: mainBtnStyle(primary, Colors.transparent, second), +// child: const Text(submit), +// ) +// ], +// ); +// }, +// ); +// } +// } diff --git a/lib/screens/profile/components/other_information/skills_hobbies/add_modal.dart b/lib/screens/profile/components/other_information/skills_hobbies/add_modal.dart index cbcdc47..8633278 100644 --- a/lib/screens/profile/components/other_information/skills_hobbies/add_modal.dart +++ b/lib/screens/profile/components/other_information/skills_hobbies/add_modal.dart @@ -36,7 +36,7 @@ class _AddHobbiesAndSkillsScreenState extends State { selectedListData: selectedList, onApplyButtonClick: (list) { // Navigator.pop(context, list); - context.read().add(AddHobbyAndSkills(profileId: widget.profileId, token: widget.token, skillsHobbies: selectedList)); + context.read().add(AddHobbyAndSkills(profileId: widget.profileId, token: widget.token, skillsHobbies: list!)); }, choiceChipLabel: (item) { diff --git a/lib/sevices/skillshobbies_services.dart b/lib/sevices/skillshobbies_services.dart index c09d67f..f998dcc 100644 --- a/lib/sevices/skillshobbies_services.dart +++ b/lib/sevices/skillshobbies_services.dart @@ -48,7 +48,11 @@ class SkillsHobbiesServices { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': authToken }; - Map body = {"skill_hobby": skillsHobbies}; + List> skillsHobbiesBody=[]; + for(var element in skillsHobbies){ + skillsHobbiesBody.add({"id":element.id,"name":element.name}); + } + Map body = {"skill_hobby": skillsHobbiesBody}; Map statusResponse = {}; try { http.Response response = await Request.instance @@ -76,21 +80,22 @@ class SkillsHobbiesServices { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': authToken }; - + Map params = {"force_mode": "true"}; Map body = { - "skill_hobby": [skillsHobbies] + "skill_hobby": [{"id":skillsHobbies[0].id, "name":skillsHobbies[0].name}] + }; - try { + // try { http.Response response = await Request.instance.deleteRequest( - path: path, headers: headers, body: body, param: {}); + path: path, headers: headers, body: body, param: params); if (response.statusCode == 200) { Map data = jsonDecode(response.body); success = data['success']; } - } catch (e) { - throw e.toString(); - } + // } catch (e) { + // throw e.toString(); + // } return success; } diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index ef2d994..38c24aa 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -5,9 +5,9 @@ class Url { String host() { // return '192.168.10.221:3003'; // return 'agusandelnorte.gov.ph'; - return "192.168.10.219:3000"; + // return "192.168.10.219:3000"; // return "devweb.agusandelnorte.gov.ph"; - // return 'devapi.agusandelnorte.gov.ph:3004'; + return 'devapi.agusandelnorte.gov.ph:3004'; } String authentication() { From 3789d998a8114bca97949bf4a8edbcd6e461ffb1 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Wed, 5 Apr 2023 08:54:24 +0800 Subject: [PATCH 59/86] Add save credentials feature --- .../contact/contact_bloc.dart | 75 +++++ .../contact/contact_event.dart | 59 +++- .../contact/contact_state.dart | 58 +++- .../profile/references/references_bloc.dart | 1 - lib/bloc/user/user_bloc.dart | 31 +- lib/bloc/user/user_state.dart | 4 +- lib/main.dart | 19 +- .../contact_information/add_modal.dart | 246 ++++++++++++++ .../contact_information/edit_modal.dart | 278 ++++++++++++++++ .../contact_information_screen.dart | 161 +++++++++- .../components/eligibility/add_modal.dart | 3 + .../components/eligibility/edit_modal.dart | 20 +- .../components/reference/add_modal.dart | 3 +- .../profile/components/references_screen.dart | 2 + .../unit2/homepage.dart/components/menu.dart | 8 +- lib/screens/unit2/login/login.dart | 44 ++- .../components/custom_switch.dart | 3 +- .../qr_code_scanner.dart/settings_screen.dart | 2 +- lib/sevices/profile/contact_services.dart | 128 ++++++++ lib/utils/alerts.dart | 34 ++ lib/utils/global.dart | 9 +- lib/utils/profile_utilities.dart | 33 +- lib/utils/urls.dart | 14 + lib/widgets/error_state.dart | 2 +- lib/widgets/splash_screen.dart | 16 +- pubspec.lock | 300 +++++++++++++++++- pubspec.yaml | 8 +- 27 files changed, 1497 insertions(+), 64 deletions(-) create mode 100644 lib/screens/profile/components/basic_information/contact_information/add_modal.dart create mode 100644 lib/screens/profile/components/basic_information/contact_information/edit_modal.dart create mode 100644 lib/sevices/profile/contact_services.dart diff --git a/lib/bloc/profile/primary_information/contact/contact_bloc.dart b/lib/bloc/profile/primary_information/contact/contact_bloc.dart index b01bb81..9f8070d 100644 --- a/lib/bloc/profile/primary_information/contact/contact_bloc.dart +++ b/lib/bloc/profile/primary_information/contact/contact_bloc.dart @@ -1,5 +1,10 @@ +import 'dart:math'; + import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import 'package:unit2/sevices/profile/contact_services.dart'; +import 'package:unit2/utils/profile_utilities.dart'; import '../../../../model/profile/basic_information/contact_information.dart'; @@ -9,6 +14,8 @@ part 'contact_state.dart'; class ContactBloc extends Bloc { ContactBloc() : super(ContactInitial()) { List contactInformations = []; + List serviceTypes =[]; + //// get all contacts on((event, emit) { emit(ContactLoadingState()); try { @@ -18,5 +25,73 @@ class ContactBloc extends Bloc { emit(ContactErrorState(message: e.toString())); } }); + //// Load Contacts + on((event,emit){ + emit(ContactLoadedState(contactInformation: event.contactInformation)); + }); + //// show add form + on((event,emit)async{ + try{ + emit(ContactLoadingState()); + if(serviceTypes.isEmpty){ + serviceTypes = await ProfileUtilities.instance.getServiceType(); + } + emit(ContactAddingState(serviceTypes: serviceTypes)); + }catch(e){ + emit(ContactErrorState(message: e.toString())); + } + }); + ///// Show edit form + on((event,emit)async{ + ServiceType serviceType; + List serviceProvivers; + ServiceProvider serviceProvider; + try{ + if(serviceTypes.isEmpty){ + serviceTypes = await ProfileUtilities.instance.getServiceType(); + } + serviceType = serviceTypes.firstWhere((var element){ + return event.contactInfo.commService!.serviceType!.id == element.id; + }); + serviceProvivers = await ContactService.instance.getServiceProvider(serviceTypeId: serviceType.id!); + serviceProvider = serviceProvivers.firstWhere((element) => element.id == event.contactInfo.commService!.serviceProvider!.id); + emit(ContactEditingState(serviceTypes: serviceTypes,selectedServiceType: serviceType,serviceProviders: serviceProvivers,selectedProvider: serviceProvider, contactInfo: event.contactInfo)); + }catch(e){ + emit(ContactErrorState(message: e.toString())); + } + }); + ////edit contact + on((event,emit)async{ + Map responseStatus = await ContactService.instance.update(profileId: event.profileId, token: event.token, contactInfo: event.contactInfo); + if(responseStatus['success']){ + ContactInfo contactInfo = ContactInfo.fromJson(responseStatus['data']['contact_info']); + contactInformations.removeWhere((ContactInfo element) => element.id == event.contactInfo.id); + contactInformations.add(contactInfo); + emit(ContactEditedState(contactInformation: contactInformations, response: responseStatus)); + }else{ + emit(ContactEditedState(contactInformation: contactInformations, response: responseStatus)); + } + }); + + //// add contact + try{ + + }catch(e){ + emit(ContactErrorState(message: e.toString())); + } + //// delete contact + on((event, emit)async{ + try{ + final bool success = await ContactService.instance.deleteContact(profileId: event.profileId, token: event.token, contactInfo: event.contactInfo); + if(success){ + contactInformations.removeWhere((element) => element.id == event.contactInfo.id); + emit(ContactDeletedState(contactInformation: contactInformations, succcess: success)); + }else{ + emit(ContactDeletedState(contactInformation: contactInformations, succcess: success)); + } + }catch(e){ + emit(ContactErrorState(message: e.toString())); + } + }); } } diff --git a/lib/bloc/profile/primary_information/contact/contact_event.dart b/lib/bloc/profile/primary_information/contact/contact_event.dart index e2bdc88..e5259eb 100644 --- a/lib/bloc/profile/primary_information/contact/contact_event.dart +++ b/lib/bloc/profile/primary_information/contact/contact_event.dart @@ -7,10 +7,65 @@ abstract class ContactEvent extends Equatable { List get props => []; } - +////get contacts class GetContacts extends ContactEvent{ final List contactInformations; const GetContacts({required this.contactInformations}); @override List get props => []; -} \ No newline at end of file +} + +//// load contacts +class LoadContacts extends ContactEvent{ + final List contactInformation; + const LoadContacts({required this.contactInformation}); + @override + List get props => [contactInformation]; +} + +//// show add form +class ShowAddForm extends ContactEvent{ + +} + +//// show edit form +class ShowEditForm extends ContactEvent{ + final ContactInfo contactInfo; + const ShowEditForm({required this.contactInfo}); + @override + List get props => [contactInfo]; + +} + +////add event +class AddContactInformation extends ContactEvent{ + final int profileId; + final String token; + final ContactInfo contactInfo; + const AddContactInformation({required this.contactInfo, required this.profileId, required this.token}); + @override + List get props => [profileId,token,contactInfo]; +} + +////edit event +class EditContactInformation extends ContactEvent{ + final int profileId; + final String token; + final ContactInfo contactInfo; + const EditContactInformation({required this.contactInfo, required this.profileId, required this.token}); + @override + List get props => [profileId,token,contactInfo]; +} + +//// delete event + +class DeleteContactInformation extends ContactEvent{ + final int profileId; + final String token; + final ContactInfo contactInfo; + const DeleteContactInformation({required this.contactInfo, required this.profileId, required this.token}); + @override + List get props => [profileId,token,contactInfo]; +} + + diff --git a/lib/bloc/profile/primary_information/contact/contact_state.dart b/lib/bloc/profile/primary_information/contact/contact_state.dart index f5a403b..863f11c 100644 --- a/lib/bloc/profile/primary_information/contact/contact_state.dart +++ b/lib/bloc/profile/primary_information/contact/contact_state.dart @@ -9,19 +9,73 @@ abstract class ContactState extends Equatable { class ContactInitial extends ContactState {} +////loaded state class ContactLoadedState extends ContactState{ final List contactInformation; const ContactLoadedState({required this.contactInformation}); @override List get props => []; } - +////loading state class ContactLoadingState extends ContactState{ } +//// adding state +class ContactAddingState extends ContactState{ + final List serviceTypes; + const ContactAddingState({ required this.serviceTypes}); + @override + List get props => [serviceTypes]; +} +//// Editing state +class ContactEditingState extends ContactState{ + final List serviceTypes; + final List serviceProviders; + final ServiceProvider selectedProvider; + final ServiceType selectedServiceType; + final ContactInfo contactInfo; + const ContactEditingState({ required this.serviceTypes, required this.selectedServiceType, required this.selectedProvider, required this.serviceProviders, required this.contactInfo}); + @override + List get props => [serviceTypes]; +} + + +//// added state +class ContactAddedState extends ContactState{ + final List contactInformation; + final Map response; + const ContactAddedState({required this.contactInformation, required this.response}); + @override + List get props => [contactInformation,response]; +} + + + + +//// edited state +class ContactEditedState extends ContactState{ + final List contactInformation; + final Map response; + const ContactEditedState({required this.contactInformation, required this.response}); + @override + List get props => [contactInformation,response]; +} +////deleted state +class ContactDeletedState extends ContactState{ + final List contactInformation; + final bool succcess; + const ContactDeletedState({required this.contactInformation, required this.succcess}); + @override + List get props => [contactInformation,succcess]; +} + +////error state class ContactErrorState extends ContactState{ final String message; const ContactErrorState({required this.message}); @override List get props => [message]; -} \ No newline at end of file +} + + + diff --git a/lib/bloc/profile/references/references_bloc.dart b/lib/bloc/profile/references/references_bloc.dart index 5c2b2f8..3564706 100644 --- a/lib/bloc/profile/references/references_bloc.dart +++ b/lib/bloc/profile/references/references_bloc.dart @@ -162,7 +162,6 @@ class ReferencesBloc extends Bloc { //// add reference event on((event, emit) async { try { - emit(ReferencesLoadingState()); Map status = await ReferencesServices.instace .addReference( ref: event.reference, diff --git a/lib/bloc/user/user_bloc.dart b/lib/bloc/user/user_bloc.dart index 1042352..26e3e2f 100644 --- a/lib/bloc/user/user_bloc.dart +++ b/lib/bloc/user/user_bloc.dart @@ -1,13 +1,14 @@ import 'dart:async'; import 'dart:io'; - import 'package:barcode_scan2/barcode_scan2.dart'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; import 'package:unit2/model/login_data/user_info/user_data.dart'; import 'package:unit2/model/login_data/version_info.dart'; import 'package:unit2/screens/unit2/login/functions/get_app_version.dart'; import 'package:unit2/sevices/login_service/auth_service.dart'; +import 'package:unit2/utils/global.dart'; import '../../utils/scanner.dart'; import '../../utils/text_container.dart'; @@ -19,6 +20,7 @@ class UserBloc extends Bloc { UserData? _userData; VersionInfo? _versionInfo; String? _apkVersion; + bool save = false; UserBloc() : super(UserInitial()) { // this event is called when opening the app to check if // there is new app version @@ -29,7 +31,16 @@ class UserBloc extends Bloc { _versionInfo = versionInfo; String apkVersion = await getAppVersion(); _apkVersion = apkVersion; - emit(VersionLoaded(versionInfo: _versionInfo, apkVersion: _apkVersion)); + final String? saved = CREDENTIALS?.get('saved'); + final String? username = CREDENTIALS?.get('username'); + final String? password = CREDENTIALS?.get('password'); + if (saved != null) { + save = true; + add(UserLogin(username: username, password: password)); + } else { + emit(VersionLoaded( + versionInfo: _versionInfo, apkVersion: _apkVersion)); + } } catch (e) { emit(UserError( message: e.toString(), @@ -48,14 +59,10 @@ class UserBloc extends Bloc { if (response['status'] == true) { UserData userData = UserData.fromJson(response['data']); emit(UserLoggedIn( - userData: userData, - success: true, - message: response['message'])); - }else{ - emit(UserLoggedIn( - userData: null, - success: false, - message: response['message'])); + userData: userData, success: true, message: response['message'],savedCredentials: save)); + } else { + emit(UserLoggedIn( + userData: null, success: false, message: response['message'],savedCredentials: save)); } } on TimeoutException catch (_) { emit(InternetTimeout(message: timeoutError)); @@ -68,7 +75,7 @@ class UserBloc extends Bloc { UserData? userData = await AuthService.instance .qrLogin(uuid: event.uuid, password: event.password); _userData = userData; - emit(UserLoggedIn(userData: _userData)); + emit(UserLoggedIn(userData: _userData,savedCredentials: save)); } on TimeoutException catch (_) { emit(InternetTimeout(message: timeoutError)); } on SocketException catch (_) { @@ -78,7 +85,7 @@ class UserBloc extends Bloc { } }); on((event, emit) { - emit(UserLoggedIn(userData: _userData)); + emit(UserLoggedIn(userData: _userData,savedCredentials: save)); }); on((event, emit) async { ScanResult result = await QRCodeBarCodeScanner.instance.scanner(); diff --git a/lib/bloc/user/user_state.dart b/lib/bloc/user/user_state.dart index 26d815f..de6688f 100644 --- a/lib/bloc/user/user_state.dart +++ b/lib/bloc/user/user_state.dart @@ -20,6 +20,7 @@ class UserLoading extends UserState { } class SplashScreen extends UserState { + @override List get props => []; } @@ -34,7 +35,8 @@ class UserLoggedIn extends UserState{ final UserData? userData; final String? message; final bool? success; - UserLoggedIn({this.userData,this.message,this.success}); + final bool? savedCredentials; + UserLoggedIn({this.userData,this.message,this.success,this.savedCredentials}); } class VersionLoaded extends UserState { diff --git a/lib/main.dart b/lib/main.dart index ce06e14..da7df5a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,17 +3,28 @@ import 'package:flutter/material.dart'; import 'package:device_preview/device_preview.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:hive/hive.dart'; +import 'package:hive_flutter/hive_flutter.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/app_router.dart'; import 'package:unit2/utils/global_context.dart'; import 'package:unit2/utils/global_context.dart'; +import 'package:path_provider/path_provider.dart' as path_provider; import './utils/router.dart'; import './utils/global.dart'; -void main() { - runApp(MyApp()); +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + var appDirectory = await path_provider.getApplicationDocumentsDirectory(); + await Hive.initFlutter(appDirectory.path); + CREDENTIALS = await Hive.openBox('credentials'); + SOSCONTACTS = await Hive.openBox('soscontacts'); + SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]) + .then((_) { + runApp(MyApp()); + }); } // void main() => runApp( @@ -48,7 +59,7 @@ class MyApp extends StatelessWidget { BlocProvider( create: (_) => UserBloc(), ), - BlocProvider( + BlocProvider( create: (_) => ProfileBloc(), ), ], @@ -60,7 +71,7 @@ class MyApp extends StatelessWidget { // routeInformationParser: goRouter.routeInformationParser, // routerDelegate: goRouter.routerDelegate, // routeInformationProvider: goRouter.routeInformationProvider, - + title: 'uniT2 - Universal Tracker and Tracer', theme: ThemeData( primarySwatch: Colors.red, diff --git a/lib/screens/profile/components/basic_information/contact_information/add_modal.dart b/lib/screens/profile/components/basic_information/contact_information/add_modal.dart new file mode 100644 index 0000000..362d88f --- /dev/null +++ b/lib/screens/profile/components/basic_information/contact_information/add_modal.dart @@ -0,0 +1,246 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:fluttericon/entypo_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:mask_text_input_formatter/mask_text_input_formatter.dart'; +import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; +import 'package:unit2/bloc/profile/primary_information/contact/contact_bloc.dart'; +import 'package:unit2/model/profile/basic_information/contact_information.dart'; +import 'package:unit2/screens/unit2/roles/qr_code_scanner.dart/components/custom_switch.dart'; +import 'package:unit2/sevices/profile/contact_services.dart'; +import 'package:unit2/theme-data.dart/btn-style.dart'; +import 'package:unit2/theme-data.dart/form-style.dart'; +import 'package:unit2/utils/text_container.dart'; + +import '../../../../../theme-data.dart/colors.dart'; + +class AddContactInformationScreen extends StatefulWidget { + final int profileId; + final String token; + const AddContactInformationScreen( + {super.key, required this.profileId, required this.token}); + + @override + State createState() => + _AddContactInformationScreenState(); +} + +class _AddContactInformationScreenState + extends State { + final formKey = GlobalKey(); + ServiceType? selectedServiceType; + ServiceProvider? selectedProvider; + List serviceProviders = []; + bool callServiceType = false; + bool primaryaContact = false; + bool active = false; + String? numberMail; + var mobileFormatter = MaskTextInputFormatter( + mask: "+63 (##) ###-###", + filter: {"#": RegExp(r"^[1-9][0-9]*$")}, + type: MaskAutoCompletionType.lazy, + initialText: "0"); + + var landLineFormatter = MaskTextInputFormatter( + mask: "(###) ###-###", + filter: {"#": RegExp(r"^[0-9]")}, + type: MaskAutoCompletionType.lazy, + initialText: "0"); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is ContactAddingState) { + return FormBuilder( + key: formKey, + child: Padding( + padding: + const EdgeInsets.symmetric(vertical: 24, horizontal: 12), + child: Column( + children: [ + const SizedBox( + height: 24, + ), + ////Service Type + FormBuilderDropdown( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "service_type", + items: state.serviceTypes + .map>( + (ServiceType e) { + return DropdownMenuItem( + value: e, child: Text(e.name!)); + }).toList(), + decoration: normalTextFieldStyle("Service Type*", ""), + onChanged: (var service) async { + if (selectedServiceType != service) { + selectedServiceType = service; + setState(() { + callServiceType = true; + }); + serviceProviders = await ContactService.instance + .getServiceProvider( + serviceTypeId: selectedServiceType!.id!); + setState(() { + setState(() { + callServiceType = false; + }); + }); + } + }), + const SizedBox( + height: 12, + ), + ////Service Provider + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: callServiceType, + child: FormBuilderDropdown( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "Service Provider", + items: serviceProviders.isEmpty + ? [] + : serviceProviders + .map>( + (ServiceProvider e) { + return DropdownMenuItem( + value: e, child: Text(e.agency!.name!)); + }).toList(), + decoration: normalTextFieldStyle( + "Communication Service *", ""), + onChanged: (var serviceProvider) { + selectedProvider = serviceProvider; + }), + ), + ), + selectedServiceType != null + ? selectedServiceType?.id == 2 + //// Landline + ? FormBuilderTextField( + inputFormatters: [landLineFormatter], + name: 'number-mail', + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: normalTextFieldStyle( + "Landline number *", + "(area code) xxx - xxxx"), + ) + : selectedServiceType!.id == 1 || + selectedServiceType!.id == 19 + //// Mobile number + ? FormBuilderTextField( + name: 'number-mail', + inputFormatters: [mobileFormatter], + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: normalTextFieldStyle( + "Mobile number *", + "+63 (9xx) xxx - xxxx"), + ) + : selectedServiceType!.id == 4 + ////Social Media + ? FormBuilderTextField( + name: 'number-mail', + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + decoration: normalTextFieldStyle( + "Account ID / Username *", ""), + ) + : selectedServiceType!.id == 3 + ////Email Address + ? FormBuilderTextField( + name: 'number-mail', + validator: + FormBuilderValidators.compose([ + FormBuilderValidators.email( + errorText: + "Input vaild email"), + FormBuilderValidators.required( + errorText: + "This field is required") + ]), + decoration: normalTextFieldStyle( + "Email Address*", ""), + ) + : Container() + : const SizedBox(), + SizedBox( + height: selectedServiceType != null ? 12 : 0, + ), + //// Primary + FormBuilderSwitch( + initialValue: primaryaContact, + activeColor: second, + onChanged: (value) { + setState(() { + primaryaContact = value!; + }); + }, + decoration: normalTextFieldStyle("Primary?", 'Primary?'), + name: 'overseas', + title: Text(primaryaContact ? "YES" : "NO"), + ), + //// Active + const SizedBox( + height: 12, + ), + FormBuilderSwitch( + initialValue: primaryaContact, + activeColor: second, + onChanged: (value) { + setState(() { + active = value!; + }); + }, + decoration: normalTextFieldStyle("Active?", ''), + name: 'overseas', + title: Text(active ? "YES" : "NO"), + ), + const Expanded(child: SizedBox()), + SizedBox( + height: 60, + width: double.infinity, + child: ElevatedButton( + onPressed: () { + if (formKey.currentState!.saveAndValidate()) { + + numberMail = + formKey.currentState!.value['number-mail']; + CommService commService = CommService( + id: null, + serviceType: selectedServiceType, + serviceProvider: selectedProvider); + ContactInfo contactInfo = ContactInfo( + id: null, + active: active, + primary: primaryaContact, + numbermail: numberMail, + commService: commService); + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add(AddContactInformation(contactInfo: contactInfo, profileId: widget.profileId, token: widget.token)); + } + }, + style: + mainBtnStyle(primary, Colors.transparent, second), + child: const Text(submit), + ), + ) + ], + ), + )); + } + return Container(); + }, + ); + } +} diff --git a/lib/screens/profile/components/basic_information/contact_information/edit_modal.dart b/lib/screens/profile/components/basic_information/contact_information/edit_modal.dart new file mode 100644 index 0000000..9b6b1e4 --- /dev/null +++ b/lib/screens/profile/components/basic_information/contact_information/edit_modal.dart @@ -0,0 +1,278 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:fluttericon/entypo_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:mask_text_input_formatter/mask_text_input_formatter.dart'; +import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; +import 'package:unit2/bloc/profile/primary_information/contact/contact_bloc.dart'; +import 'package:unit2/model/profile/basic_information/contact_information.dart'; +import 'package:unit2/screens/unit2/roles/qr_code_scanner.dart/components/custom_switch.dart'; +import 'package:unit2/sevices/profile/contact_services.dart'; +import 'package:unit2/theme-data.dart/btn-style.dart'; +import 'package:unit2/theme-data.dart/form-style.dart'; +import 'package:unit2/utils/text_container.dart'; + +import '../../../../../theme-data.dart/colors.dart'; + +class EditContactInformationScreen extends StatefulWidget { + final int profileId; + final String token; + const EditContactInformationScreen( + {super.key, required this.profileId, required this.token}); + + @override + State createState() => + _EditContactInformationScreenState(); +} + +class _EditContactInformationScreenState + extends State { + final formKey = GlobalKey(); + ServiceType? selectedServiceType; + ServiceProvider? selectedProvider; + List serviceProviders = []; + String? numberMail; + bool callServiceType = false; + bool? primaryaContact; + bool? active; + + var mobileFormatter = MaskTextInputFormatter( + mask: "+63 (##) ###-###", + filter: {"#": RegExp(r"^[1-9][0-9]*$")}, + type: MaskAutoCompletionType.lazy, + initialText: "0"); + + var landLineFormatter = MaskTextInputFormatter( + mask: "(###) ###-###", + filter: {"#": RegExp(r"^[0-9]")}, + type: MaskAutoCompletionType.lazy, + initialText: "0"); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is ContactEditingState) { + selectedServiceType = state.selectedServiceType; + selectedProvider = state.selectedProvider; + serviceProviders = state.serviceProviders; + primaryaContact = state.contactInfo.primary!; + active = state.contactInfo.active!; + return FormBuilder( + key: formKey, + child: Padding( + padding: + const EdgeInsets.symmetric(vertical: 24, horizontal: 12), + child: Column( + children: [ + const SizedBox( + height: 24, + ), + StatefulBuilder(builder: (context, setState) { + return Column(children: [ + ////Service Type + DropdownButtonFormField( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + value: selectedServiceType, + items: state.serviceTypes + .map>( + (ServiceType e) { + return DropdownMenuItem( + value: e, child: Text(e.name!)); + }).toList(), + decoration: + normalTextFieldStyle("Service Type*", ""), + onChanged: (var service) async { + if (selectedServiceType!.id != service!.id) { + selectedServiceType = service; + setState(() { + callServiceType = true; + }); + serviceProviders = await ContactService.instance + .getServiceProvider( + serviceTypeId: + selectedServiceType!.id!); + selectedProvider = null; + setState(() { + setState(() { + callServiceType = false; + }); + }); + } + }), + const SizedBox( + height: 12, + ), + ////Service Provider + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: callServiceType, + child: DropdownButtonFormField( + value: selectedProvider, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + items: serviceProviders.isEmpty + ? [] + : serviceProviders + .map>( + (ServiceProvider e) { + return DropdownMenuItem< + ServiceProvider>( + value: e, + child: Text(e.agency!.name!)); + }).toList(), + decoration: normalTextFieldStyle( + "Communication Service *", ""), + onChanged: (var serviceProvider) { + selectedProvider = serviceProvider; + }), + ), + ), + selectedServiceType != null + ? selectedServiceType?.id == 2 + //// Landline + ? FormBuilderTextField( + name: 'number-mail', + initialValue: state.contactInfo.numbermail, + inputFormatters: [landLineFormatter], + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: normalTextFieldStyle( + "Landline number *", + "(area code) xxx - xxxx"), + ) + : selectedServiceType!.id == 1 || + selectedServiceType!.id == 19 + //// Mobile number + ? FormBuilderTextField( + name: 'number-mail', + inputFormatters: [mobileFormatter], + initialValue: + state.contactInfo.numbermail, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + decoration: normalTextFieldStyle( + "Mobile number *", + "+63 (9xx) xxx - xxxx"), + ) + : selectedServiceType!.id == 4 + ////Social Media + ? FormBuilderTextField( + name: 'number-mail', + initialValue: + state.contactInfo.numbermail, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + decoration: normalTextFieldStyle( + "Account ID / Username *", ""), + ) + : selectedServiceType!.id == 3 + ////Email Address + ? FormBuilderTextField( + name: 'number-mail', + initialValue: state + .contactInfo.numbermail, + validator: FormBuilderValidators + .compose([ + FormBuilderValidators.email( + errorText: + "Input vaild email"), + FormBuilderValidators.required( + errorText: + "This field is required") + ]), + decoration: + normalTextFieldStyle( + "Email Address*", ""), + ) + : Container() + : const SizedBox(), + ]); + }), + SizedBox( + height: selectedServiceType != null ? 12 : 0, + ), + //// Primary + StatefulBuilder(builder: (context, setState) { + return FormBuilderSwitch( + initialValue: primaryaContact, + activeColor: second, + onChanged: (value) { + setState(() { + primaryaContact = value; + }); + }, + decoration: normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Primary ?"), + ); + }), + //// Active + const SizedBox( + height: 12, + ), + StatefulBuilder(builder: (context, setState) { + return FormBuilderSwitch( + initialValue: active, + activeColor: second, + onChanged: (value) { + setState(() { + active = value; + }); + }, + decoration: normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Active ?"), + ); + }), + const Expanded(child: SizedBox()), + SizedBox( + height: 60, + width: double.infinity, + child: ElevatedButton( + onPressed: () { + if (formKey.currentState!.saveAndValidate()) { + numberMail = + formKey.currentState!.value['number-mail']; + CommService commService = CommService( + id: state.contactInfo.commService!.id, + serviceType: selectedServiceType, + serviceProvider: selectedProvider); + ContactInfo contactInfo = ContactInfo( + id: state.contactInfo.id, + active: active, + primary: primaryaContact, + numbermail: numberMail, + commService: commService); + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add( + EditContactInformation( + contactInfo: contactInfo, + profileId: widget.profileId, + token: widget.token)); + } + }, + style: + mainBtnStyle(primary, Colors.transparent, second), + child: const Text(submit), + ), + ) + ], + ), + )); + } + return Container(); + }, + ); + } +} diff --git a/lib/screens/profile/components/basic_information/contact_information_screen.dart b/lib/screens/profile/components/basic_information/contact_information_screen.dart index e7601a1..721ab4c 100644 --- a/lib/screens/profile/components/basic_information/contact_information_screen.dart +++ b/lib/screens/profile/components/basic_information/contact_information_screen.dart @@ -1,16 +1,23 @@ +import 'package:app_popup_menu/app_popup_menu.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; import 'package:unit2/bloc/profile/primary_information/contact/contact_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; +import 'package:unit2/screens/profile/components/basic_information/contact_information/add_modal.dart'; +import 'package:unit2/screens/profile/components/basic_information/contact_information/edit_modal.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/alerts.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; +import '../../../../bloc/profile/eligibility/eligibility_bloc.dart'; + class ContactInformationScreen extends StatelessWidget { const ContactInformationScreen({ super.key, @@ -18,13 +25,19 @@ class ContactInformationScreen extends StatelessWidget { @override Widget build(BuildContext context) { + int profileId; + String token; return SafeArea( child: Scaffold( appBar: AppBar( title: const Text(contactScreenTitle), centerTitle: true, backgroundColor: primary, - actions: [AddLeading(onPressed: () {})], + actions: [ + AddLeading(onPressed: () { + context.read().add(ShowAddForm()); + }) + ], ), body: ProgressHUD( padding: const EdgeInsets.all(24), @@ -33,6 +46,8 @@ class ContactInformationScreen extends StatelessWidget { child: BlocBuilder( builder: (context, state) { if (state is UserLoggedIn) { + token = state.userData!.user!.login!.token!; + profileId = state.userData!.user!.login!.user!.profileId!; return BlocBuilder( builder: (context, state) { if (state is ProfileLoaded) { @@ -43,10 +58,58 @@ class ContactInformationScreen extends StatelessWidget { progress!.showWithText("Please wait..."); } if (state is ContactLoadedState || - state is ContactErrorState) { + state is ContactErrorState || + state is ContactAddingState || + state is ContactEditingState || + state is ContactDeletedState || + state is ContactAddedState || + state is ContactEditedState) { final progress = ProgressHUD.of(context); progress!.dismiss(); } + ////EDIT CONTACT STATE + if (state is ContactEditedState) { + if (state.response['success']) { + successAlert(context, "Update Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context.read().add(LoadContacts( + contactInformation: + state.contactInformation)); + }); + } else { + errorAlert(context, "Update Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context.read().add(LoadContacts( + contactInformation: + state.contactInformation)); + }); + } + } + + ////DELETED STATE + if (state is ContactDeletedState) { + if (state.succcess) { + successAlert(context, "Deletion Successfull", + "Contact Info has been deleted successfully", + () { + Navigator.of(context).pop(); + context.read().add(LoadContacts( + contactInformation: + state.contactInformation)); + }); + } else { + errorAlert(context, "Deletion Failed", + "Error deleting Contact Info", () { + Navigator.of(context).pop(); + context.read().add(LoadContacts( + contactInformation: + state.contactInformation)); + }); + } + } }, builder: (context, state) { if (state is ContactLoadedState) { @@ -151,12 +214,65 @@ class ContactInformationScreen extends StatelessWidget { .toString()), ]), ), - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - )) + AppPopupMenu( + offset: + const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + ////delete contact-= = = = = = = = =>> + if (value == 2) { + final progress = + ProgressHUD.of( + context); + progress!.showWithText( + "Loading..."); + confirmAlert( + context, + () => context + .read< + ContactBloc>() + .add(DeleteContactInformation( + contactInfo: + state.contactInformation[ + index], + profileId: + profileId, + token: + token)), + "Delete?", + "Are you sure you want to delete this contact info?"); + } + if (value == 1) { + ////edit contact-= = = = = = = = =>> + context + .read() + .add(ShowEditForm( + contactInfo: state + .contactInformation[ + index])); + final progress = + ProgressHUD.of( + context); + progress!.showWithText( + "Loading..."); + } + }, + menuItems: [ + popMenuItem( + text: "Edit", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Delete", + value: 2, + icon: Icons.delete), + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ) ], ), ), @@ -172,6 +288,16 @@ class ContactInformationScreen extends StatelessWidget { "You don't have contact information added. Please click + to add"); } } + if (state is ContactAddingState) { + return AddContactInformationScreen( + profileId: profileId, + token: token, + ); + } + if (state is ContactEditingState) { + return EditContactInformationScreen( + profileId: profileId, token: token); + } return Container(); }, ); @@ -186,4 +312,23 @@ class ContactInformationScreen extends StatelessWidget { )), ); } + + PopupMenuItem popMenuItem({String? text, int? value, IconData? icon}) { + return PopupMenuItem( + value: value, + child: Row( + children: [ + Icon( + icon, + ), + const SizedBox( + width: 10, + ), + Text( + text!, + ), + ], + ), + ); + } } diff --git a/lib/screens/profile/components/eligibility/add_modal.dart b/lib/screens/profile/components/eligibility/add_modal.dart index 561c76b..7b896fa 100644 --- a/lib/screens/profile/components/eligibility/add_modal.dart +++ b/lib/screens/profile/components/eligibility/add_modal.dart @@ -152,6 +152,7 @@ class _AddEligibilityScreenState extends State { Flexible( flex: 1, child: DateTimePicker( + validator: FormBuilderValidators.required(errorText: "This field is required"), use24HourFormat: false, icon: const Icon( Icons.date_range), @@ -178,6 +179,7 @@ class _AddEligibilityScreenState extends State { Flexible( flex: 1, child: DateTimePicker( + validator: FormBuilderValidators.required(errorText: "This field is required"), controller: validityDateController, firstDate: DateTime(1970), @@ -214,6 +216,7 @@ class _AddEligibilityScreenState extends State { Column( children: [ FormBuilderSwitch( + validator: FormBuilderValidators.required(errorText: 'This field is required'), initialValue: overseas, activeColor: second, onChanged: (value) { diff --git a/lib/screens/profile/components/eligibility/edit_modal.dart b/lib/screens/profile/components/eligibility/edit_modal.dart index 69b7f64..24df914 100644 --- a/lib/screens/profile/components/eligibility/edit_modal.dart +++ b/lib/screens/profile/components/eligibility/edit_modal.dart @@ -2,6 +2,7 @@ import 'package:date_time_picker/date_time_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:intl/intl.dart'; import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; @@ -173,6 +174,7 @@ class _EditEligibilityScreenState extends State { Flexible( flex: 1, child: DateTimePicker( + validator: FormBuilderValidators.required(errorText: "This field is required"), use24HourFormat: false, controller: examDateController, firstDate: DateTime(1970), @@ -188,7 +190,7 @@ class _EditEligibilityScreenState extends State { const SizedBox( width: 12, ), - //VALIDITY DATE + ////VALIDITY DATE Flexible( flex: 1, child: DateTimePicker( @@ -335,7 +337,7 @@ class _EditEligibilityScreenState extends State { const SizedBox( height: 20, ), - //PROVINCE DROPDOWN + ////PROVINCE DROPDOWN SizedBox( height: 70, child: ModalProgressHUD( @@ -395,7 +397,7 @@ class _EditEligibilityScreenState extends State { ), ), - // City municipality + //// City municipality SizedBox( height: 70, child: ModalProgressHUD( @@ -458,29 +460,29 @@ class _EditEligibilityScreenState extends State { style: mainBtnStyle( primary, Colors.transparent, second), onPressed: () { - //rating + ////rating double? rate = rating == null ? null : double.parse(rating!); - //license + ////license String? newLicense = license; - //city municipality + ////city municipality CityMunicipality? cityMunicipality = selectedMunicipality; - //exam date + ////exam date DateTime? examDate = examDateController.text.isEmpty ? null : DateTime.parse( examDateController.text); - // validity date + // // validity date DateTime? validityDate = validityDateController.text.isEmpty ? null : DateTime.parse( validityDateController .text); - // exam address + //// exam address ExamAddress examAddress = ExamAddress( barangay: state.eligibityCert .examAddress?.barangay, diff --git a/lib/screens/profile/components/reference/add_modal.dart b/lib/screens/profile/components/reference/add_modal.dart index 0060bbc..d3f73b9 100644 --- a/lib/screens/profile/components/reference/add_modal.dart +++ b/lib/screens/profile/components/reference/add_modal.dart @@ -468,7 +468,8 @@ class _AddReferenceScreenState extends State { firstName: firstname, middleName: middlename); } - +final progress = ProgressHUD.of(context); +progress!.showWithText("Please wait..."); context.read().add( AddReference( profileId: diff --git a/lib/screens/profile/components/references_screen.dart b/lib/screens/profile/components/references_screen.dart index 495bf3a..9282301 100644 --- a/lib/screens/profile/components/references_screen.dart +++ b/lib/screens/profile/components/references_screen.dart @@ -215,6 +215,8 @@ class ReferencesScreen extends StatelessWidget { ////delete eligibilty-= = = = = = = = =>> if (value == 2) { + final progress = ProgressHUD.of(context); +progress!.showWithText("Please wait..."); confirmAlert(context, () { context .read< diff --git a/lib/screens/unit2/homepage.dart/components/menu.dart b/lib/screens/unit2/homepage.dart/components/menu.dart index 17922a4..d635fd0 100644 --- a/lib/screens/unit2/homepage.dart/components/menu.dart +++ b/lib/screens/unit2/homepage.dart/components/menu.dart @@ -3,6 +3,7 @@ import 'package:unit2/model/login_data/employee_info/employee_info.dart'; import 'package:unit2/model/login_data/user_info/user_data.dart'; import 'package:unit2/utils/alerts.dart'; import '../../../../theme-data.dart/colors.dart'; +import '../../../../utils/global.dart'; Widget getTile( IconData icondata, String title, String route, BuildContext context,UserData userData) { @@ -18,7 +19,8 @@ Widget getTile( ), onTap: () async { if (title.toLowerCase() == "logout") { - confirmAlert(context, () { + confirmAlert(context, () async{ + await CREDENTIALS!.clear(); Navigator.pushReplacementNamed (context,"/"); },"Logout","Are You sure you want to logout?"); @@ -26,9 +28,7 @@ Widget getTile( ProfileArguments profileArguments = ProfileArguments(token: userData.user!.login!.token!, userID:userData.user!.login!.user!.profileId!); Navigator.pushNamed(context, route,arguments: profileArguments); } - else { - Navigator.pushNamed(context, route); - } + }, ); } diff --git a/lib/screens/unit2/login/login.dart b/lib/screens/unit2/login/login.dart index b5dc6b5..11e7dce 100644 --- a/lib/screens/unit2/login/login.dart +++ b/lib/screens/unit2/login/login.dart @@ -14,6 +14,7 @@ import 'package:unit2/utils/internet_time_out.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/error_state.dart'; import '../../../bloc/user/user_bloc.dart'; +import '../../../utils/global_context.dart'; import '../../../widgets/splash_screen.dart'; import '../../../widgets/wave.dart'; import '../../../utils/global.dart'; @@ -34,6 +35,7 @@ class _UniT2LoginState extends State { final _formKey = GlobalKey(); bool showSuffixIcon = false; bool _showPassword = true; + String? password; @override Widget build(BuildContext context) { return WillPopScope( @@ -46,12 +48,31 @@ class _UniT2LoginState extends State { if (state is UserLoggedIn || state is UuidLoaded) { final progress = ProgressHUD.of(context); progress!.dismiss(); - } if (state is UserLoggedIn) { if (state.success == true) { - Fluttertoast.showToast(msg: state.message!,toastLength: Toast.LENGTH_LONG,gravity: ToastGravity.CENTER); - Navigator.pushReplacementNamed(context, '/module-screen'); + if (!state.savedCredentials!) { + confirmAlertWithCancel(context, () async { + await CREDENTIALS?.put('saved', "saved"); + await CREDENTIALS?.put('username', + state.userData!.user!.login!.user!.username!); + await CREDENTIALS?.put('password', password); + Fluttertoast.showToast( + msg: "Credentials Successfully saved", + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.black, + ); + Navigator.pushReplacementNamed(context, '/module-screen'); + }, + () => Navigator.pushReplacementNamed( + context, '/module-screen'), + "Save credentials?", + "Do you want to save credentials so you don't have to login again next time?."); + }else{ + Navigator.pushReplacementNamed( + context, '/module-screen'); + } } else { final progress = ProgressHUD.of(context); progress!.dismiss(); @@ -215,6 +236,7 @@ class _UniT2LoginState extends State { TextStyle(color: Colors.white), ), onPressed: () { + password = "nav071394"; final progress = ProgressHUD.of(context); @@ -228,8 +250,7 @@ class _UniT2LoginState extends State { BlocProvider.of(context) .add(UserLogin( - username: - "rjvincentlopeplopez", + username: "rjvincentlopeplopez", password: "shesthequ33n", // username: _formKey // .currentState! @@ -293,7 +314,9 @@ class _UniT2LoginState extends State { third, Colors.transparent, Colors.white38), - onPressed: () {}, + onPressed: () { + + }, label: const Text( requestSOS, style: @@ -321,7 +344,14 @@ class _UniT2LoginState extends State { if (state is UserError) { return SomethingWentWrong( message: onError, - onpressed: () {}, + onpressed: () { + BlocProvider.of( + NavigationService.navigatorKey.currentContext!) + .add(GetApkVersion()); + return MaterialPageRoute(builder: (_) { + return const UniT2Login(); + }); + }, ); } if (state is InternetTimeout) { diff --git a/lib/screens/unit2/roles/qr_code_scanner.dart/components/custom_switch.dart b/lib/screens/unit2/roles/qr_code_scanner.dart/components/custom_switch.dart index 907993f..0255d85 100644 --- a/lib/screens/unit2/roles/qr_code_scanner.dart/components/custom_switch.dart +++ b/lib/screens/unit2/roles/qr_code_scanner.dart/components/custom_switch.dart @@ -24,6 +24,7 @@ final int initialLabelIndex; padding: const EdgeInsets.all(15), height: 80, child: ToggleSwitch( + animate: true, minWidth: 150.0, cornerRadius: 25.0, activeBgColors: [ @@ -37,7 +38,7 @@ final int initialLabelIndex; totalSwitches: 2, labels: labels, icons: icons, - radiusStyle: false, + radiusStyle: true, onToggle: onToggle), ); } diff --git a/lib/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart b/lib/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart index 72b3f18..57081ed 100644 --- a/lib/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart +++ b/lib/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart @@ -94,7 +94,7 @@ class _QRCodeScannerSettingsState extends State { ), FittedBox( child: CostumToggleSwitch( - activeBGColors: [Colors.green[800]!, Colors.red[800]!], + activeBGColors: [ Colors.red[800]!,Colors.green[800]!], initialLabelIndex: scanMode == 'INCOMING' ? 0 : 1, icons: const [ Entypo.down_bold, diff --git a/lib/sevices/profile/contact_services.dart b/lib/sevices/profile/contact_services.dart new file mode 100644 index 0000000..6e23edf --- /dev/null +++ b/lib/sevices/profile/contact_services.dart @@ -0,0 +1,128 @@ +import 'dart:convert'; + +import 'package:unit2/model/profile/basic_information/contact_information.dart'; +import 'package:unit2/utils/request.dart'; +import 'package:unit2/utils/urls.dart'; +import 'package:http/http.dart' as http; + +class ContactService { + static final ContactService _instance = ContactService(); + static ContactService get instance => _instance; + + Future> getServiceProvider( + {required int serviceTypeId}) async { + String path = Url.instance.getServiceType(); + Map params = {"service_type__id": serviceTypeId.toString()}; + List serviceProviders = []; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + try { + http.Response response = await Request.instance + .getRequest(param: params, path: path, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + for (var element in data['data']) { + CommService commService = CommService.fromJson(element); + serviceProviders.add(commService.serviceProvider!); + } + } + } + } catch (e) { + throw e.toString(); + } + return serviceProviders; + } + +//// update + Future> update( + {required int profileId, + required String token, + required ContactInfo contactInfo}) async { + String path = "${Url.instance.contactPath()}$profileId/"; + String authToken = "Token $token"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + Map body = { + "personid": profileId, + "contactinfoid": contactInfo.id, + "_numbermail": contactInfo.numbermail, + "_active": contactInfo.active, + "_primary": contactInfo.primary, + "_commServiceId": contactInfo.commService!.serviceProvider!.id! + }; + Map responseStatus = {}; + try { + http.Response response = await Request.instance + .putRequest(path: path, headers: headers, body: body, param: {}); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + responseStatus = data; + } else { + responseStatus.addAll({'success': false}); + } + return responseStatus; + } catch (e) { + throw e.toString(); + } + } + + //// add +// Future> update( +// {required int profileId, +// required String token, +// required ContactInfo contactInfo}) async { +// String path = "${Url.instance.contactPath()}$profileId/"; +// String authToken = "Token $token"; +// Map headers = { +// 'Content-Type': 'application/json; charset=UTF-8', +// 'Authorization': authToken +// }; +// Map body ={ +// "personid": profileId, +// "_numbermail": contactInfo.numbermail, +// "_active": contactInfo.active, +// "_primary": contactInfo.primary, +// "_commServiceId": contactInfo +// } + // } + +////delete + Future deleteContact( + {required int profileId, + required String token, + required ContactInfo contactInfo}) async { + String path = "${Url.instance.deleteContact()}$profileId/"; + String authToken = "Token $token"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'mode': 'same-origin', + 'include': 'credentials', + 'Authorization': authToken + }; + bool success = false; + Map params = {"force_mode": "true"}; + Map body = { + "personid": profileId, + "contactinfoid": contactInfo.id, + "_numbermail": contactInfo.numbermail, + "_active": contactInfo.active, + "_primary": contactInfo.primary, + "_commServiceId": contactInfo.commService!.id + }; + try { + http.Response response = await Request.instance.deleteRequest( + path: path, headers: headers, body: body, param: params); + if (response.statusCode == 20) { + Map data = jsonDecode(response.body); + success = data['success']; + } + } catch (e) { + throw e.toString(); + } + return success; + } +} diff --git a/lib/utils/alerts.dart b/lib/utils/alerts.dart index a3e6f4e..1cd6a82 100644 --- a/lib/utils/alerts.dart +++ b/lib/utils/alerts.dart @@ -37,6 +37,40 @@ confirmAlert(context, Function() yes,String title, String subtitle) { ).show(); } +confirmAlertWithCancel(context, Function() yes,Function() no,String title, String subtitle) { + AwesomeDialog( + context: context, + dialogType: DialogType.question, + borderSide: const BorderSide( + color: Colors.green, + width: 0, + ), + width: blockSizeHorizontal * 90, + buttonsBorderRadius: const BorderRadius.all( + Radius.circular(2), + ), + dismissOnTouchOutside: false, + dismissOnBackKeyPress: false, + // onDismissCallback: (type) { + // ScaffoldMessenger.of(context).showSnackBar( + // SnackBar( + // content: Text('Dismissed by $type'), + // ), + // ); + // }, + headerAnimationLoop: false, + animType: AnimType.bottomSlide, + title: title, + desc: subtitle, + btnOkText: "Yes", + btnCancelText: "No", + showCloseIcon: false, + btnCancelOnPress: no, + btnOkOnPress: yes, + ).show(); +} + + errorAlert(context, title, description,Function() func) { AwesomeDialog( width: blockSizeHorizontal * 90, diff --git a/lib/utils/global.dart b/lib/utils/global.dart index ccbabf2..de816db 100644 --- a/lib/utils/global.dart +++ b/lib/utils/global.dart @@ -1,3 +1,5 @@ +import 'package:hive/hive.dart'; + double screenWidth = 0; double screenHeight = 0; double blockSizeHorizontal = 0; @@ -5,4 +7,9 @@ double blockSizeVertical = 0; double safeAreaHorizontal = 0; double safeAreaVertical = 0; double safeBlockHorizontal = 0; -double safeBlockVertical = 0; \ No newline at end of file +double safeBlockVertical = 0; + + +//// hive boxes +Box? CREDENTIALS; +Box? SOSCONTACTS; \ No newline at end of file diff --git a/lib/utils/profile_utilities.dart b/lib/utils/profile_utilities.dart index 50ec5d3..310dc61 100644 --- a/lib/utils/profile_utilities.dart +++ b/lib/utils/profile_utilities.dart @@ -8,6 +8,7 @@ import 'package:unit2/model/utils/eligibility.dart'; import 'package:unit2/utils/request.dart'; import 'package:unit2/utils/urls.dart'; +import '../model/profile/basic_information/contact_information.dart'; import '../model/utils/agency.dart'; import '../model/utils/category.dart'; class ProfileUtilities { @@ -21,7 +22,7 @@ class ProfileUtilities { Map headers = { 'Content-Type': 'application/json; charset=UTF-8', }; - // try{ + try{ http.Response response = await Request.instance.getRequest(path: path, param: {},headers: headers); if(response.statusCode == 200){ Map data = jsonDecode(response.body); @@ -32,9 +33,9 @@ class ProfileUtilities { }); } } - // }catch(e){ - // throw(e.toString()); - // } + }catch(e){ + throw(e.toString()); + } return eligibilities; } @@ -89,5 +90,29 @@ class ProfileUtilities { return agencyCategory; } +//// get service type + Future> getServiceType()async{ + List serviceTypes = []; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + String path = Url.instance.getServiceTypes(); + + try{ + http.Response response = await Request.instance.getRequest(param: {},path:path,headers: headers ); + if(response.statusCode == 200){ + Map data = jsonDecode(response.body); + if(data['data'] != null){ + for(var element in data['data']){ + ServiceType newServiceType = ServiceType.fromJson(element); + serviceTypes.add(newServiceType); + } + } + } + }catch(e){ + throw e.toString(); + } + return serviceTypes; + } } \ No newline at end of file diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index 38c24aa..e872d95 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -103,6 +103,20 @@ String getFamilies(){ return "/api/jobnet_app/profile/pds/family/"; } +//// contacts path +String getServiceTypes(){ + return "/api/jobnet_app/comm_service_type/"; + +} +String contactPath(){ + return "/api/jobnet_app/profile/pds/basic/contact/"; +} +String getServiceType(){ + return "/api/jobnet_app/comm_services/"; +} +String deleteContact (){ + return "/api/jobnet_app/profile/pds/basic/contact/"; +} // location utils path String getCounties(){ diff --git a/lib/widgets/error_state.dart b/lib/widgets/error_state.dart index e66a523..b23027f 100644 --- a/lib/widgets/error_state.dart +++ b/lib/widgets/error_state.dart @@ -20,7 +20,7 @@ class SomethingWentWrong extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, children: [ SvgPicture.asset( - 'assets/svgs/error.svg', + 'assets/svgs/timeout.svg', height: 200.0, width: 200.0, allowDrawingOutsideViewBox: true, diff --git a/lib/widgets/splash_screen.dart b/lib/widgets/splash_screen.dart index 41a2a8d..69eaaca 100644 --- a/lib/widgets/splash_screen.dart +++ b/lib/widgets/splash_screen.dart @@ -3,6 +3,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/src/widgets/container.dart'; import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; @@ -14,7 +15,7 @@ class UniTSplashScreen extends StatelessWidget { Widget build(BuildContext context) { return Container( height: MediaQuery.of(context).size.height, - color: Colors.white, + color: Colors.black12, child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -44,6 +45,19 @@ class UniTSplashScreen extends StatelessWidget { height: 1, color: Colors.black)), ), + const SizedBox(height: 5,), + SpinKitThreeBounce(color: Colors.black,size: 32,) + // Row( + // mainAxisAlignment: MainAxisAlignment.center, + // crossAxisAlignment: CrossAxisAlignment.center, + // children: [ + // Flexible( + // flex: 2, + // child: Text("Please Wait ",style: Theme.of(context).textTheme.labelLarge!.copyWith(fontSize: 18))), + // const SizedBox(width: 5,), + // const SpinKitDoubleBounce(color: primary,size: 32,) + // ],) + ], ), ), diff --git a/pubspec.lock b/pubspec.lock index f35808f..ee61216 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,22 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: "4897882604d919befd350648c7f91926a9d5de99e67b455bf0917cc2362f4bb8" + url: "https://pub.dev" + source: hosted + version: "47.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "690e335554a8385bc9d787117d9eb52c0c03ee207a607e593de3c9d71b1cfe80" + url: "https://pub.dev" + source: hosted + version: "4.7.0" animate_do: dependency: "direct main" description: @@ -25,6 +41,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.3.6" + args: + dependency: transitive + description: + name: args + sha256: "4cab82a83ffef80b262ddedf47a0a8e56ee6fbf7fe21e6e768b02792034dd440" + url: "https://pub.dev" + source: hosted + version: "2.4.0" async: dependency: transitive description: @@ -89,6 +113,70 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + build: + dependency: transitive + description: + name: build + sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777" + url: "https://pub.dev" + source: hosted + version: "2.3.1" + build_config: + dependency: transitive + description: + name: build_config + sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + url: "https://pub.dev" + source: hosted + version: "1.1.1" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: "757153e5d9cd88253cb13f28c2fb55a537dc31fefd98137549895b5beb7c6169" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: "687cf90a3951affac1bd5f9ecb5e3e90b60487f3d9cdc359bb310f8876bb02a6" + url: "https://pub.dev" + source: hosted + version: "2.0.10" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727 + url: "https://pub.dev" + source: hosted + version: "2.3.3" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: "14febe0f5bac5ae474117a36099b4de6f1dbc52df6c5e55534b3da9591bf4292" + url: "https://pub.dev" + source: hosted + version: "7.2.7" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: "31b7c748fd4b9adf8d25d72a4c4a59ef119f12876cf414f94f8af5131d5fa2b0" + url: "https://pub.dev" + source: hosted + version: "8.4.4" cached_network_image: dependency: "direct main" description: @@ -121,6 +209,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.1" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: "3d1505d91afa809d177efd4eed5bb0eb65805097a1463abdd2add076effae311" + url: "https://pub.dev" + source: hosted + version: "2.0.2" clock: dependency: transitive description: @@ -129,6 +225,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe" + url: "https://pub.dev" + source: hosted + version: "4.4.0" collection: dependency: transitive description: @@ -169,6 +273,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.2" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4" + url: "https://pub.dev" + source: hosted + version: "2.2.4" date_time_picker: dependency: "direct main" description: @@ -421,6 +533,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + glob: + dependency: transitive + description: + name: glob + sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c" + url: "https://pub.dev" + source: hosted + version: "2.1.1" globbing: dependency: transitive description: @@ -437,6 +565,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.0" + hive: + dependency: "direct main" + description: + name: hive + sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941" + url: "https://pub.dev" + source: hosted + version: "2.2.3" + hive_flutter: + dependency: "direct main" + description: + name: hive_flutter + sha256: dca1da446b1d808a51689fb5d0c6c9510c0a2ba01e22805d492c73b68e33eecc + url: "https://pub.dev" + source: hosted + version: "1.1.0" + hive_generator: + dependency: "direct dev" + description: + name: hive_generator + sha256: "81fd20125cb2ce8fd23623d7744ffbaf653aae93706c9bd3bf7019ea0ace3938" + url: "https://pub.dev" + source: hosted + version: "1.1.3" http: dependency: transitive description: @@ -445,6 +597,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.13.5" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" + source: hosted + version: "3.2.1" http_parser: dependency: transitive description: @@ -469,6 +629,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.17.0" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" js: dependency: transitive description: @@ -493,6 +661,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" + logging: + dependency: transitive + description: + name: logging + sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d" + url: "https://pub.dev" + source: hosted + version: "1.1.1" lottie: dependency: transitive description: @@ -501,6 +677,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.3" + mask_text_input_formatter: + dependency: "direct main" + description: + name: mask_text_input_formatter + sha256: "19bb7809c3c2559277e95521b3ee421e1409eb2cc85efd2feb191696c92490f4" + url: "https://pub.dev" + source: hosted + version: "2.4.0" matcher: dependency: transitive description: @@ -525,6 +709,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.0" + mime: + dependency: transitive + description: + name: mime + sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + url: "https://pub.dev" + source: hosted + version: "1.0.4" modal_progress_hud_nsn: dependency: "direct main" description: @@ -549,6 +741,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.2" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" package_info_plus: dependency: "direct main" description: @@ -593,10 +793,10 @@ packages: dependency: "direct main" description: name: path_provider - sha256: dcea5feb97d8abf90cab9e9030b497fb7c3cbf26b7a1fe9e3ef7dcb0a1ddec95 + sha256: c7edf82217d4b2952b2129a61d3ad60f1075b9299e629e149a8d2e39c2e6aad4 url: "https://pub.dev" source: hosted - version: "2.0.12" + version: "2.0.14" path_provider_android: dependency: transitive description: @@ -717,6 +917,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.6.2" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" process: dependency: transitive description: @@ -741,6 +949,22 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.5" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17" + url: "https://pub.dev" + source: hosted + version: "2.1.3" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: ec85d7d55339d85f44ec2b682a82fea340071e8978257e5a43e69f79e98ef50c + url: "https://pub.dev" + source: hosted + version: "1.2.2" qr: dependency: transitive description: @@ -845,6 +1069,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.3" + shelf: + dependency: transitive + description: + name: shelf + sha256: c24a96135a2ccd62c64b69315a14adc5c3419df63b4d7c05832a346fdb73682c + url: "https://pub.dev" + source: hosted + version: "1.4.0" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: a988c0e8d8ffbdb8a28aa7ec8e449c260f3deb808781fe1284d22c5bba7156e8 + url: "https://pub.dev" + source: hosted + version: "1.0.3" signature: dependency: "direct main" description: @@ -866,6 +1106,22 @@ packages: description: flutter source: sdk version: "0.0.99" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: "2d79738b6bbf38a43920e2b8d189e9a3ce6cc201f4b8fc76be5e4fe377b1c38d" + url: "https://pub.dev" + source: hosted + version: "1.2.6" + source_helper: + dependency: transitive + description: + name: source_helper + sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f" + url: "https://pub.dev" + source: hosted + version: "1.3.3" source_span: dependency: transitive description: @@ -906,6 +1162,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" + source: hosted + version: "2.1.0" string_scanner: dependency: transitive description: @@ -946,6 +1210,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.4.16" + timing: + dependency: transitive + description: + name: timing + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" + url: "https://pub.dev" + source: hosted + version: "1.0.1" toggle_switch: dependency: "direct main" description: @@ -978,6 +1250,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + watcher: + dependency: transitive + description: + name: watcher + sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: ca49c0bc209c687b887f30527fb6a9d80040b072cc2990f34b9bec3e7663101b + url: "https://pub.dev" + source: hosted + version: "2.3.0" win32: dependency: transitive description: @@ -1002,6 +1290,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.2.2" + yaml: + dependency: transitive + description: + name: yaml + sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370" + url: "https://pub.dev" + source: hosted + version: "3.1.1" sdks: dart: ">=2.19.0 <3.0.0" flutter: ">=3.3.0" diff --git a/pubspec.yaml b/pubspec.yaml index bc3e3a5..5cd2be5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -62,7 +62,7 @@ dependencies: equatable: ^2.0.5 package_info_plus: ^3.0.2 easy_app_installer: ^1.0.0 - path_provider: ^2.0.11 + path_provider: ^2.0.14 dio: ^4.0.6 cool_alert: ^1.1.0 permission_handler: ^10.2.0 @@ -73,7 +73,9 @@ dependencies: searchfield: ^0.7.5 filter_list: ^1.0.2 simple_chips_input: ^1.0.0 - + hive: ^2.0.5 + hive_flutter: ^1.1.0 + mask_text_input_formatter: ^2.4.0 dev_dependencies: flutter_test: @@ -85,6 +87,8 @@ dev_dependencies: # package. See that file for information about deactivating specific lint # rules and activating additional ones. flutter_lints: ^2.0.0 + build_runner: ^2.1.7 + hive_generator: ^1.1.2 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec From 5dcfd32a51bd73b96b6ba001b8bc512420ac8871 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Tue, 11 Apr 2023 09:27:53 +0800 Subject: [PATCH 60/86] erge into develop --- .../profile/eligibility/eligibility_bloc.dart | 114 +- .../eligibility/eligibility_event.dart | 10 +- .../eligibility/eligibility_state.dart | 15 +- .../organization_membership_bloc.dart | 15 +- .../organization_membership_event.dart | 12 +- .../organization_membership_state.dart | 10 +- .../contact/contact_bloc.dart | 172 +- .../contact/contact_event.dart | 5 +- .../contact/contact_state.dart | 24 +- .../profile/references/references_bloc.dart | 20 +- .../profile/references/references_event.dart | 71 +- .../profile/references/references_state.dart | 14 +- .../profile/workHistory/workHistory_bloc.dart | 102 +- .../workHistory/workHistory_event.dart | 9 +- .../workHistory/workHistory_state.dart | 15 +- lib/bloc/user/user_bloc.dart | 2 + .../contact_information/add_modal.dart | 29 +- .../contact_information/edit_modal.dart | 39 +- .../contact_information_screen.dart | 125 +- .../components/eligibility/add_modal.dart | 824 +++++----- .../components/eligibility/edit_modal.dart | 41 +- .../components/eligibility_screen.dart | 78 +- .../profile/components/loading_screen.dart | 11 +- .../org_membership/add_modal.dart | 599 ++++--- .../org_membership_screen.dart | 31 +- .../skills_and_hobbies_screen.dart | 4 - .../components/reference/add_modal.dart | 802 +++++----- .../components/reference/edit_modal.dart | 34 +- .../profile/components/references_screen.dart | 80 +- .../components/work_history/add_modal.dart | 55 +- .../components/work_history/edit_modal.dart | 1394 ++++++++--------- .../components/work_history_screen.dart | 104 +- .../basic-info/components/cover-image.dart | 3 +- .../unit2/homepage.dart/components/menu.dart | 3 + lib/screens/unit2/login/login.dart | 20 +- lib/sevices/profile/contact_services.dart | 62 +- lib/utils/urls.dart | 6 +- lib/widgets/splash_screen.dart | 4 +- 38 files changed, 2409 insertions(+), 2549 deletions(-) diff --git a/lib/bloc/profile/eligibility/eligibility_bloc.dart b/lib/bloc/profile/eligibility/eligibility_bloc.dart index 9ce54ee..28c0627 100644 --- a/lib/bloc/profile/eligibility/eligibility_bloc.dart +++ b/lib/bloc/profile/eligibility/eligibility_bloc.dart @@ -16,33 +16,57 @@ class EligibilityBloc extends Bloc { EligibilityBloc() : super(EligibilityInitial()) { List? globalCountries; List? globalRegions; - List? globalEligibilities; - List? eligibilities; -////===================================================================== + List globalEligibilities = []; + List eligibilities = []; +//// LOAD ELIGIBILTY on((event, emit) { emit(EligibilityLoadingState()); - eligibilities = event.eligibilities; - emit(EligibilityLoaded(eligibilities: event.eligibilities)); + if (eligibilities.isEmpty) { + GetEligibilities(profileId: event.profileId!, token: event.token!); + } else { + emit(EligibilityLoaded(eligibilities: eligibilities)); + } }); -////==================================================================== - on((event, emit) async { + + //// DELETE + on((event, emit) async { try { - if (eligibilities != null) { - emit(EligibilityLoaded(eligibilities: eligibilities!)); + final bool success = await EligibilityService.instance.delete( + eligibilityId: event.eligibilityId, + profileId: int.parse(event.profileId), + token: event.token); + if (success) { + eligibilities.removeWhere( + ((EligibityCert element) => element.id == event.eligibilityId)); + emit(DeletedState( + success: success, + )); } else { - emit(EligibilityLoadingState()); - eligibilities = await EligibilityService.instance - .getEligibilities(event.profileId, event.token); - emit(EligibilityLoaded(eligibilities: eligibilities!)); + emit(DeletedState(success: success)); } } catch (e) { emit(EligibilityErrorState(message: e.toString())); } }); -////==================================================================== + +//// GET ELIGIBILITY + on((event, emit) async { + try { + if (eligibilities.isNotEmpty) { + emit(EligibilityLoaded(eligibilities: eligibilities)); + } else { + emit(EligibilityLoadingState()); + eligibilities = await EligibilityService.instance + .getEligibilities(event.profileId, event.token); + emit(EligibilityLoaded(eligibilities: eligibilities)); + } + } catch (e) { + emit(EligibilityErrorState(message: e.toString())); + } + }); +//// SHOW EDIT FORM on((event, emit) async { try { - emit(EligibilityLoadingState()); if (globalCountries == null) { List countries = await LocationUtils.instance.getCountries(); globalCountries = countries; @@ -51,12 +75,12 @@ class EligibilityBloc extends Bloc { List regions = await LocationUtils.instance.getRegions(); globalRegions = regions; } - if (globalEligibilities == null) { + if (globalEligibilities.isEmpty) { List eligibilities = await ProfileUtilities.instance.getEligibilities(); globalEligibilities = eligibilities; } - Eligibility currentEligibility = globalEligibilities!.firstWhere( + Eligibility currentEligibility = globalEligibilities.firstWhere( (Eligibility eligibility) => event.eligibityCert.eligibility!.id == eligibility.id); bool? isOverseas = event.eligibityCert.overseas; @@ -95,7 +119,7 @@ class EligibilityBloc extends Bloc { eligibityCert: event.eligibityCert, countries: globalCountries!, regions: globalRegions!, - eligibilities: globalEligibilities!)); + eligibilities: globalEligibilities)); } else { emit(EditEligibilityState( selectedCountry: currentCountry, @@ -109,17 +133,16 @@ class EligibilityBloc extends Bloc { eligibityCert: event.eligibityCert, countries: globalCountries!, regions: globalRegions!, - eligibilities: globalEligibilities!)); + eligibilities: globalEligibilities)); } } catch (e) { emit(EligibilityErrorState(message: e.toString())); } }); - ////==================================================================== + //// UPDATE on((event, emit) async { try { - emit(EligibilityLoadingState()); Map status = await EligibilityService.instance.update( eligibityCert: event.eligibityCert, token: event.token, @@ -127,26 +150,25 @@ class EligibilityBloc extends Bloc { oldEligibility: event.oldEligibility); if (status['success']) { EligibityCert newEligibility = EligibityCert.fromJson(status['data']); - eligibilities!.removeWhere( + eligibilities.removeWhere( (EligibityCert element) => element.id == event.eligibityCert.id); - eligibilities!.add(newEligibility); - emit(EligibilityEditedState( - eligibilities: eligibilities!, response: status)); + eligibilities.add(newEligibility); + emit(EligibilityEditedState(response: status)); } else { - emit(EligibilityEditedState( - eligibilities: eligibilities!, response: status)); + emit(EligibilityEditedState(response: status)); } } catch (e) { emit(EligibilityErrorState(message: e.toString())); } }); + //// SHOW ADD FORM on((event, emit) async { emit(EligibilityLoadingState()); if (globalRegions == null) { List regions = await LocationUtils.instance.getRegions(); globalRegions = regions; } - if (globalEligibilities == null) { + if (globalEligibilities.isEmpty) { List eligibilities = await ProfileUtilities.instance.getEligibilities(); globalEligibilities = eligibilities; @@ -157,37 +179,15 @@ class EligibilityBloc extends Bloc { } emit(AddEligibilityState( - eligibilities: globalEligibilities!, + eligibilities: globalEligibilities, regions: globalRegions!, countries: globalCountries!)); }); - ////==================================================================== - on((event, emit) async { - emit(EligibilityLoadingState()); - try { - final bool success = await EligibilityService.instance.delete( - eligibilityId: event.eligibilityId, - profileId: int.parse(event.profileId), - token: event.token); - if (success) { - event.eligibilities.removeWhere( - ((EligibityCert element) => element.id == event.eligibilityId)); - - List eligibilities = event.eligibilities; - emit(DeletedState(success: success, eligibilities: eligibilities)); - } else { - emit(DeletedState( - success: success, eligibilities: event.eligibilities)); - } - } catch (e) { - emit(EligibilityErrorState(message: e.toString())); - } - }); - ////==================================================================== + + //// ADD on( (event, emit) async { try { - emit(EligibilityLoadingState()); Map status = await EligibilityService.instance.add( eligibityCert: event.eligibityCert, token: event.token, @@ -195,12 +195,10 @@ class EligibilityBloc extends Bloc { if (status['success']) { EligibityCert? eligibityCert = EligibityCert.fromJson(status['data']); - eligibilities!.add(eligibityCert); - emit(EligibilityAddedState( - eligibilities: eligibilities!, response: status)); + eligibilities.add(eligibityCert); + emit(EligibilityAddedState(response: status)); } else { - emit(EligibilityAddedState( - eligibilities: eligibilities!, response: status)); + emit(EligibilityAddedState(response: status)); } } catch (e) { emit(EligibilityErrorState(message: e.toString())); diff --git a/lib/bloc/profile/eligibility/eligibility_event.dart b/lib/bloc/profile/eligibility/eligibility_event.dart index 381ffd8..6b83a27 100644 --- a/lib/bloc/profile/eligibility/eligibility_event.dart +++ b/lib/bloc/profile/eligibility/eligibility_event.dart @@ -38,8 +38,9 @@ class UpdateEligibility extends EligibilityEvent{ List get props =>[eligibityCert,profileId,token,oldEligibility]; } class LoadEligibility extends EligibilityEvent { - final List eligibilities; - const LoadEligibility({required this.eligibilities}); + final int? profileId; + final String? token; + const LoadEligibility({ this.profileId, this.token}); @override List get props => []; } @@ -52,17 +53,16 @@ class ShowEditEligibilityForm extends EligibilityEvent { } class DeleteEligibility extends EligibilityEvent { - final List eligibilities; final String profileId; final int eligibilityId; final String token; const DeleteEligibility( - {required this.eligibilities, + { required this.eligibilityId, required this.profileId, required this.token}); @override - List get props => [eligibilities, profileId, eligibilityId, token]; + List get props => [ profileId, eligibilityId, token]; } class CallErrorState extends EligibilityEvent{ diff --git a/lib/bloc/profile/eligibility/eligibility_state.dart b/lib/bloc/profile/eligibility/eligibility_state.dart index 63421db..3b57f6d 100644 --- a/lib/bloc/profile/eligibility/eligibility_state.dart +++ b/lib/bloc/profile/eligibility/eligibility_state.dart @@ -43,11 +43,10 @@ class EditEligibilityState extends EligibilityState { } class DeletedState extends EligibilityState { - final List eligibilities; final bool success; - const DeletedState({required this.eligibilities, required this.success}); + const DeletedState({required this.success}); @override - List get props => [success, eligibilities]; + List get props => [success]; } class AddEligibilityState extends EligibilityState { @@ -63,20 +62,18 @@ class AddEligibilityState extends EligibilityState { List get props => [eligibilities,countries,regions]; } class EligibilityEditedState extends EligibilityState{ - final List eligibilities; final Map response; - const EligibilityEditedState({required this.eligibilities, required this.response}); + const EligibilityEditedState({required this.response}); @override - List get props =>[eligibilities, response]; + List get props =>[ response]; } class EligibilityAddedState extends EligibilityState{ - final List eligibilities; final Map response; - const EligibilityAddedState({required this.eligibilities, required this.response}); + const EligibilityAddedState({ required this.response}); @override - List get props =>[eligibilities,response]; + List get props =>[response]; } class EligibilityLoadingState extends EligibilityState{ diff --git a/lib/bloc/profile/other_information/org_membership/organization_membership_bloc.dart b/lib/bloc/profile/other_information/org_membership/organization_membership_bloc.dart index 41da2ed..fee8447 100644 --- a/lib/bloc/profile/other_information/org_membership/organization_membership_bloc.dart +++ b/lib/bloc/profile/other_information/org_membership/organization_membership_bloc.dart @@ -1,6 +1,5 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; -import 'package:unit2/model/profile/work_history.dart'; import 'package:unit2/model/utils/agency.dart'; import 'package:unit2/model/utils/category.dart'; import 'package:unit2/sevices/profile/orgmembership_services.dart'; @@ -30,7 +29,7 @@ class OrganizationMembershipBloc extends Bloc((event,emit){ - emit(OrganizationMembershipLoaded(orgMemberships: event.organizationMemberships)); + emit(OrganizationMembershipLoaded(orgMemberships: organizationMemberships)); }); ////SHOW ADD ORG MEMBERSHIP FORM @@ -52,15 +51,14 @@ class OrganizationMembershipBloc extends Bloc((event,emit)async{ - emit(OrgmembershipLoadingState()); try{ Map status= await OrganizationMembershipServices.instance.add(agency: event.agency, token: event.token, profileId: event.profileId.toString()); if(status["success"]){ OrganizationMembership organizationMembership = OrganizationMembership.fromJson(status["data"]); organizationMemberships.add(organizationMembership); - emit(OrgMembershipAddedState(orgMemberships: organizationMemberships, response: status)); + emit(OrgMembershipAddedState( response: status)); }else{ - emit(OrgMembershipAddedState(orgMemberships: organizationMemberships, response: status)); + emit(OrgMembershipAddedState( response: status)); } }catch(e){ emit(OrganizationMembershipErrorState(message: e.toString())); @@ -72,11 +70,10 @@ class OrganizationMembershipBloc extends Bloc element.agency!.id == event.org.agency!.id ); - List orgmemberships = event.organizationMemberships; - emit(OrgMembershipDeletedState(organizationMemberships: orgmemberships, success: success)); + organizationMemberships.removeWhere((element) => element.agency!.id == event.org.agency!.id ); + emit(OrgMembershipDeletedState(success: success)); }else{ - emit(OrgMembershipDeletedState(organizationMemberships: organizationMemberships, success: success)); + emit(OrgMembershipDeletedState(success: success)); } }catch(e){ emit(OrganizationMembershipErrorState(message: e.toString())); diff --git a/lib/bloc/profile/other_information/org_membership/organization_membership_event.dart b/lib/bloc/profile/other_information/org_membership/organization_membership_event.dart index 2be3627..a85a91a 100644 --- a/lib/bloc/profile/other_information/org_membership/organization_membership_event.dart +++ b/lib/bloc/profile/other_information/org_membership/organization_membership_event.dart @@ -8,17 +8,15 @@ abstract class OrganizationMembershipEvent extends Equatable { } class LoadOrganizationMemberships extends OrganizationMembershipEvent{ - final List organizationMemberships; - const LoadOrganizationMemberships({required this.organizationMemberships}); @override - List get props => [organizationMemberships]; + List get props => []; } class GetOrganizationMembership extends OrganizationMembershipEvent{ final int? profileId; final String? token; - const GetOrganizationMembership({ this.profileId, this.token}); + const GetOrganizationMembership({this.profileId, this.token}); } class ShowAddOrgMembershipForm extends OrganizationMembershipEvent{ @@ -37,9 +35,9 @@ class DeleteOrgMemberShip extends OrganizationMembershipEvent{ final int profileId; final String token; final OrganizationMembership org; - final List organizationMemberships; - const DeleteOrgMemberShip({required this.profileId, required this.token, required this.org, required this.organizationMemberships}); + + const DeleteOrgMemberShip({required this.profileId, required this.token, required this.org,}); @override - List get props => [profileId,token,org,organizationMemberships]; + List get props => [profileId,token,org]; } diff --git a/lib/bloc/profile/other_information/org_membership/organization_membership_state.dart b/lib/bloc/profile/other_information/org_membership/organization_membership_state.dart index f84adb7..0680c75 100644 --- a/lib/bloc/profile/other_information/org_membership/organization_membership_state.dart +++ b/lib/bloc/profile/other_information/org_membership/organization_membership_state.dart @@ -27,18 +27,16 @@ class OrgmembershipLoadingState extends OrganizationMembershipState{ } class OrgMembershipDeletedState extends OrganizationMembershipState{ - final List organizationMemberships; final bool success; - const OrgMembershipDeletedState({required this.organizationMemberships, required this.success}); + const OrgMembershipDeletedState({ required this.success}); @override - List get props => [organizationMemberships,success]; + List get props => [success]; } class OrgMembershipAddedState extends OrganizationMembershipState{ - final List orgMemberships; final Map response; - const OrgMembershipAddedState({required this.orgMemberships, required this.response}); + const OrgMembershipAddedState({required this.response}); @override - List get props => [orgMemberships,response]; + List get props => [response]; } class AddOrgMembershipState extends OrganizationMembershipState{ diff --git a/lib/bloc/profile/primary_information/contact/contact_bloc.dart b/lib/bloc/profile/primary_information/contact/contact_bloc.dart index 9f8070d..5753c70 100644 --- a/lib/bloc/profile/primary_information/contact/contact_bloc.dart +++ b/lib/bloc/profile/primary_information/contact/contact_bloc.dart @@ -14,7 +14,7 @@ part 'contact_state.dart'; class ContactBloc extends Bloc { ContactBloc() : super(ContactInitial()) { List contactInformations = []; - List serviceTypes =[]; + List serviceTypes = []; //// get all contacts on((event, emit) { emit(ContactLoadingState()); @@ -26,72 +26,118 @@ class ContactBloc extends Bloc { } }); //// Load Contacts - on((event,emit){ - emit(ContactLoadedState(contactInformation: event.contactInformation)); + on((event, emit) { + emit(ContactLoadedState(contactInformation: contactInformations)); }); - //// show add form - on((event,emit)async{ - try{ - emit(ContactLoadingState()); - if(serviceTypes.isEmpty){ + //// show add form + on((event, emit) async { + try { + emit(ContactLoadingState()); + if (serviceTypes.isEmpty) { + serviceTypes = await ProfileUtilities.instance.getServiceType(); + } + emit(ContactAddingState(serviceTypes: serviceTypes)); + } catch (e) { + emit(ContactErrorState(message: e.toString())); + } + }); + ///// Show edit form + on((event, emit) async { + ServiceType serviceType; + List commServiceProvivers; + CommService serviceProvider; + try{ + if (serviceTypes.isEmpty) { serviceTypes = await ProfileUtilities.instance.getServiceType(); } - emit(ContactAddingState(serviceTypes: serviceTypes)); - }catch(e){ - emit(ContactErrorState(message: e.toString())); - } - }); - ///// Show edit form - on((event,emit)async{ - ServiceType serviceType; - List serviceProvivers; - ServiceProvider serviceProvider; - try{ - if(serviceTypes.isEmpty){ - serviceTypes = await ProfileUtilities.instance.getServiceType(); - } - serviceType = serviceTypes.firstWhere((var element){ - return event.contactInfo.commService!.serviceType!.id == element.id; + serviceType = serviceTypes.firstWhere((ServiceType element) { + return element.id == event.contactInfo.commService!.serviceType!.id; }); - serviceProvivers = await ContactService.instance.getServiceProvider(serviceTypeId: serviceType.id!); - serviceProvider = serviceProvivers.firstWhere((element) => element.id == event.contactInfo.commService!.serviceProvider!.id); - emit(ContactEditingState(serviceTypes: serviceTypes,selectedServiceType: serviceType,serviceProviders: serviceProvivers,selectedProvider: serviceProvider, contactInfo: event.contactInfo)); - }catch(e){ - emit(ContactErrorState(message: e.toString())); - } - }); - ////edit contact - on((event,emit)async{ - Map responseStatus = await ContactService.instance.update(profileId: event.profileId, token: event.token, contactInfo: event.contactInfo); - if(responseStatus['success']){ - ContactInfo contactInfo = ContactInfo.fromJson(responseStatus['data']['contact_info']); - contactInformations.removeWhere((ContactInfo element) => element.id == event.contactInfo.id); - contactInformations.add(contactInfo); - emit(ContactEditedState(contactInformation: contactInformations, response: responseStatus)); - }else{ - emit(ContactEditedState(contactInformation: contactInformations, response: responseStatus)); - } - }); - - //// add contact - try{ - - }catch(e){ - emit(ContactErrorState(message: e.toString())); - } - //// delete contact - on((event, emit)async{ - try{ - final bool success = await ContactService.instance.deleteContact(profileId: event.profileId, token: event.token, contactInfo: event.contactInfo); - if(success){ - contactInformations.removeWhere((element) => element.id == event.contactInfo.id); - emit(ContactDeletedState(contactInformation: contactInformations, succcess: success)); - }else{ - emit(ContactDeletedState(contactInformation: contactInformations, succcess: success)); + commServiceProvivers = await ContactService.instance + .getServiceProvider(serviceTypeId: serviceType.id!); + serviceProvider = commServiceProvivers.firstWhere((CommService element) => + element.id == event.contactInfo.commService!.id); + emit(ContactEditingState( + serviceTypes: serviceTypes, + selectedServiceType: serviceType, + commServiceProviders: commServiceProvivers, + selectedProvider: serviceProvider, + contactInfo: event.contactInfo)); + }catch(e){ + emit(ContactErrorState(message: e.toString())); } - }catch(e){ - emit(ContactErrorState(message: e.toString())); - } - }); + }); + ////edit contact + on((event, emit) async { + try { + Map responseStatus = await ContactService.instance + .update( + profileId: event.profileId, + token: event.token, + contactInfo: event.contactInfo); + if (responseStatus['success']) { + ContactInfo contactInfo = + ContactInfo.fromJson(responseStatus['data']['contact_info']); + contactInformations.removeWhere( + (ContactInfo element) => element.id == event.contactInfo.id); + contactInformations.add(contactInfo); + emit(ContactEditedState( + + response: responseStatus)); + } else { + emit(ContactEditedState( + + response: responseStatus)); + } + } catch (e) { + emit(ContactErrorState(message: e.toString())); + } + }); + + //// add contact + + on((event, emit) async { + try { + Map responseStatus = await ContactService.instance + .add( + profileId: event.profileId, + token: event.token, + contactInfo: event.contactInfo); + if (responseStatus['success']) { + ContactInfo contactInfo = + ContactInfo.fromJson(responseStatus['data']['contact_info']); + contactInformations.add(contactInfo); + emit(ContactEditedState( + + response: responseStatus)); + } else { + emit(ContactEditedState( + + response: responseStatus)); + } + } catch (e) { + emit(ContactErrorState(message: e.toString())); + } + }); + //// delete contact + on((event, emit) async { + try { + final bool success = await ContactService.instance.deleteContact( + profileId: event.profileId, + token: event.token, + contactInfo: event.contactInfo); + if (success) { + contactInformations + .removeWhere((element) => element.id == event.contactInfo.id); + emit(ContactDeletedState( + succcess: success)); + } else { + emit(ContactDeletedState( + succcess: success)); + } + } catch (e) { + emit(ContactErrorState(message: e.toString())); + } + }); } } diff --git a/lib/bloc/profile/primary_information/contact/contact_event.dart b/lib/bloc/profile/primary_information/contact/contact_event.dart index e5259eb..5e7106f 100644 --- a/lib/bloc/profile/primary_information/contact/contact_event.dart +++ b/lib/bloc/profile/primary_information/contact/contact_event.dart @@ -17,10 +17,9 @@ class GetContacts extends ContactEvent{ //// load contacts class LoadContacts extends ContactEvent{ - final List contactInformation; - const LoadContacts({required this.contactInformation}); + @override - List get props => [contactInformation]; + List get props => []; } //// show add form diff --git a/lib/bloc/profile/primary_information/contact/contact_state.dart b/lib/bloc/profile/primary_information/contact/contact_state.dart index 863f11c..af38743 100644 --- a/lib/bloc/profile/primary_information/contact/contact_state.dart +++ b/lib/bloc/profile/primary_information/contact/contact_state.dart @@ -30,11 +30,11 @@ class ContactAddingState extends ContactState{ //// Editing state class ContactEditingState extends ContactState{ final List serviceTypes; - final List serviceProviders; - final ServiceProvider selectedProvider; + final List commServiceProviders; + final CommService selectedProvider; final ServiceType selectedServiceType; final ContactInfo contactInfo; - const ContactEditingState({ required this.serviceTypes, required this.selectedServiceType, required this.selectedProvider, required this.serviceProviders, required this.contactInfo}); + const ContactEditingState({ required this.serviceTypes, required this.selectedServiceType, required this.selectedProvider, required this.commServiceProviders, required this.contactInfo}); @override List get props => [serviceTypes]; } @@ -42,11 +42,11 @@ class ContactEditingState extends ContactState{ //// added state class ContactAddedState extends ContactState{ - final List contactInformation; + final Map response; - const ContactAddedState({required this.contactInformation, required this.response}); + const ContactAddedState({ required this.response}); @override - List get props => [contactInformation,response]; + List get props => [response]; } @@ -54,19 +54,19 @@ class ContactAddedState extends ContactState{ //// edited state class ContactEditedState extends ContactState{ - final List contactInformation; + final Map response; - const ContactEditedState({required this.contactInformation, required this.response}); + const ContactEditedState({ required this.response}); @override - List get props => [contactInformation,response]; + List get props => [response]; } ////deleted state class ContactDeletedState extends ContactState{ - final List contactInformation; + final bool succcess; - const ContactDeletedState({required this.contactInformation, required this.succcess}); + const ContactDeletedState({required this.succcess}); @override - List get props => [contactInformation,succcess]; + List get props => [succcess]; } ////error state diff --git a/lib/bloc/profile/references/references_bloc.dart b/lib/bloc/profile/references/references_bloc.dart index 3564706..ba27258 100644 --- a/lib/bloc/profile/references/references_bloc.dart +++ b/lib/bloc/profile/references/references_bloc.dart @@ -104,7 +104,11 @@ class ReferencesBloc extends Bloc { event.personalReference.address!.cityMunicipality!.code == city.code); List barangays = await LocationUtils.instance.getBarangay(code: selectedCity.code.toString()); - selectedBarangay = barangays.firstWhere((Barangay barangay)=>event.personalReference.address!.barangay!.code == barangay.code); + if(event.personalReference.address?.barangay!=null){ + selectedBarangay = barangays.firstWhere((Barangay barangay)=>event.personalReference.address?.barangay?.code == barangay.code); + }else{ + selectedBarangay = null; + } emit(EditReferenceState( selectedRegion: selectedRegion, ref: event.personalReference, @@ -144,17 +148,16 @@ class ReferencesBloc extends Bloc { message: "Something went wrong. Please try again")); //// EDIT REFERENCES EVENT });on((event,emit)async{ - emit(ReferencesLoadingState()); Map status =await ReferencesServices.instace.update(ref: event.reference, token: event.token, profileId: event.profileId); if (status['success']) { PersonalReference ref = PersonalReference.fromJson(status['data']); references.removeWhere((PersonalReference element)=>element.id == event.reference.id); references.add(ref); emit( - ReferenceEditedState(personalRefs: references, response: status)); + ReferenceEditedState( response: status)); } else { emit( - ReferenceEditedState(personalRefs: references, response: status)); + ReferenceEditedState( response: status)); } }); @@ -171,10 +174,10 @@ class ReferencesBloc extends Bloc { PersonalReference ref = PersonalReference.fromJson(status['data']); references.add(ref); emit( - ReferencesAddedState(personalRefs: references, response: status)); + ReferencesAddedState( response: status)); } else { emit( - ReferencesAddedState(personalRefs: references, response: status)); + ReferencesAddedState( response: status)); } } catch (e) { emit(ReferencesErrorState(message: e.toString())); @@ -183,7 +186,6 @@ class ReferencesBloc extends Bloc { ////LOAD REFERENCE on((event, emit) { emit(ReferencesLoadingState()); - references = event.references; emit(ReferencesLoadedState(references: references)); }); @@ -196,9 +198,9 @@ class ReferencesBloc extends Bloc { event.references.removeWhere( (PersonalReference element) => element.id == event.refId); references = event.references; - emit(DeleteReferenceState(references: references, success: success)); + emit(DeleteReferenceState( success: success)); } else { - emit(DeleteReferenceState(references: references, success: success)); + emit(DeleteReferenceState(success: success)); } } catch (e) { emit(ReferencesErrorState(message: e.toString())); diff --git a/lib/bloc/profile/references/references_event.dart b/lib/bloc/profile/references/references_event.dart index 584bebb..5eedf7d 100644 --- a/lib/bloc/profile/references/references_event.dart +++ b/lib/bloc/profile/references/references_event.dart @@ -7,63 +7,60 @@ abstract class ReferencesEvent extends Equatable { List get props => []; } -class GetReferences extends ReferencesEvent{ +class GetReferences extends ReferencesEvent { final int profileId; final String token; const GetReferences({required this.profileId, required this.token}); - @override - List get props => [profileId,token]; + @override + List get props => [profileId, token]; } -class ShowAddReferenceForm extends ReferencesEvent{ - -} +class ShowAddReferenceForm extends ReferencesEvent {} -class ShowEditReferenceForm extends ReferencesEvent{ +class ShowEditReferenceForm extends ReferencesEvent { final PersonalReference personalReference; const ShowEditReferenceForm({required this.personalReference}); - @override + @override List get props => [personalReference]; - -} -class CallErrorState extends ReferencesEvent{ - } +class CallErrorState extends ReferencesEvent {} -class AddReference extends ReferencesEvent{ +class AddReference extends ReferencesEvent { final PersonalReference reference; final String token; final int profileId; - const AddReference({required this.profileId, required this.reference,required this.token}); - @override - List get props => [profileId,token,reference]; -} -class EditReference extends ReferencesEvent{ - final PersonalReference reference; - final String token; - final int profileId; - const EditReference({required this.profileId, required this.reference,required this.token}); - @override - List get props => [profileId,token,reference]; + const AddReference( + {required this.profileId, required this.reference, required this.token}); + @override + List get props => [profileId, token, reference]; } -class DeleteReference extends ReferencesEvent{ - final Listreferences; +class EditReference extends ReferencesEvent { + final PersonalReference reference; + final String token; + final int profileId; + const EditReference( + {required this.profileId, required this.reference, required this.token}); + @override + List get props => [profileId, token, reference]; +} + +class DeleteReference extends ReferencesEvent { + final List references; final int profileId; final String token; final int refId; - const DeleteReference({required this.profileId, required this.refId, required this.references, required this.token}); - @override - List get props => [profileId,token,refId,references]; -} - -class LoadReferences extends ReferencesEvent{ - final List references; - const LoadReferences({required this.references}); + const DeleteReference( + {required this.profileId, + required this.refId, + required this.references, + required this.token}); @override - List get props => [references]; + List get props => [profileId, token, refId, references]; } - - +class LoadReferences extends ReferencesEvent { + @override + List get props => []; +} diff --git a/lib/bloc/profile/references/references_state.dart b/lib/bloc/profile/references/references_state.dart index 207a3fe..b3a07d3 100644 --- a/lib/bloc/profile/references/references_state.dart +++ b/lib/bloc/profile/references/references_state.dart @@ -28,20 +28,19 @@ class ReferencesErrorState extends ReferencesState { class ReferencesLoadingState extends ReferencesState {} class ReferencesAddedState extends ReferencesState { - final List personalRefs; final Map response; const ReferencesAddedState( - {required this.personalRefs, required this.response}); + { required this.response}); @override - List get props => [personalRefs, response]; + List get props => [ response]; } class ReferenceEditedState extends ReferencesState { - final List personalRefs; + final Map response; const ReferenceEditedState( - {required this.personalRefs, required this.response}); + { required this.response}); @override - List get props => [personalRefs, response]; + List get props => [ response]; } class EditReferenceState extends ReferencesState { @@ -89,7 +88,6 @@ class AddReferenceState extends ReferencesState { } class DeleteReferenceState extends ReferencesState { - final List references; final bool success; - const DeleteReferenceState({required this.references, required this.success}); + const DeleteReferenceState({ required this.success}); } diff --git a/lib/bloc/profile/workHistory/workHistory_bloc.dart b/lib/bloc/profile/workHistory/workHistory_bloc.dart index 4f42748..d19eca5 100644 --- a/lib/bloc/profile/workHistory/workHistory_bloc.dart +++ b/lib/bloc/profile/workHistory/workHistory_bloc.dart @@ -24,9 +24,12 @@ class WorkHistoryBloc extends Bloc { on((event, emit) async { emit(WorkHistoryLoadingState()); try { - List works = await WorkHistoryService.instance - .getWorkExperiences(event.profileId, event.token); - workExperiences = works; + if (workExperiences.isEmpty) { + List works = await WorkHistoryService.instance + .getWorkExperiences(event.profileId!, event.token!); + workExperiences = works; + } + emit(WorkHistoryLoaded(workExperiences: workExperiences)); } catch (e) { emit(WorkHistoryErrorState(message: e.toString())); @@ -35,34 +38,29 @@ class WorkHistoryBloc extends Bloc { ///// LOAD WORK HISTORIES on((event, emit) { emit(WorkHistoryLoadingState()); - workExperiences = event.workHistories; emit(WorkHistoryLoaded(workExperiences: workExperiences)); }); ////DELETE on((event, emit) async { - emit(WorkHistoryLoadingState()); try { final bool success = await WorkHistoryService.instance.delete( profileId: event.profileId, token: event.token, work: event.workHistory); if (success) { - event.workHistories.removeWhere( + workExperiences.removeWhere( (WorkHistory element) => element.id == event.workHistory.id); - List newWorkHistories = event.workHistories; - emit(DeletedState(success: success, workHistories: newWorkHistories)); + emit(DeletedState(success: success)); } else { - emit(DeletedState( - success: success, workHistories: event.workHistories)); + emit(DeletedState(success: success)); } } catch (e) { emit(WorkHistoryErrorState(message: e.toString())); } }); - //// ADD WORK HISTORIES + //// ADD WORK HISTORIES on((event, emit) async { try { - emit(WorkHistoryLoadingState()); Map status = await WorkHistoryService.instance.add( isPrivate: event.isPrivate, workHistory: event.workHistory, @@ -71,39 +69,39 @@ class WorkHistoryBloc extends Bloc { if (status['success']) { WorkHistory workHistory = WorkHistory.fromJson(status['data']); workExperiences.add(workHistory); - emit(WorkHistoryAddedState( - response: status, workExperiences: workExperiences)); + emit(WorkHistoryAddedState(response: status)); } else { - emit(WorkHistoryAddedState( - response: status, workExperiences: workExperiences)); + emit(WorkHistoryAddedState(response: status)); } } catch (e) { emit(WorkHistoryErrorState(message: e.toString())); } }); - ////UPDATE WORK HISTORY -on((event, emit)async{ - emit(WorkHistoryLoadingState()); - // try{ - Map status = await WorkHistoryService.instance.update(oldWorkHistory: event.oldWorkHistory, newWorkHistory: event.workHistory, token: event.token, profileId: event.profileId); - if(status['success']){ - WorkHistory workHistory = WorkHistory.fromJson(status['data']); - workExperiences.removeWhere((WorkHistory work) { - return work.id == event.oldWorkHistory.id; + on((event, emit) async { + try { + Map status = await WorkHistoryService.instance.update( + oldWorkHistory: event.oldWorkHistory, + newWorkHistory: event.workHistory, + token: event.token, + profileId: event.profileId); + if (status['success']) { + WorkHistory workHistory = WorkHistory.fromJson(status['data']); + workExperiences.removeWhere((WorkHistory work) { + return work.id == event.oldWorkHistory.id; + }); + workExperiences.add(workHistory); + emit(WorkHistoryEditedState(response: status)); + } else { + emit(WorkHistoryEditedState( + response: status, + )); + } + } catch (e) { + emit(WorkHistoryErrorState(message: e.toString())); + } }); - workExperiences.add(workHistory); - emit(WorkHistoryEditedState(workExperiences: workExperiences,response: status)); - }else{ - emit(WorkHistoryEditedState(response: status, workExperiences: workExperiences)); - } - - // }catch(e){ - // emit(WorkHistoryErrorState(message: e.toString())); - // } -}); - ////SHOW EDIT WORK HISTORIES on((event, emit) async { @@ -150,23 +148,31 @@ on((event, emit)async{ emit(WorkHistoryLoadingState()); try { /////POSITIONS------------------------------------------ - List positions = - await WorkHistoryService.instance.getAgencyPosition(); - agencyPositions = positions; + if (agencyPositions.isEmpty) { + List positions = + await WorkHistoryService.instance.getAgencyPosition(); + agencyPositions = positions; + } /////AGENCIES------------------------------------------ - List newAgencies = - await ProfileUtilities.instance.getAgecies(); - agencies = newAgencies; + if (agencies.isEmpty) { + List newAgencies = + await ProfileUtilities.instance.getAgecies(); + agencies = newAgencies; + } /////Category Agency------------------------------------------ - List categoryAgencies = - await ProfileUtilities.instance.agencyCategory(); - agencyCategory = categoryAgencies; + if (agencyCategory.isEmpty) { + List categoryAgencies = + await ProfileUtilities.instance.agencyCategory(); + agencyCategory = categoryAgencies; + } /////////------------------------------------- - List status = - WorkHistoryService.instance.getAppointmentStatusList(); - appointmentStatus = status; + if (appointmentStatus.isEmpty) { + List status = + WorkHistoryService.instance.getAppointmentStatusList(); + appointmentStatus = status; + } emit(AddWorkHistoryState( agencyPositions: agencyPositions, diff --git a/lib/bloc/profile/workHistory/workHistory_event.dart b/lib/bloc/profile/workHistory/workHistory_event.dart index 5a0dbe1..0f394b7 100644 --- a/lib/bloc/profile/workHistory/workHistory_event.dart +++ b/lib/bloc/profile/workHistory/workHistory_event.dart @@ -18,10 +18,8 @@ class GetWorkHistories extends WorkHistorytEvent{ } class LoadWorkHistories extends WorkHistorytEvent{ - final List workHistories; - const LoadWorkHistories({required this.workHistories}); @override - List get props => [workHistories]; + List get props => []; } class ShowAddWorkHistoryForm extends WorkHistorytEvent{ @@ -35,13 +33,12 @@ class ShowEditWorkHistoryForm extends WorkHistorytEvent{ } class DeleteWorkHistory extends WorkHistorytEvent{ - final List workHistories; final String token; final int profileId; final WorkHistory workHistory; - const DeleteWorkHistory({required this.profileId, required this.token, required this.workHistory, required this.workHistories}); + const DeleteWorkHistory({required this.profileId, required this.token, required this.workHistory}); @override - List get props => [token, profileId,workHistory, workHistories]; + List get props => [token, profileId,workHistory]; } class UpdateWorkHistory extends WorkHistorytEvent{ diff --git a/lib/bloc/profile/workHistory/workHistory_state.dart b/lib/bloc/profile/workHistory/workHistory_state.dart index 12a2037..2008857 100644 --- a/lib/bloc/profile/workHistory/workHistory_state.dart +++ b/lib/bloc/profile/workHistory/workHistory_state.dart @@ -53,25 +53,22 @@ class EditWorkHistoryState extends WorkHistoryState{ } class DeletedState extends WorkHistoryState{ - final List workHistories; final bool success; - const DeletedState({required this.success, required this.workHistories}); + const DeletedState({required this.success}); @override - List get props => [workHistories,success]; + List get props => [success]; } class WorkHistoryEditedState extends WorkHistoryState{ - final List workExperiences; final Map response; - const WorkHistoryEditedState({required this.response, required this.workExperiences}); + const WorkHistoryEditedState({required this.response}); @override - List get props => [workExperiences,response]; + List get props => [response]; } class WorkHistoryAddedState extends WorkHistoryState{ - final List workExperiences; final Map response; - const WorkHistoryAddedState({required this.response, required this.workExperiences}); + const WorkHistoryAddedState({required this.response}); @override - List get props => [workExperiences,response]; + List get props => [response]; } diff --git a/lib/bloc/user/user_bloc.dart b/lib/bloc/user/user_bloc.dart index 26e3e2f..5c9c705 100644 --- a/lib/bloc/user/user_bloc.dart +++ b/lib/bloc/user/user_bloc.dart @@ -34,6 +34,8 @@ class UserBloc extends Bloc { final String? saved = CREDENTIALS?.get('saved'); final String? username = CREDENTIALS?.get('username'); final String? password = CREDENTIALS?.get('password'); + debugPrint(username); + debugPrint(password); if (saved != null) { save = true; add(UserLogin(username: username, password: password)); diff --git a/lib/screens/profile/components/basic_information/contact_information/add_modal.dart b/lib/screens/profile/components/basic_information/contact_information/add_modal.dart index 362d88f..40c2494 100644 --- a/lib/screens/profile/components/basic_information/contact_information/add_modal.dart +++ b/lib/screens/profile/components/basic_information/contact_information/add_modal.dart @@ -31,14 +31,14 @@ class _AddContactInformationScreenState extends State { final formKey = GlobalKey(); ServiceType? selectedServiceType; - ServiceProvider? selectedProvider; - List serviceProviders = []; + CommService? selectedCommServiceProvider; + List commServiceProviders = []; bool callServiceType = false; bool primaryaContact = false; bool active = false; String? numberMail; var mobileFormatter = MaskTextInputFormatter( - mask: "+63 (##) ###-###", + mask: "+63 (###) ###-####", filter: {"#": RegExp(r"^[1-9][0-9]*$")}, type: MaskAutoCompletionType.lazy, initialText: "0"); @@ -82,7 +82,7 @@ class _AddContactInformationScreenState setState(() { callServiceType = true; }); - serviceProviders = await ContactService.instance + commServiceProviders = await ContactService.instance .getServiceProvider( serviceTypeId: selectedServiceType!.id!); setState(() { @@ -101,22 +101,22 @@ class _AddContactInformationScreenState child: ModalProgressHUD( color: Colors.transparent, inAsyncCall: callServiceType, - child: FormBuilderDropdown( + child: FormBuilderDropdown( validator: FormBuilderValidators.required( errorText: "This field is required"), name: "Service Provider", - items: serviceProviders.isEmpty + items: commServiceProviders.isEmpty ? [] - : serviceProviders - .map>( - (ServiceProvider e) { - return DropdownMenuItem( - value: e, child: Text(e.agency!.name!)); + : commServiceProviders + .map>( + (CommService e) { + return DropdownMenuItem( + value: e, child: Text(e.serviceProvider!.agency!.name!)); }).toList(), decoration: normalTextFieldStyle( "Communication Service *", ""), onChanged: (var serviceProvider) { - selectedProvider = serviceProvider; + selectedCommServiceProvider = serviceProvider; }), ), ), @@ -215,10 +215,7 @@ class _AddContactInformationScreenState numberMail = formKey.currentState!.value['number-mail']; - CommService commService = CommService( - id: null, - serviceType: selectedServiceType, - serviceProvider: selectedProvider); + CommService commService = selectedCommServiceProvider!; ContactInfo contactInfo = ContactInfo( id: null, active: active, diff --git a/lib/screens/profile/components/basic_information/contact_information/edit_modal.dart b/lib/screens/profile/components/basic_information/contact_information/edit_modal.dart index 9b6b1e4..e2c10a0 100644 --- a/lib/screens/profile/components/basic_information/contact_information/edit_modal.dart +++ b/lib/screens/profile/components/basic_information/contact_information/edit_modal.dart @@ -31,15 +31,15 @@ class _EditContactInformationScreenState extends State { final formKey = GlobalKey(); ServiceType? selectedServiceType; - ServiceProvider? selectedProvider; - List serviceProviders = []; + CommService? selectedCommProvider; + List commServiceProviders = []; String? numberMail; bool callServiceType = false; bool? primaryaContact; bool? active; var mobileFormatter = MaskTextInputFormatter( - mask: "+63 (##) ###-###", + mask: "+63 (###) ###-####", filter: {"#": RegExp(r"^[1-9][0-9]*$")}, type: MaskAutoCompletionType.lazy, initialText: "0"); @@ -56,8 +56,8 @@ class _EditContactInformationScreenState builder: (context, state) { if (state is ContactEditingState) { selectedServiceType = state.selectedServiceType; - selectedProvider = state.selectedProvider; - serviceProviders = state.serviceProviders; + selectedCommProvider = state.selectedProvider; + commServiceProviders = state.commServiceProviders; primaryaContact = state.contactInfo.primary!; active = state.contactInfo.active!; return FormBuilder( @@ -91,11 +91,11 @@ class _EditContactInformationScreenState setState(() { callServiceType = true; }); - serviceProviders = await ContactService.instance + commServiceProviders = await ContactService.instance .getServiceProvider( serviceTypeId: selectedServiceType!.id!); - selectedProvider = null; + selectedCommProvider = null; setState(() { setState(() { callServiceType = false; @@ -112,24 +112,24 @@ class _EditContactInformationScreenState child: ModalProgressHUD( color: Colors.transparent, inAsyncCall: callServiceType, - child: DropdownButtonFormField( - value: selectedProvider, + child: DropdownButtonFormField( + value: selectedCommProvider, validator: FormBuilderValidators.required( errorText: "This field is required"), - items: serviceProviders.isEmpty + items: commServiceProviders.isEmpty ? [] - : serviceProviders - .map>( - (ServiceProvider e) { + : commServiceProviders + .map>( + (CommService e) { return DropdownMenuItem< - ServiceProvider>( + CommService>( value: e, - child: Text(e.agency!.name!)); + child: Text(e.serviceProvider!.agency!.name!)); }).toList(), decoration: normalTextFieldStyle( "Communication Service *", ""), - onChanged: (var serviceProvider) { - selectedProvider = serviceProvider; + onChanged: (var commServiceProvider) { + selectedCommProvider = commServiceProvider; }), ), ), @@ -243,10 +243,7 @@ class _EditContactInformationScreenState if (formKey.currentState!.saveAndValidate()) { numberMail = formKey.currentState!.value['number-mail']; - CommService commService = CommService( - id: state.contactInfo.commService!.id, - serviceType: selectedServiceType, - serviceProvider: selectedProvider); + CommService commService = selectedCommProvider!; ContactInfo contactInfo = ContactInfo( id: state.contactInfo.id, active: active, diff --git a/lib/screens/profile/components/basic_information/contact_information_screen.dart b/lib/screens/profile/components/basic_information/contact_information_screen.dart index 721ab4c..b572c86 100644 --- a/lib/screens/profile/components/basic_information/contact_information_screen.dart +++ b/lib/screens/profile/components/basic_information/contact_information_screen.dart @@ -3,7 +3,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; -import 'package:fluttericon/font_awesome_icons.dart'; import 'package:unit2/bloc/profile/primary_information/contact/contact_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; @@ -14,9 +13,9 @@ import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/alerts.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; +import 'package:unit2/widgets/Leadings/close_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; - -import '../../../../bloc/profile/eligibility/eligibility_bloc.dart'; +import 'package:unit2/widgets/error_state.dart'; class ContactInformationScreen extends StatelessWidget { const ContactInformationScreen({ @@ -33,11 +32,15 @@ class ContactInformationScreen extends StatelessWidget { title: const Text(contactScreenTitle), centerTitle: true, backgroundColor: primary, - actions: [ + actions: context.watch().state is ContactLoadedState? [ AddLeading(onPressed: () { context.read().add(ShowAddForm()); }) - ], + ]:( context.watch().state is ContactAddingState || context.watch().state is ContactEditingState)?[ + CloseLeading(onPressed: (){ + context.read().add(LoadContacts()); + }) + ]:[] ), body: ProgressHUD( padding: const EdgeInsets.all(24), @@ -67,15 +70,14 @@ class ContactInformationScreen extends StatelessWidget { final progress = ProgressHUD.of(context); progress!.dismiss(); } - ////EDIT CONTACT STATE - if (state is ContactEditedState) { + ////ADDED CONTACT STATE + if (state is ContactAddedState) { if (state.response['success']) { successAlert(context, "Update Successfull!", state.response['message'], () { Navigator.of(context).pop(); context.read().add(LoadContacts( - contactInformation: - state.contactInformation)); + )); }); } else { errorAlert(context, "Update Failed", @@ -83,8 +85,27 @@ class ContactInformationScreen extends StatelessWidget { () { Navigator.of(context).pop(); context.read().add(LoadContacts( - contactInformation: - state.contactInformation)); + )); + }); + } + } + + ////EDIT CONTACT STATE + if (state is ContactEditedState) { + if (state.response['success']) { + successAlert(context, "Update Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context.read().add(LoadContacts( + )); + }); + } else { + errorAlert(context, "Update Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context.read().add(LoadContacts( + )); }); } } @@ -97,16 +118,14 @@ class ContactInformationScreen extends StatelessWidget { () { Navigator.of(context).pop(); context.read().add(LoadContacts( - contactInformation: - state.contactInformation)); + )); }); } else { errorAlert(context, "Deletion Failed", "Error deleting Contact Info", () { Navigator.of(context).pop(); context.read().add(LoadContacts( - contactInformation: - state.contactInformation)); + )); }); } } @@ -136,8 +155,7 @@ class ContactInformationScreen extends StatelessWidget { children: [ Container( decoration: box1(), - padding: const EdgeInsets.symmetric( - horizontal: 12, vertical: 8), + padding: const EdgeInsets.fromLTRB(12, 12, 0, 12), child: Row( children: [ Expanded( @@ -149,30 +167,18 @@ class ContactInformationScreen extends StatelessWidget { CrossAxisAlignment .start, children: [ - Text(numberMail, - style: Theme.of( - context) - .textTheme - .titleMedium! - .copyWith( - fontWeight: - FontWeight - .w500)), - const Divider(), - const SizedBox( - height: 5, - ), Row( children: [ Expanded( child: Text( - commService - .toString(), - style: Theme.of( - context) - .textTheme - .titleSmall, - ), + numberMail, + style: Theme.of( + context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight.w500)), ), state.contactInformation[index] .active == @@ -201,17 +207,40 @@ class ContactInformationScreen extends StatelessWidget { : const SizedBox() ], ), + const Divider(), const SizedBox( height: 5, ), - Text(state - .contactInformation[ - index] - .commService! - .serviceProvider! - .agency! - .name - .toString()), + Row( + children: [ + Flexible( + flex: 2, + child: Text( + commService + .toString().toUpperCase(), + style: Theme.of( + context) + .textTheme + .titleSmall, + ), + ), + const SizedBox( + + child: Text(" - "), + ), + Flexible( + flex: 1, + child: Text(state + .contactInformation[ + index] + .commService! + .serviceProvider! + .agency! + .name + .toString()), + ), + ], + ), ]), ), AppPopupMenu( @@ -298,6 +327,12 @@ class ContactInformationScreen extends StatelessWidget { return EditContactInformationScreen( profileId: profileId, token: token); } + if (state is ContactErrorState) { + return SomethingWentWrong( + message: state.message, onpressed: () { + context.read().add(LoadContacts()); + }); + } return Container(); }, ); diff --git a/lib/screens/profile/components/eligibility/add_modal.dart b/lib/screens/profile/components/eligibility/add_modal.dart index 7b896fa..07068e9 100644 --- a/lib/screens/profile/components/eligibility/add_modal.dart +++ b/lib/screens/profile/components/eligibility/add_modal.dart @@ -24,7 +24,10 @@ import '../../../../utils/location_utilities.dart'; import '../../../../utils/text_container.dart'; class AddEligibilityScreen extends StatefulWidget { - const AddEligibilityScreen({super.key}); + const AddEligibilityScreen( + {super.key, required this.profileId, required this.token}); + final int profileId; + final String token; @override State createState() => _AddEligibilityScreenState(); @@ -32,7 +35,7 @@ class AddEligibilityScreen extends StatefulWidget { class _AddEligibilityScreenState extends State { final formKey = GlobalKey(); - bool? overseas; + bool? overseas = false; DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); Region? selectedRegion; Province? selectedProvince; @@ -51,449 +54,400 @@ class _AddEligibilityScreenState extends State { String? license; @override Widget build(BuildContext context) { - ////USERBLOC - return BlocBuilder( + return BlocBuilder( + buildWhen: (previous, current) { + return false; + }, builder: (context, state) { - ////LOGGED IN USER STATE - if (state is UserLoggedIn) { - ////PROFIILE BLOC - token = state.userData!.user!.login!.token; - profileId = state.userData!.user!.login!.user!.profileId.toString(); - return BlocBuilder( - builder: (context, state) { - if (state is ProfileLoaded) { - return BlocBuilder( - buildWhen: (previous, current) { - if (state is EditEligibilityState) {} - return false; - }, - builder: (context, state) { - ////ADD ELIGIBILITY STATE - if (state is AddEligibilityState) { - return ProgressHUD( - child: Center( - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 25, horizontal: 18), - child: FormBuilder( - key: formKey, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - ////ELIGIBILITIES DROPDOWN - FormBuilderDropdown( - onChanged: (Eligibility? eligibility) { - selectedEligibility = eligibility; - }, - autovalidateMode: - AutovalidateMode.onUserInteraction, - validator: (value) => - value == null ? 'required' : null, - items: state.eligibilities - .map>( - (Eligibility eligibility) { - return DropdownMenuItem( - value: eligibility, - child: Text(eligibility.title)); - }).toList(), - name: "eligibility", - decoration: normalTextFieldStyle( - "Eligibility", "Eligibility")), - const SizedBox( - height: 20, + ////ADD ELIGIBILITY STATE + if (state is AddEligibilityState) { + return SingleChildScrollView( + child: SizedBox( + height: screenHeight * .95, + child: ProgressHUD( + child: Center( + child: Padding( + padding: + const EdgeInsets.symmetric(vertical: 25, horizontal: 18), + child: FormBuilder( + key: formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + ////ELIGIBILITIES DROPDOWN + FormBuilderDropdown( + onChanged: (Eligibility? eligibility) { + selectedEligibility = eligibility; + }, + autovalidateMode: + AutovalidateMode.onUserInteraction, + validator: (value) => + value == null ? 'required' : null, + items: state.eligibilities + .map>( + (Eligibility eligibility) { + return DropdownMenuItem( + value: eligibility, + child: Text(eligibility.title)); + }).toList(), + name: "eligibility", + decoration: normalTextFieldStyle( + "Eligibility", "Eligibility")), + const SizedBox( + height: 8, + ), + + SizedBox( + width: screenWidth, + child: Row( + children: [ + ////LICENSE NUMBER + Flexible( + flex: 1, + child: FormBuilderTextField( + onChanged: (value) { + license = value; + }, + name: 'license_number', + decoration: normalTextFieldStyle( + "license number", "license number"), ), - - SizedBox( - width: screenWidth, - child: Row( - children: [ - ////LICENSE NUMBER - Flexible( - flex: 1, - child: FormBuilderTextField( - onChanged: (value) { - license = value; - }, - name: 'license_number', - decoration: normalTextFieldStyle( - "license number", - "license number"), - ), - ), - const SizedBox( - width: 12, - ), - ////RATING - Flexible( - flex: 1, - child: FormBuilderTextField( - keyboardType: const TextInputType - .numberWithOptions(), - onChanged: (value) { - rating = value; - }, - name: 'rating', - decoration: normalTextFieldStyle( - 'rating %', 'rating'), - ), - ), - ], - ), + ), + const SizedBox( + width: 8, + ), + ////RATING + Flexible( + flex: 1, + child: FormBuilderTextField( + keyboardType: + const TextInputType.numberWithOptions(), + onChanged: (value) { + rating = value; + }, + name: 'rating', + decoration: normalTextFieldStyle( + 'rating %', 'rating'), ), - const SizedBox( - height: 20, - ), - SizedBox( - width: screenWidth, - child: Row( - children: [ - ////EXAM DATE - Flexible( - flex: 1, - child: DateTimePicker( - validator: FormBuilderValidators.required(errorText: "This field is required"), - use24HourFormat: false, - icon: const Icon( - Icons.date_range), - controller: examDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), - timeHintText: - "Date of Examination/Conferment", - decoration: - normalTextFieldStyle( - "Exam date", "") - .copyWith( - prefixIcon: - const Icon( - Icons.date_range, - color: Colors.black87, - )), - initialValue: null, - )), - const SizedBox( - width: 12, - ), - ////VALIDITY DATE - Flexible( - flex: 1, - child: DateTimePicker( - validator: FormBuilderValidators.required(errorText: "This field is required"), - controller: - validityDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), - decoration: normalTextFieldStyle( - "Validity date", - "Validity date") - .copyWith( - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - )), - initialValue: null, - ), - ), - ], - ), - ), - const SizedBox( - height: 20, - ), - Text( - "Placement of Examination/Conferment", - style: Theme.of(context) - .textTheme - .displaySmall! + ), + ], + ), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + ////EXAM DATE + Flexible( + flex: 1, + child: DateTimePicker( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + use24HourFormat: false, + icon: const Icon(Icons.date_range), + controller: examDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + timeHintText: + "Date of Examination/Conferment", + decoration: + normalTextFieldStyle("Exam date", "") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + initialValue: null, + )), + const SizedBox( + width: 8, + ), + ////VALIDITY DATE + Flexible( + flex: 1, + child: DateTimePicker( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + controller: validityDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + decoration: normalTextFieldStyle( + "Validity date", "Validity date") .copyWith( - fontSize: blockSizeVertical * 2), + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + initialValue: null, ), - const SizedBox( - height: 12, - ), - ////OVERSEAS ADDRESS SWITCH - Column( - children: [ - FormBuilderSwitch( - validator: FormBuilderValidators.required(errorText: 'This field is required'), - initialValue: overseas, - activeColor: second, - onChanged: (value) { - setState(() { - overseas = value; - }); - }, - decoration: - normalTextFieldStyle("", ''), - name: 'overseas', - title: - const Text("Overseas Address?"), - ), - const SizedBox( - height: 20, - ), - ////COUNTRY DROPDOWN - SizedBox( - child: overseas == true - ? FormBuilderDropdown( - initialValue: null, - validator: FormBuilderValidators.required(errorText: "This field is required"), - items: state.countries.map< - DropdownMenuItem< - Country>>( - (Country country) { - return DropdownMenuItem< - Country>( - value: country, - child: FittedBox( - child: Text( - country - .name!))); - }).toList(), - name: 'country', + ), + ], + ), + ), + const SizedBox( + height: 8, + ), + Text( + "Placement of Examination/Conferment", + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith(fontSize: blockSizeVertical * 2), + ), + const SizedBox( + height: 8, + ), + ////OVERSEAS ADDRESS SWITCH + Column( + children: [ + FormBuilderSwitch( + validator: FormBuilderValidators.required( + errorText: 'This field is required'), + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value; + }); + }, + decoration: normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Overseas Address?"), + ), + const SizedBox( + height: 8, + ), + ////COUNTRY DROPDOWN + SizedBox( + child: overseas == true + ? FormBuilderDropdown( + initialValue: null, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + items: state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox( + child: Text(country.name!))); + }).toList(), + name: 'country', + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) { + selectedCountry = value; + }, + ) + : Column( + children: [ + ////REGION DROPDOWN + FormBuilderDropdown( + autovalidateMode: AutovalidateMode + .onUserInteraction, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + //// region onchange + onChanged: (Region? region) async { + + if(selectedRegion != region){ + setState(() { + provinceCall = true; + }); + selectedRegion = region; + getProvinces(); + } + }, + initialValue: selectedRegion, + decoration: normalTextFieldStyle( + "Region*", "Region"), + name: 'region', + items: state.regions + .map>( + (Region region) { + return DropdownMenuItem( + value: region, + child: Text( + region.description!)); + }).toList(), + ), + const SizedBox( + height: 8, + ), + ////PROVINCE DROPDOWN + SizedBox( + height: 70, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + value: selectedProvince, + onChanged: + (Province? province) { + + if(selectedProvince != province){ + setState(() { + cityCall = true; + }); + selectedProvince = province; + getCities(); + } + }, + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province province) { + return DropdownMenuItem( + value: province, + child: FittedBox( + child: Text(province + .description!), + )); + }).toList(), + decoration: + normalTextFieldStyle( + "Province*", + "Province")), + ), + ), + + //// CityMunicipalities dropdown + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: DropdownButtonFormField< + CityMunicipality>( + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + onChanged: + (CityMunicipality? city) { + selectedMunicipality = city; + }, decoration: normalTextFieldStyle( - "Country*", - "Country"), - onChanged: - (Country? value) { - selectedCountry = value; - }, - ) - : Column( - children: [ - ////REGION DROPDOWN - FormBuilderDropdown< - Region?>( - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: FormBuilderValidators.required(errorText: "This field is required"), - //// region onchange - onChanged: (Region? - region) async { - setState(() { - provinceCall = true; - }); - selectedRegion = - region; - getProvinces(); - }, - initialValue: - selectedRegion, - decoration: - normalTextFieldStyle( - "Region*", - "Region"), - name: 'region', - items: state.regions.map< - DropdownMenuItem< - Region>>((Region - region) { - return DropdownMenuItem< - Region>( - value: region, - child: Text(region - .description!)); - }).toList(), - ), - const SizedBox( - height: 20, - ), - ////PROVINCE DROPDOWN - SizedBox( - height: 70, - child: ModalProgressHUD( - color: Colors - .transparent, - inAsyncCall: - provinceCall, - child: DropdownButtonFormField< - Province?>( - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - value: - selectedProvince, - onChanged: - (Province? - province) { - setState(() { - cityCall = - true; - }); - selectedProvince = - province; - getCities(); - }, - items: provinces == - null - ? [] - : provinces!.map< - DropdownMenuItem< - Province>>((Province - province) { - return DropdownMenuItem( - value: - province, - child: - FittedBox( - child: - Text(province.description!), - )); - }).toList(), - decoration: - normalTextFieldStyle( - "Province*", - "Province")), - ), - ), - - //// CityMunicipalities dropdown - SizedBox( - height: 70, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: cityCall, - child: DropdownButtonFormField< - CityMunicipality>( - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - onChanged: - (CityMunicipality? - city) { - selectedMunicipality = - city; - }, - decoration: normalTextFieldStyle( - "Municipality*", - "Municipality"), - value: - selectedMunicipality, - items: citymuns == - null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality - c) { - return DropdownMenuItem( - value: - c, - child: Text( - c.description!)); - }).toList(), - ), - ), - ), - const SizedBox( - height: 20, - ), - ], - )), - ], - ), - - const Expanded( - child: SizedBox(), - ), - - SizedBox( - width: screenWidth, - height: 60, - child: ElevatedButton( - style: mainBtnStyle(primary, - Colors.transparent, second), - onPressed: () { - //rating - double? rate = rating == null - ? null - : double.parse(rating!); - //lisence - String? licenseNumber = license; - CityMunicipality? cityMunicipality = - selectedMunicipality; - DateTime? examDate = - examDateController.text.isEmpty - ? null - : DateTime.parse( - examDateController - .text); - DateTime? validityDate = - validityDateController - .text.isEmpty - ? null - : DateTime.parse( - validityDateController - .text); - - ExamAddress examAddress = - ExamAddress( - barangay: null, - id: null, - addressCategory: null, - examAddressClass: null, - country: selectedCountry ?? - Country( - id: 175, - name: 'Philippines', - code: 'PH'), - cityMunicipality: - cityMunicipality); - EligibityCert eligibityCert = - EligibityCert( - id: null, - rating: rate, - examDate: examDate, - attachments: null, - eligibility: - selectedEligibility, - examAddress: examAddress, - validityDate: validityDate, - licenseNumber: - licenseNumber, - overseas: overseas); - if (formKey.currentState! - .saveAndValidate()) { - context - .read() - .add(AddEligibility( - eligibityCert: - eligibityCert, - profileId: profileId!, - token: token!)); - } - // context.read().add(AddEligibility(eligibityCert: eligibityCert, profileId: profileId, token: token)) - }, - child: const Text(submit)), - ), - const SizedBox( - height: 20, - ), - ]), + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), + ), + ), + ), + + ], + )), + ], ), - ), - ), - ); - } - return Container(); - }, - ); - } - return Container(); - }, + + const Expanded( + child: SizedBox(), + ), + + SizedBox( + width: screenWidth, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + //rating + double? rate = rating == null + ? null + : double.parse(rating!); + //lisence + String? licenseNumber = license; + CityMunicipality? cityMunicipality = + selectedMunicipality; + DateTime? examDate = examDateController + .text.isEmpty + ? null + : DateTime.parse(examDateController.text); + DateTime? validityDate = + validityDateController.text.isEmpty + ? null + : DateTime.parse( + validityDateController.text); + + ExamAddress examAddress = ExamAddress( + barangay: null, + id: null, + addressCategory: null, + examAddressClass: null, + country: selectedCountry ?? + Country( + id: 175, + name: 'Philippines', + code: 'PH'), + cityMunicipality: cityMunicipality); + EligibityCert eligibityCert = EligibityCert( + id: null, + rating: rate, + examDate: examDate, + attachments: null, + eligibility: selectedEligibility, + examAddress: examAddress, + validityDate: validityDate, + licenseNumber: licenseNumber, + overseas: overseas); + if (formKey.currentState!.saveAndValidate()) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add( + AddEligibility( + eligibityCert: eligibityCert, + profileId: + widget.profileId.toString(), + token: widget.token)); + } + // context.read().add(AddEligibility(eligibityCert: eligibityCert, profileId: profileId, token: token)) + }, + child: const Text(submit)), + ), + const SizedBox( + height: 20, + ), + ]), + ), + ), + ), + ), + ), ); } + return Container(); }, ); diff --git a/lib/screens/profile/components/eligibility/edit_modal.dart b/lib/screens/profile/components/eligibility/edit_modal.dart index 24df914..b2ffb29 100644 --- a/lib/screens/profile/components/eligibility/edit_modal.dart +++ b/lib/screens/profile/components/eligibility/edit_modal.dart @@ -2,11 +2,10 @@ import 'package:date_time_picker/date_time_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:intl/intl.dart'; import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; -import 'package:unit2/bloc/profile/profile_bloc.dart'; -import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/location/city.dart'; import 'package:unit2/model/profile/eligibility.dart'; import 'package:unit2/model/utils/eligibility.dart'; @@ -23,7 +22,9 @@ import '../../../../utils/text_container.dart'; class EditEligibilityScreen extends StatefulWidget { final EligibityCert eligibityCert; - const EditEligibilityScreen({super.key, required this.eligibityCert}); + final int profileId; + final String token; + const EditEligibilityScreen({super.key, required this.eligibityCert, required this.profileId, required this.token}); @override State createState() => _EditEligibilityScreenState(); @@ -52,20 +53,10 @@ class _EditEligibilityScreenState extends State { final validityDateController = TextEditingController(); @override Widget build(BuildContext context) { - //USERBLOC - return BlocBuilder( - builder: (context, state) { - //LOGGED IN USER STATE - if (state is UserLoggedIn) { - //PROFIILE BLOC - token = state.userData!.user!.login!.token; - profileId = state.userData!.user!.login!.user!.profileId.toString(); - return BlocBuilder( - builder: (context, state) { - if(state is ProfileLoaded){ + return BlocBuilder( buildWhen: (previous, current) { - if (state is EditEligibilityState) {} + return false; }, builder: (context, state) { @@ -194,6 +185,8 @@ class _EditEligibilityScreenState extends State { Flexible( flex: 1, child: DateTimePicker( + validator: FormBuilderValidators.required(errorText: "This field is required"), + use24HourFormat: false, controller: validityDateController, firstDate: DateTime(1970), @@ -514,6 +507,11 @@ class _EditEligibilityScreenState extends State { overseas: overseas); if (formKey.currentState! .saveAndValidate()) { + final progress = + ProgressHUD.of( + context); + progress!.showWithText( + "Loading..."); context.read().add( UpdateEligibility( eligibityCert: eligibityCert, @@ -521,8 +519,8 @@ class _EditEligibilityScreenState extends State { .eligibityCert .eligibility! .id, - profileId: profileId!, - token: token!)); + profileId:widget.profileId.toString(), + token: widget.token)); } }, child: const Text(submit)), @@ -534,15 +532,8 @@ class _EditEligibilityScreenState extends State { ), ), ); - } - return Container(); - }, - ); - } - return Container(); - }, - ); + } return Container(); }, diff --git a/lib/screens/profile/components/eligibility_screen.dart b/lib/screens/profile/components/eligibility_screen.dart index 96e43d5..34c1af2 100644 --- a/lib/screens/profile/components/eligibility_screen.dart +++ b/lib/screens/profile/components/eligibility_screen.dart @@ -16,6 +16,7 @@ import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/Leadings/close_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; +import 'package:unit2/widgets/error_state.dart'; import '../../../bloc/profile/eligibility/eligibility_bloc.dart'; import '../../../utils/alerts.dart'; @@ -25,13 +26,14 @@ class EligibiltyScreen extends StatelessWidget { @override Widget build(BuildContext context) { String? token; - String? profileId; + int? profileId; return WillPopScope( onWillPop: () async { return true; }, child: Scaffold( + resizeToAvoidBottomInset: true, appBar: AppBar( title: context.watch().state is AddEligibilityState ? const Text("Add Eligiblity") @@ -40,8 +42,8 @@ class EligibiltyScreen extends StatelessWidget { : const Text(elibilityScreenTitle), centerTitle: true, backgroundColor: primary, - actions: (context.watch().state is EligibilityLoaded || - context.watch().state is ProfileLoading) + actions: (context.watch().state is EligibilityLoaded) + ? [ AddLeading(onPressed: () { context @@ -49,19 +51,18 @@ class EligibiltyScreen extends StatelessWidget { .add(ShowAddEligibilityForm()); }) ] - : [ + :(context.watch().state is AddEligibilityState || context.watch().state is EditEligibilityState)? [ CloseLeading(onPressed: () { - context.read().add(GetEligibilities( - profileId: int.parse(profileId!), token: token!)); + context.read().add(const LoadEligibility()); }) - ], + ]:[], ), body: BlocBuilder( builder: (context, state) { if (state is UserLoggedIn) { token = state.userData!.user!.login!.token; profileId = - state.userData!.user!.login!.user!.profileId.toString(); + state.userData!.user!.login!.user!.profileId; return BlocBuilder( builder: (context, state) { if(state is ProfileLoaded){ @@ -82,7 +83,9 @@ class EligibiltyScreen extends StatelessWidget { state is EditEligibilityState || state is DeletedState || state is EligibilityAddedState || - state is EligibilityEditedState) { + state is EligibilityEditedState || + state is EligibilityErrorState + ) { final progress = ProgressHUD.of(context); progress!.dismiss(); } @@ -93,15 +96,15 @@ class EligibiltyScreen extends StatelessWidget { "Eligibility has been deleted successfully", () { Navigator.of(context).pop(); - context.read().add(LoadEligibility( - eligibilities: state.eligibilities)); + context.read().add(const LoadEligibility( + )); }); } else { errorAlert(context, "Deletion Failed", "Error deleting eligibility", () { Navigator.of(context).pop(); - context.read().add(LoadEligibility( - eligibilities: state.eligibilities)); + context.read().add(const LoadEligibility( + )); }); } } @@ -111,16 +114,16 @@ class EligibiltyScreen extends StatelessWidget { successAlert(context, "Adding Successfull!", state.response['message'], () { Navigator.of(context).pop(); - context.read().add(LoadEligibility( - eligibilities: state.eligibilities)); + context.read().add(const LoadEligibility( + )); }); } else { errorAlert(context, "Adding Failed", "Something went wrong. Please try again.", () { Navigator.of(context).pop(); - context.read().add(LoadEligibility( - eligibilities: state.eligibilities)); + context.read().add(const LoadEligibility( + )); }); } } @@ -130,16 +133,16 @@ class EligibiltyScreen extends StatelessWidget { successAlert(context, "Update Successfull!", state.response['message'], () { Navigator.of(context).pop(); - context.read().add(LoadEligibility( - eligibilities: state.eligibilities)); + context.read().add(const LoadEligibility( + )); }); } else { errorAlert(context, "Update Failed", "Something went wrong. Please try again.", () { Navigator.of(context).pop(); - context.read().add(LoadEligibility( - eligibilities: state.eligibilities)); + context.read().add(const LoadEligibility( + )); }); } } @@ -221,27 +224,29 @@ class EligibiltyScreen extends StatelessWidget { const Offset(-10, -10), elevation: 3, onSelected: (value) { - final progress = + + ////delete eligibilty-= = = = = = = = =>> + if (value == 2) { + + confirmAlert(context, + () { + final progress = ProgressHUD.of( context); progress!.showWithText( "Loading..."); - ////delete eligibilty-= = = = = = = = =>> - if (value == 2) { - confirmAlert(context, - () { BlocProvider.of< EligibilityBloc>( context) .add(DeleteEligibility( - eligibilities: state - .eligibilities, + + eligibilityId: state .eligibilities[ index] .id!, profileId: - profileId!, + profileId.toString(), token: token!)); }, "Delete?", @@ -249,6 +254,11 @@ class EligibiltyScreen extends StatelessWidget { } if (value == 1) { ////edit eligibilty-= = = = = = = = =>> + final progress = + ProgressHUD.of( + context); + progress!.showWithText( + "Loading..."); EligibityCert eligibityCert = state.eligibilities[ @@ -312,15 +322,17 @@ class EligibiltyScreen extends StatelessWidget { } if (state is EditEligibilityState) { return EditEligibilityScreen( + profileId: profileId!, + token: token!, eligibityCert: state.eligibityCert); } if (state is AddEligibilityState) { - return const AddEligibilityScreen(); + return AddEligibilityScreen(token: token!,profileId: profileId!,); } if (state is EligibilityErrorState) { - return Center( - child: Text(state.message), - ); + return SomethingWentWrong(message: state.message, onpressed: (){ + context.read().add(LoadEligibility(token: token,profileId: profileId)); + }); } return Container( color: Colors.grey.shade200, diff --git a/lib/screens/profile/components/loading_screen.dart b/lib/screens/profile/components/loading_screen.dart index 36952e2..0d469b9 100644 --- a/lib/screens/profile/components/loading_screen.dart +++ b/lib/screens/profile/components/loading_screen.dart @@ -135,21 +135,20 @@ class LoadingScreen extends StatelessWidget { ), Center( child: Container( - height: 120, - width: 120, + height: 80, + width: 80, decoration:const BoxDecoration( color: Colors.black87, - borderRadius: BorderRadius.all(Radius.circular(25)) + borderRadius: BorderRadius.all(Radius.circular(8)) ), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: const[ SpinKitFadingCircle( - + size: 42, color: Colors.white), - SizedBox(height: 10,), - Text("Loading Profile",textAlign: TextAlign.center, style: TextStyle(color: Colors.white,fontSize: 10),) + ], ), ), diff --git a/lib/screens/profile/components/other_information/org_membership/add_modal.dart b/lib/screens/profile/components/other_information/org_membership/add_modal.dart index 0e019f7..b2e60df 100644 --- a/lib/screens/profile/components/other_information/org_membership/add_modal.dart +++ b/lib/screens/profile/components/other_information/org_membership/add_modal.dart @@ -5,11 +5,8 @@ import 'package:fluttericon/font_awesome_icons.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:searchfield/searchfield.dart'; import 'package:unit2/bloc/profile/other_information/org_membership/organization_membership_bloc.dart'; -import 'package:unit2/bloc/profile/profile_bloc.dart'; -import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/utils/agency.dart'; import 'package:unit2/utils/text_container.dart'; - import '../../../../../model/utils/category.dart'; import '../../../../../theme-data.dart/box_shadow.dart'; import '../../../../../theme-data.dart/btn-style.dart'; @@ -17,7 +14,10 @@ import '../../../../../theme-data.dart/colors.dart'; import '../../../../../theme-data.dart/form-style.dart'; class AddOrgMemberShipScreen extends StatefulWidget { - const AddOrgMemberShipScreen({super.key}); + final int profileId; + final String token; + const AddOrgMemberShipScreen( + {super.key, required this.profileId, required this.token}); @override State createState() => _AddOrgMemberShipScreenState(); @@ -34,339 +34,290 @@ Agency? newAgency; bool showIsPrivateRadio = false; bool? isPrivate = false; final _formKey = GlobalKey(); - int? profileId; - String? token; class _AddOrgMemberShipScreenState extends State { @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocBuilder( builder: (context, state) { - if (state is UserLoggedIn) { - token = state.userData!.user!.login!.token; - profileId = state.userData!.user!.login!.user!.profileId; - return BlocBuilder( - builder: (context, state) { - if (state is ProfileLoaded) { - return BlocBuilder( - builder: (context, state) { - if (state is AddOrgMembershipState) { - return SingleChildScrollView( - child: FormBuilder( - key: _formKey, - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 25, horizontal: 18), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: - CrossAxisAlignment.center, - children: [ - const SizedBox( - height: 100, - ), - StatefulBuilder( - builder: (context, setState) { - //// AGENCY SEARCHFIELD - return Column( - children: [ - SearchField( - itemHeight: 70, - suggestions: state.agencies - .map((Agency agency) => - SearchFieldListItem( - agency.name!, - item: agency, - child: ListTile( - title: Text( - agency.name! - .toUpperCase(), - overflow: - TextOverflow - .ellipsis, - ), - subtitle: Text(agency - .privateEntity == - true - ? "Private" - : agency.privateEntity == - false - ? "Government" - : ""), - ))) - .toList(), - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - focusNode: agencyFocusNode, - searchInputDecoration: - normalTextFieldStyle( - "Agency *", "") - .copyWith( - suffixIcon: - const Icon(Icons - .arrow_drop_down)), - ////agency suggestion tap - onSuggestionTap: (agency) { - setState(() { - selectedAgency = agency.item; - agencyFocusNode.unfocus(); - if (selectedAgency - ?.category == - null) { - showAgencyCategory = true; - showIsPrivateRadio = true; - } else { - showAgencyCategory = false; - showIsPrivateRadio = false; - } - }); - }, - emptyWidget: Container( - decoration: box1(), - height: 100, - child: Column( - mainAxisAlignment: - MainAxisAlignment - .center, - crossAxisAlignment: - CrossAxisAlignment - .center, - children: [ - const SizedBox( - height: 20, - ), - const Text( - "No result found..."), - const SizedBox( - height: 10, - ), - TextButton( - //// Add agency onpressed - onPressed: () { - showDialog( - context: - context, - builder: - (BuildContext - context) { - return AlertDialog( - title: const Text( - "Add Agency?"), - content: - SizedBox( - height: - 130, - child: - Column( - children: [ - TextFormField( - controller: - addAgencyController, - decoration: - normalTextFieldStyle("", ""), - ), - const SizedBox( - height: - 12, - ), - SizedBox( - width: double.infinity, - height: 50, - child: ElevatedButton( - style: mainBtnStyle(primary, Colors.transparent, second), - //// onpressed - onPressed: () { - setState(() { - newAgency = Agency(id: null, name: addAgencyController.text.toUpperCase(), category: null, privateEntity: null); - state.agencies.insert(0, newAgency!); - addAgencyController.clear(); - Navigator.pop(context); - }); - }, - child: const Text("Add"))), - ], - ), - ), - ); - }); - }, - child: const Text( - "Add position")) - ]), - ), - ), - const SizedBox( - height: 8, - ), - SizedBox( - child: showAgencyCategory - ? SearchField( - focusNode: - agencyCategoryFocusNode, - itemHeight: 70, - suggestions: state - .agencyCategories - .map((Category - category) => - SearchFieldListItem( - category - .name!, - item: - category, - child: - ListTile( - title: Text( - category - .name!), - subtitle: Text(category - .industryClass! - .name!), - ))) - .toList(), - emptyWidget: Container( - height: 100, - decoration: box1(), - child: const Center( - child: Text( - "No result found ...")), - ), - ////agency controller suggestion tap - onSuggestionTap: - (agencyCategory) { - setState(() { - selectedCategory = - agencyCategory - .item; - - agencyCategoryFocusNode - .unfocus(); - }); - }, - searchInputDecoration: - normalTextFieldStyle( - "Category *", - "") - .copyWith( - suffixIcon: - const Icon( - Icons - .arrow_drop_down)), - validator: - FormBuilderValidators - .required( - errorText: - "This field is required"), - ) - : const SizedBox(), - ), - - ////PRVIATE SECTOR - SizedBox( - child: showIsPrivateRadio - ? FormBuilderRadioGroup( - decoration: - InputDecoration( - border: - InputBorder.none, - label: Row( + if (state is AddOrgMembershipState) { + return SingleChildScrollView( + child: FormBuilder( + key: _formKey, + child: Padding( + padding: + const EdgeInsets.symmetric(vertical: 25, horizontal: 18), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox( + height: 100, + ), + StatefulBuilder(builder: (context, setState) { + //// AGENCY SEARCHFIELD + return Column( + children: [ + SearchField( + itemHeight: 70, + suggestions: state.agencies + .map((Agency agency) => + SearchFieldListItem(agency.name!, + item: agency, + child: ListTile( + title: Text( + agency.name!.toUpperCase(), + overflow: TextOverflow.ellipsis, + ), + subtitle: Text( + agency.privateEntity == true + ? "Private" + : agency.privateEntity == + false + ? "Government" + : ""), + ))) + .toList(), + validator: FormBuilderValidators.required( + errorText: "This field is required"), + focusNode: agencyFocusNode, + searchInputDecoration: normalTextFieldStyle( + "Agency *", "") + .copyWith( + suffixIcon: + const Icon(Icons.arrow_drop_down)), + ////agency suggestion tap + onSuggestionTap: (agency) { + setState(() { + selectedAgency = agency.item; + agencyFocusNode.unfocus(); + if (selectedAgency?.category == null) { + showAgencyCategory = true; + showIsPrivateRadio = true; + } else { + showAgencyCategory = false; + showIsPrivateRadio = false; + } + }); + }, + emptyWidget: Container( + decoration: box1(), + height: 100, + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.center, + children: [ + const SizedBox( + height: 20, + ), + const Text("No result found..."), + const SizedBox( + height: 10, + ), + TextButton( + //// Add agency onpressed + onPressed: () { + showDialog( + context: context, + builder: + (BuildContext context) { + return AlertDialog( + title: const Text( + "Add Agency?"), + content: SizedBox( + height: 130, + child: Column( children: [ - Text( - "Is this private sector? ", - style: Theme.of( - context) - .textTheme - .headlineSmall! - .copyWith( - fontSize: - 24), + TextFormField( + controller: + addAgencyController, + decoration: + normalTextFieldStyle( + "", ""), ), - const Icon(FontAwesome - .help_circled) + const SizedBox( + height: 12, + ), + SizedBox( + width: double + .infinity, + height: 50, + child: + ElevatedButton( + style: mainBtnStyle( + primary, + Colors + .transparent, + second), + //// onpressed + onPressed: + () { + setState( + () { + newAgency = Agency( + id: null, + name: addAgencyController.text.toUpperCase(), + category: null, + privateEntity: null); + state.agencies.insert(0, + newAgency!); + addAgencyController.clear(); + Navigator.pop(context); + }); + }, + child: const Text( + "Add"))), ], ), ), - - ////onvhange private sector - onChanged: (value) { - setState(() { - if (value - .toString() == - "YES") { - isPrivate = true; - } else { - isPrivate = false; - } - }); - }, - - name: 'isPrivate', - validator: - FormBuilderValidators - .required( - errorText: - "This field is required"), - options: ["YES", "NO"] - .map((lang) => - FormBuilderFieldOption( - value: - lang)) - .toList( - growable: - false), - ) - : const SizedBox()), - ], - ); - }), - ////SHOW CATEGORY AGENCY - - const SizedBox( - height: 24, - ), - SizedBox( - height: 60, - width: double.infinity, - child: ElevatedButton( - style: mainBtnStyle(primary, - Colors.transparent, second), - onPressed: () { - if (_formKey.currentState! - .saveAndValidate()) { - if(selectedAgency?.privateEntity != null){ - newAgency = selectedAgency; - }else{ - newAgency = Agency( - id: selectedAgency?.id, - name: selectedAgency!.name, - category: - selectedCategory, - privateEntity: isPrivate); - } - - context.read().add(AddOrgMembership(agency: newAgency!, profileId: profileId!, token: token!)); - setState(() { - showAgencyCategory = false; - showIsPrivateRadio = false; + ); }); - } + }, + child: const Text("Add position")) + ]), + ), + ), + const SizedBox( + height: 8, + ), + SizedBox( + child: showAgencyCategory + ? SearchField( + focusNode: agencyCategoryFocusNode, + itemHeight: 70, + suggestions: state.agencyCategories + .map((Category category) => + SearchFieldListItem( + category.name!, + item: category, + child: ListTile( + title: + Text(category.name!), + subtitle: Text(category + .industryClass! + .name!), + ))) + .toList(), + emptyWidget: Container( + height: 100, + decoration: box1(), + child: const Center( + child: + Text("No result found ...")), + ), + ////agency controller suggestion tap + onSuggestionTap: (agencyCategory) { + setState(() { + selectedCategory = + agencyCategory.item; + + agencyCategoryFocusNode.unfocus(); + }); + }, + searchInputDecoration: + normalTextFieldStyle( + "Category *", "") + .copyWith( + suffixIcon: const Icon( + Icons.arrow_drop_down)), + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + ) + : const SizedBox(), + ), + + ////PRVIATE SECTOR + SizedBox( + child: showIsPrivateRadio + ? FormBuilderRadioGroup( + decoration: InputDecoration( + border: InputBorder.none, + label: Row( + children: [ + Text( + "Is this private sector? ", + style: Theme.of(context) + .textTheme + .headlineSmall! + .copyWith(fontSize: 24), + ), + const Icon( + FontAwesome.help_circled) + ], + ), + ), + + ////onvhange private sector + onChanged: (value) { + setState(() { + if (value.toString() == "YES") { + isPrivate = true; + } else { + isPrivate = false; + } + }); }, - child: const Text(submit)), - ) - ]), - )), - ); - } - return Container(); - }, - ); - } - return Container(); - }, + + name: 'isPrivate', + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + options: ["YES", "NO"] + .map((lang) => + FormBuilderFieldOption( + value: lang)) + .toList(growable: false), + ) + : const SizedBox()), + ], + ); + }), + ////SHOW CATEGORY AGENCY + + const SizedBox( + height: 24, + ), + SizedBox( + height: 60, + width: double.infinity, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + if (_formKey.currentState!.saveAndValidate()) { + if (selectedAgency?.privateEntity != null) { + newAgency = selectedAgency; + } else { + newAgency = Agency( + id: selectedAgency?.id, + name: selectedAgency!.name, + category: selectedCategory, + privateEntity: isPrivate); + } + + context + .read() + .add(AddOrgMembership( + agency: newAgency!, + profileId: widget.profileId, + token: widget.token)); + setState(() { + showAgencyCategory = false; + showIsPrivateRadio = false; + }); + } + }, + child: const Text(submit)), + ) + ]), + )), ); } - return const Placeholder(); + return Container(); }, ); } diff --git a/lib/screens/profile/components/other_information/org_membership_screen.dart b/lib/screens/profile/components/other_information/org_membership_screen.dart index 7ade0c0..68cdc20 100644 --- a/lib/screens/profile/components/other_information/org_membership_screen.dart +++ b/lib/screens/profile/components/other_information/org_membership_screen.dart @@ -14,6 +14,7 @@ import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/Leadings/close_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; +import 'package:unit2/widgets/error_state.dart'; import '../../../../bloc/profile/other_information/org_membership/organization_membership_bloc.dart'; import '../../../../utils/alerts.dart'; import '../../../../utils/global.dart'; @@ -65,6 +66,7 @@ class OrgMembershipsScreen extends StatelessWidget { final progress = ProgressHUD.of(context); progress!.dismiss(); } + ////ADDED STATE if (state is OrgMembershipAddedState) { if (state.response['success']) { @@ -73,8 +75,7 @@ class OrgMembershipsScreen extends StatelessWidget { Navigator.of(context).pop(); context.read().add( LoadOrganizationMemberships( - organizationMemberships: - state.orgMemberships)); + )); }); } else { errorAlert(context, "Adding Failed", @@ -83,8 +84,7 @@ class OrgMembershipsScreen extends StatelessWidget { Navigator.of(context).pop(); context.read().add( LoadOrganizationMemberships( - organizationMemberships: - state.orgMemberships)); + )); }); } } @@ -96,7 +96,7 @@ class OrgMembershipsScreen extends StatelessWidget { Navigator.of(context).pop(); context.read().add( LoadOrganizationMemberships( - organizationMemberships: state.organizationMemberships)); + )); }); } else { errorAlert(context, "Deletion Failed", @@ -104,7 +104,7 @@ class OrgMembershipsScreen extends StatelessWidget { Navigator.of(context).pop(); context.read().add( LoadOrganizationMemberships( - organizationMemberships: state.organizationMemberships)); + )); }); } } @@ -161,14 +161,15 @@ class OrgMembershipsScreen extends StatelessWidget { offset: const Offset(-10, -10), elevation: 3, onSelected: (value) { - final progress = - ProgressHUD.of(context); - progress! - .showWithText("Loading..."); + ////delete orgmembership-= = = = = = = = =>> if (value == 1) { confirmAlert(context, () { - context.read().add(DeleteOrgMemberShip(profileId: profileId, token: token!, org: state.orgMemberships[index], organizationMemberships: state.orgMemberships)); + final progress = + ProgressHUD.of(context); + progress! + .showWithText("Loading..."); + context.read().add(DeleteOrgMemberShip(profileId: profileId, token: token!, org: state.orgMemberships[index])); }, "Delete?", "Confirm Delete?"); } @@ -198,9 +199,13 @@ class OrgMembershipsScreen extends StatelessWidget { }); } if (state is AddOrgMembershipState) { - return const AddOrgMemberShipScreen(); + return AlertDialog( + content: AddOrgMemberShipScreen(profileId: profileId,token: token!,) + ); }if(state is OrganizationMembershipErrorState){ - return Container(child: Text(state.message),); + return SomethingWentWrong(message: state.message, onpressed: (){ + context.read().add(GetOrganizationMembership(token: token,profileId: profileId)); + }); } return Container(); }, diff --git a/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart b/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart index 727b909..53a28a7 100644 --- a/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart +++ b/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart @@ -1,6 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; @@ -9,9 +7,7 @@ import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/other_information/skills_and_hobbies.dart'; import 'package:unit2/screens/profile/components/other_information/skills_hobbies/add_modal.dart'; -import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; -import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; diff --git a/lib/screens/profile/components/reference/add_modal.dart b/lib/screens/profile/components/reference/add_modal.dart index d3f73b9..6195f9c 100644 --- a/lib/screens/profile/components/reference/add_modal.dart +++ b/lib/screens/profile/components/reference/add_modal.dart @@ -4,14 +4,13 @@ import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; -import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/profile/references/references_bloc.dart'; -import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/location/address_category.dart'; import 'package:unit2/model/location/barangay.dart'; import 'package:unit2/model/profile/references.dart'; import 'package:unit2/theme-data.dart/btn-style.dart'; import 'package:unit2/theme-data.dart/form-style.dart'; +import 'package:unit2/utils/global.dart'; import '../../../../model/location/city.dart'; import '../../../../model/location/country.dart'; import '../../../../model/location/provinces.dart'; @@ -21,7 +20,10 @@ import '../../../../utils/location_utilities.dart'; import '../../../../utils/text_container.dart'; class AddReferenceScreen extends StatefulWidget { - const AddReferenceScreen({super.key}); + final int profileId; + final String token; + const AddReferenceScreen( + {super.key, required this.profileId, required this.token}); @override State createState() => _AddReferenceScreenState(); @@ -29,8 +31,6 @@ class AddReferenceScreen extends StatefulWidget { class _AddReferenceScreenState extends State { final formKey = GlobalKey(); - String? token; - String? profileId; bool provinceCall = false; bool cityCall = false; bool barangayCall = false; @@ -48,452 +48,376 @@ class _AddReferenceScreenState extends State { AddressCategory? selectedCategory; @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocBuilder( builder: (context, state) { - if (state is UserLoggedIn) { - token = state.userData!.user!.login!.token; - profileId = state.userData!.user!.login!.user!.profileId.toString(); - return BlocBuilder( - builder: (context, state) { - if (state is ProfileLoaded) { - return BlocBuilder( - builder: (context, state) { - if (state is AddReferenceState) { - return ProgressHUD( - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 25, horizontal: 18), - child: FormBuilder( - key: formKey, - child: Column( - children: [ - const SizedBox(height: 25), - Row( + if (state is AddReferenceState) { + return SingleChildScrollView( + child: SizedBox( + height: screenHeight * .95, + child: ProgressHUD( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 18), + child: FormBuilder( + key: formKey, + child: Column( + children: [ + const SizedBox(height: 25), + Row( + children: [ + ////LAST NAME + Flexible( + flex: 1, + child: FormBuilderTextField( + decoration: normalTextFieldStyle( + "Last name *", "Last name *"), + name: "lastname", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + ), + ), + const SizedBox( + width: 5, + ), + ////FIRST NAME + Flexible( + flex: 1, + child: FormBuilderTextField( + decoration: normalTextFieldStyle( + "First name *", "First name *"), + name: "firstname", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + ), + ), + ], + ), + const SizedBox( + height: 8, + ), + Row( + children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + decoration: normalTextFieldStyle( + "Middle name *", "Midlle name *"), + name: "middlename", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + ), + ), + const SizedBox( + width: 5, + ), + ////CATEGORY + Flexible( + flex: 1, + child: FormBuilderDropdown( + name: 'category', + validator: + FormBuilderValidators.required(errorText: ""), + decoration: + normalTextFieldStyle("Category", "Category"), + items: state.categories + .map>( + (AddressCategory cat) { + return DropdownMenuItem( + value: cat, + child: Text(cat.name!), + ); + }).toList(), + onChanged: (value) { + setState(() { + selectedCategory = value; + }); + }, + ), + ), + ], + ), + const SizedBox( + height: 8, + ), + + ////OVERSEAS ADDRESS + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Overseas Address?"), + ), + SizedBox( + height: overseas == true ? 8 : 0, + ), + SizedBox( + child: overseas == false + ? Column( children: [ - ////LAST NAME - Flexible( - flex: 1, - child: FormBuilderTextField( - decoration: normalTextFieldStyle( - "Last name *", "Last name *"), - name: "lastname", - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - ), + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + FormBuilderDropdown( + autovalidateMode: + AutovalidateMode.onUserInteraction, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + onChanged: (Region? region) async { + if(selectedRegion != region){ + setState(() { + provinceCall = true; + }); + selectedRegion = region; + getProvinces(); + } + }, + initialValue: null, + decoration: normalTextFieldStyle( + "Region*", "Region"), + name: 'region', + items: state.regions + .map>( + (Region region) { + return DropdownMenuItem( + value: region, + child: Text(region.description!)); + }).toList(), ), const SizedBox( - width: 5, + height: 8, ), - ////FIRST NAME - Flexible( - flex: 1, - child: FormBuilderTextField( - decoration: normalTextFieldStyle( - "First name *", "First name *"), - name: "firstname", - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - ), - ), - ], - ), - const SizedBox( - height: 8, - ), - Row( - children: [ - Flexible( - flex: 1, - child: FormBuilderTextField( - decoration: normalTextFieldStyle( - "Middle name *", "Midlle name *"), - name: "middlename", - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - ), - ), - const SizedBox( - width: 5, - ), - ////CATEGORY - Flexible( - flex: 1, - child: FormBuilderDropdown< - AddressCategory>( - name: 'category', - validator: - FormBuilderValidators.required( - errorText: ""), - decoration: normalTextFieldStyle( - "Category", "Category"), - items: state.categories.map< - DropdownMenuItem< - AddressCategory>>( - (AddressCategory cat) { - return DropdownMenuItem< - AddressCategory>( - value: cat, - child: Text(cat.name!), - ); - }).toList(), - onChanged: (value) { - setState(() { - selectedCategory = value; - }); - }, - ), - ), - ], - ), - const SizedBox( - height: 8, - ), - - ////OVERSEAS ADDRESS - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - setState(() { - overseas = value!; - }); - }, - decoration: normalTextFieldStyle("", ''), - name: 'overseas', - title: const Text("Overseas Address?"), - ), - SizedBox( - height: overseas == true ? 8 : 0, - ), - SizedBox( - child: overseas == false - ? Column( - children: [ - const SizedBox( - height: 12, - ), - ////REGION DROPDOWN - FormBuilderDropdown( - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - onChanged: - (Region? region) async { + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField( + autovalidateMode: AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null ? 'required' : null, + isExpanded: true, + value: selectedProvince, + onChanged: (Province? province) { + if(selectedProvince != province){ setState(() { - provinceCall = true; - }); - selectedRegion = region; - getProvinces(); - }, - initialValue: null, - decoration: - normalTextFieldStyle( - "Region*", "Region"), - name: 'region', - items: state.regions.map< - DropdownMenuItem< - Region>>( - (Region region) { - return DropdownMenuItem< - Region>( - value: region, - child: Text( - region.description!)); - }).toList(), - ), - const SizedBox( - height: 8, - ), - //// PROVINCE DROPDOWN - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: provinceCall, - child: DropdownButtonFormField< - Province?>( - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - value: selectedProvince, - onChanged: - (Province? province) { - setState(() { - cityCall = true; - }); - selectedProvince = - province; - getCities(); - }, - items: provinces == null - ? [] - : provinces!.map< - DropdownMenuItem< - Province>>( - (Province - province) { - return DropdownMenuItem( - value: - province, - child: - FittedBox( - child: Text( - province - .description!), - )); - }).toList(), - decoration: - normalTextFieldStyle( - "Province*", - "Province")), - ), - ), - ////CITY MUNICIPALITY - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: cityCall, - child: - DropdownButtonFormField< - CityMunicipality>( - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - isExpanded: true, - onChanged: - (CityMunicipality? - city) { - setState(() { - barangayCall = true; - }); - selectedMunicipality = - city; - getBarangays(); - }, - decoration: - normalTextFieldStyle( - "Municipality*", - "Municipality"), - value: selectedMunicipality, - items: citymuns == null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality - c) { - return DropdownMenuItem( - value: c, - child: Text(c - .description!)); - }).toList(), - ), - ), - ), - //// BARANGAY - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: barangayCall, - child: - DropdownButtonFormField< - Barangay>( - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - isExpanded: true, - onChanged: - (Barangay? baragay) { - selectedBarangay = - baragay; - }, - decoration: - normalTextFieldStyle( - "Barangay*", - "Barangay"), - value: selectedBarangay, - items: barangays == null - ? [] - : barangays!.map< - DropdownMenuItem< - Barangay>>( - (Barangay - barangay) { - return DropdownMenuItem( - value: barangay, - child: Text(barangay - .description!)); - }).toList(), - ), - ), - ), - ], - ) - //// COUNTRY DROPDOWN - : SizedBox( - height: 60, - child: FormBuilderDropdown( - initialValue: null, - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - items: state.countries.map< - DropdownMenuItem< - Country>>( - (Country country) { - return DropdownMenuItem< - Country>( - value: country, - child: FittedBox( - child: Text( - country.name!))); - }).toList(), - name: 'country', - decoration: normalTextFieldStyle( - "Country*", "Country"), - onChanged: (Country? value) { - selectedCountry = value; + cityCall = true; + }); + selectedProvince = province; + getCities(); + } }, - ), + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province province) { + return DropdownMenuItem( + value: province, + child: FittedBox( + child: Text(province + .description!), + )); + }).toList(), + decoration: normalTextFieldStyle( + "Province*", "Province")), + ), + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: DropdownButtonFormField< + CityMunicipality>( + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + isExpanded: true, + onChanged: (CityMunicipality? city) { + if(selectedMunicipality != city){ + setState(() { + barangayCall = true; + }); + selectedMunicipality = city; + getBarangays(); + } + }, + decoration: normalTextFieldStyle( + "Municipality*", "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality c) { + return DropdownMenuItem( + value: c, + child: + Text(c.description!)); + }).toList(), ), - ), - - FormBuilderTextField( - name: "mobile", - decoration: normalTextFieldStyle( - "Tel./Mobile *", "Tel./Mobile"), - validator: FormBuilderValidators.required( - errorText: "This field is required"), - ), - const Expanded( - child: SizedBox(), - ), - SizedBox( - width: double.infinity, + ), + ), + //// BARANGAY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: barangayCall, + child: DropdownButtonFormField( + + isExpanded: true, + onChanged: (Barangay? baragay) { + selectedBarangay = baragay; + }, + decoration: normalTextFieldStyle( + "Barangay*", "Barangay"), + value: selectedBarangay, + items: barangays == null + ? [] + : barangays!.map< + DropdownMenuItem>( + (Barangay barangay) { + return DropdownMenuItem( + value: barangay, + child: Text( + barangay.description!)); + }).toList(), + ), + ), + ), + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( height: 60, - child: ElevatedButton( - style: mainBtnStyle( - primary, Colors.transparent, second), - child: const Text(submit), - onPressed: () { - - PersonalReference? personalReference; - if (formKey.currentState! - .saveAndValidate()) { - String lastname = formKey - .currentState!.value['lastname']; - String firstname = formKey - .currentState!.value['firstname']; - String middlename = formKey - .currentState! - .value['middlename']; - String mobile = formKey - .currentState!.value['mobile']; - - - Region? region = selectedRegion; - Province? province = Province( - code: selectedProvince?.code, - description: - selectedProvince?.description, - region: region, - psgcCode: - selectedProvince?.psgcCode, - shortname: - selectedProvince?.shortname); - CityMunicipality? city = - CityMunicipality( - code: selectedMunicipality - ?.code, - description: - selectedMunicipality - ?.description, - province: province, - psgcCode: selectedMunicipality - ?.psgcCode, - zipcode: selectedMunicipality - ?.zipcode); - - Address address = Address( - id: null, - addressCategory: selectedCategory, - country: selectedCountry, - barangay: selectedBarangay, - addressClass: null, - cityMunicipality: city); - - if (selectedCountry != null) { - personalReference = - PersonalReference( - id: null, - address: Address( - id: null, - addressCategory: - selectedCategory, - country: selectedCountry, - barangay: null, - cityMunicipality: null, - addressClass: null), - lastName: lastname, - contactNo: mobile, - firstName: firstname, - middleName: middlename); - } else { - personalReference = - PersonalReference( - id: null, - address: address, - lastName: lastname, - contactNo: mobile, - firstName: firstname, - middleName: middlename); - } -final progress = ProgressHUD.of(context); -progress!.showWithText("Please wait..."); - context.read().add( - AddReference( - profileId: - int.parse(profileId!), - reference: personalReference, - token: token!)); - } + child: FormBuilderDropdown( + initialValue: null, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + items: state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox( + child: Text(country.name!))); + }).toList(), + name: 'country', + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) { + selectedCountry = value; }, ), ), - const SizedBox( - height: 20, - ), - ], - )), - ), - ); - } - return Container(); - }, - ); - } - return Container(); - }, + ), + + FormBuilderTextField( + name: "mobile", + decoration: normalTextFieldStyle( + "Tel./Mobile *", "Tel./Mobile"), + validator: FormBuilderValidators.required( + errorText: "This field is required"), + ), + const Expanded( + child: SizedBox(), + ), + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: + mainBtnStyle(primary, Colors.transparent, second), + child: const Text(submit), + onPressed: () { + PersonalReference? personalReference; + if (formKey.currentState!.saveAndValidate()) { + String lastname = + formKey.currentState!.value['lastname']; + String firstname = + formKey.currentState!.value['firstname']; + String middlename = + formKey.currentState!.value['middlename']; + String mobile = + formKey.currentState!.value['mobile']; + + Region? region = selectedRegion; + Province? province = Province( + code: selectedProvince?.code, + description: selectedProvince?.description, + region: region, + psgcCode: selectedProvince?.psgcCode, + shortname: selectedProvince?.shortname); + CityMunicipality? city = CityMunicipality( + code: selectedMunicipality?.code, + description: + selectedMunicipality?.description, + province: province, + psgcCode: selectedMunicipality?.psgcCode, + zipcode: selectedMunicipality?.zipcode); + + Address address = Address( + id: null, + addressCategory: selectedCategory, + country: selectedCountry, + barangay: selectedBarangay, + addressClass: null, + cityMunicipality: city); + + if (selectedCountry != null) { + personalReference = PersonalReference( + id: null, + address: Address( + id: null, + addressCategory: selectedCategory, + country: selectedCountry, + barangay: null, + cityMunicipality: null, + addressClass: null), + lastName: lastname, + contactNo: mobile, + firstName: firstname, + middleName: middlename); + } else { + personalReference = PersonalReference( + id: null, + address: address, + lastName: lastname, + contactNo: mobile, + firstName: firstname, + middleName: middlename); + } + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + context.read().add(AddReference( + profileId: widget.profileId, + reference: personalReference, + token: widget.token)); + } + }, + ), + ), + const SizedBox( + height: 20, + ), + ], + )), + ), + ), + ), ); } return Container(); diff --git a/lib/screens/profile/components/reference/edit_modal.dart b/lib/screens/profile/components/reference/edit_modal.dart index 6c02990..136081e 100644 --- a/lib/screens/profile/components/reference/edit_modal.dart +++ b/lib/screens/profile/components/reference/edit_modal.dart @@ -4,9 +4,7 @@ import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; -import '../../../../bloc/profile/profile_bloc.dart'; import '../../../../bloc/profile/references/references_bloc.dart'; -import '../../../../bloc/user/user_bloc.dart'; import '../../../../model/location/address_category.dart'; import '../../../../model/location/barangay.dart'; import '../../../../model/location/city.dart'; @@ -21,7 +19,9 @@ import '../../../../utils/location_utilities.dart'; import '../../../../utils/text_container.dart'; class EditReferenceScreen extends StatefulWidget { - const EditReferenceScreen({super.key}); + final String token; + final int profileId; + const EditReferenceScreen({super.key,required this.profileId, required this.token}); @override State createState() => _EditReferenceScreenState(); @@ -29,8 +29,6 @@ class EditReferenceScreen extends StatefulWidget { class _EditReferenceScreenState extends State { final formKey = GlobalKey(); - String? token; - String? profileId; bool provinceCall = false; bool cityCall = false; bool barangayCall = false; @@ -48,14 +46,7 @@ class _EditReferenceScreenState extends State { @override Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, state) { - if (state is UserLoggedIn) { - token = state.userData!.user!.login!.token; - profileId = state.userData!.user!.login!.user!.profileId.toString(); - return BlocBuilder( - builder: (context, state) { - if (state is ProfileLoaded) { + return BlocBuilder( buildWhen: (previous, current) => false, builder: (context, state) { @@ -525,6 +516,10 @@ class _EditReferenceScreenState extends State { PersonalReference? personalReference; if (formKey.currentState! .saveAndValidate()) { + final progress = + ProgressHUD.of(context); + progress!.showWithText( + "Please wait..."); String lastname = formKey .currentState!.value['lastname']; String firstname = formKey @@ -607,9 +602,9 @@ class _EditReferenceScreenState extends State { context.read().add( EditReference( profileId: - int.parse(profileId!), + widget.profileId, reference: personalReference, - token: token!)); + token: widget.token)); } }, ), @@ -624,14 +619,7 @@ class _EditReferenceScreenState extends State { } return Container(); }, - ); - } - return Container(); - }, - ); - } - return Container(); - }, + ); } } diff --git a/lib/screens/profile/components/references_screen.dart b/lib/screens/profile/components/references_screen.dart index 9282301..f684f6c 100644 --- a/lib/screens/profile/components/references_screen.dart +++ b/lib/screens/profile/components/references_screen.dart @@ -26,25 +26,37 @@ class ReferencesScreen extends StatelessWidget { int? profileId; String? token; return Scaffold( + resizeToAvoidBottomInset: true, appBar: AppBar( - title: context.watch().state is AddReferenceState?const Text("Add Personal Reference"):context.watch().state is EditReferenceState?const Text("Edit Personal Reference"):const Text("Personal References"), - centerTitle: true, - backgroundColor: primary, - ////if state is adding or editing state show close button - actions: (context.watch().state is AddReferenceState || context.watch().state is EditReferenceState)? - [ - //// close button - CloseLeading(onPressed: (){ - context.read().add(GetReferences(profileId: profileId!, token: token!)); - }), - ]: - //// if state is loaded state show add button - context.watch().state is ReferencesLoadedState?[ - AddLeading(onPressed: (){ - context.read().add(ShowAddReferenceForm()); - }), - ]:[] - ), + title: context.watch().state is AddReferenceState + ? const Text("Add Personal Reference") + : context.watch().state is EditReferenceState + ? const Text("Edit Personal Reference") + : const Text("Personal References"), + centerTitle: true, + backgroundColor: primary, + ////if state is adding or editing state show close button + actions: (context.watch().state + is AddReferenceState || + context.watch().state is EditReferenceState) + ? [ + //// close button + CloseLeading(onPressed: () { + context.read().add( + GetReferences(profileId: profileId!, token: token!)); + }), + ] + : + //// if state is loaded state show add button + context.watch().state is ReferencesLoadedState + ? [ + AddLeading(onPressed: () { + context + .read() + .add(ShowAddReferenceForm()); + }), + ] + : []), body: ProgressHUD( padding: const EdgeInsets.all(24), backgroundColor: Colors.black87, @@ -83,7 +95,7 @@ class ReferencesScreen extends StatelessWidget { Navigator.of(context).pop(); context.read().add( LoadReferences( - references: state.personalRefs)); + )); }); } else { errorAlert(context, "Adding Failed", @@ -92,7 +104,7 @@ class ReferencesScreen extends StatelessWidget { Navigator.of(context).pop(); context.read().add( LoadReferences( - references: state.personalRefs)); + )); }); } } @@ -104,7 +116,7 @@ class ReferencesScreen extends StatelessWidget { Navigator.of(context).pop(); context.read().add( LoadReferences( - references: state.personalRefs)); + )); }); } else { errorAlert(context, "Update Failed", @@ -113,7 +125,7 @@ class ReferencesScreen extends StatelessWidget { Navigator.of(context).pop(); context.read().add( LoadReferences( - references: state.personalRefs)); + )); }); } } @@ -127,7 +139,7 @@ class ReferencesScreen extends StatelessWidget { Navigator.of(context).pop(); context.read().add( LoadReferences( - references: state.references)); +)); }); } else { errorAlert(context, "Deletion Failed", @@ -135,7 +147,7 @@ class ReferencesScreen extends StatelessWidget { Navigator.of(context).pop(); context.read().add( LoadReferences( - references: state.references)); + )); }); } } @@ -212,12 +224,14 @@ class ReferencesScreen extends StatelessWidget { offset: const Offset(-10, -10), elevation: 3, onSelected: (value) { - ////delete eligibilty-= = = = = = = = =>> if (value == 2) { - final progress = ProgressHUD.of(context); -progress!.showWithText("Please wait..."); + confirmAlert(context, () { + final progress = + ProgressHUD.of(context); + progress!.showWithText( + "Please wait..."); context .read< ReferencesBloc>() @@ -236,6 +250,10 @@ progress!.showWithText("Please wait..."); } if (value == 1) { ////edit reference-= = = = = = = = =>> + final progress = + ProgressHUD.of(context); + progress!.showWithText( + "Please wait..."); context .read() .add(ShowEditReferenceForm( @@ -276,14 +294,16 @@ progress!.showWithText("Please wait..."); } } if (state is AddReferenceState) { - return const AddReferenceScreen(); + return AddReferenceScreen(profileId: profileId!, token: token!,); } if (state is EditReferenceState) { - return const EditReferenceScreen(); + return EditReferenceScreen(profileId: profileId!,token: token!,); } if (state is ReferencesErrorState) { return SomethingWentWrong( - message: state.message, onpressed: () {}); + message: state.message, onpressed: () { + context.read().add(GetReferences(profileId: profileId!, token: token!)); + }); } return Container(); }, diff --git a/lib/screens/profile/components/work_history/add_modal.dart b/lib/screens/profile/components/work_history/add_modal.dart index 3a9be0b..3ba5b84 100644 --- a/lib/screens/profile/components/work_history/add_modal.dart +++ b/lib/screens/profile/components/work_history/add_modal.dart @@ -24,7 +24,9 @@ import 'package:unit2/utils/validators.dart'; import '../../../../model/utils/position.dart'; class AddWorkHistoryScreen extends StatefulWidget { - const AddWorkHistoryScreen({super.key}); + final int profileId; + final String token; + const AddWorkHistoryScreen({super.key, required this.profileId, required this.token}); @override State createState() => _AddWorkHistoryScreenState(); @@ -65,14 +67,7 @@ class _AddWorkHistoryScreenState extends State { @override Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, state) { - if (state is UserLoggedIn) { - profileId = state.userData!.user!.login!.user!.profileId; - token = state.userData!.user!.login!.token; - return BlocBuilder( - builder: (context, state) { - if (state is ProfileLoaded) { + return BlocConsumer( listener: (context, state) { if (state is AddWorkHistoryState) { @@ -121,6 +116,7 @@ class _AddWorkHistoryScreenState extends State { positionFocusNode.unfocus(); }); }, + ////EMPTY WIDGET emptyWidget: Container( decoration: box1(), height: 100, @@ -139,6 +135,7 @@ class _AddWorkHistoryScreenState extends State { ), TextButton( onPressed: () { + ////ADD POSITION DIALOG showDialog( context: context, builder: (BuildContext @@ -638,12 +635,7 @@ class _AddWorkHistoryScreenState extends State { Flexible( flex: 1, child: DateTimePicker( - validator: (value) { - if (value == null) { - return "This field is required"; - } - return null; - }, + validator: FormBuilderValidators.required(errorText: "This field is required"), use24HourFormat: false, icon: const Icon( Icons.date_range), @@ -684,9 +676,7 @@ class _AddWorkHistoryScreenState extends State { .copyWith(), ) : DateTimePicker( - validator: (val) { - return null; - }, + validator: FormBuilderValidators.required(errorText: "This field is required"), controller: toDateController, firstDate: DateTime(1970), @@ -723,17 +713,12 @@ class _AddWorkHistoryScreenState extends State { style: mainBtnStyle( primary, Colors.transparent, second), onPressed: () { - print(selectedPosition?.title); - print(selectedStatus?.label); - print(selectedAgency?.name); - print(salary); - print(fromDateController.text); - print(toDateController.text); - - print(salaryGrade); - print(salaryGradeStep); - print(isPrivate); + if (_formKey.currentState!.validate()) { + final progress = + ProgressHUD.of(context); + progress!.showWithText( + "Loading..."); WorkHistory workHistory = WorkHistory( position: selectedPosition, id: null, @@ -780,17 +765,7 @@ class _AddWorkHistoryScreenState extends State { ), ); } - return Container(); - }); - } - return Container(); - }, - ); - } - return const Center( - child: Text("Add Work History"), - ); - }, - ); + return Container(); + }); } } diff --git a/lib/screens/profile/components/work_history/edit_modal.dart b/lib/screens/profile/components/work_history/edit_modal.dart index 975bfcd..bff0072 100644 --- a/lib/screens/profile/components/work_history/edit_modal.dart +++ b/lib/screens/profile/components/work_history/edit_modal.dart @@ -23,7 +23,10 @@ import '../../../../utils/text_container.dart'; import '../../../../utils/validators.dart'; class EditWorkHistoryScreen extends StatefulWidget { - const EditWorkHistoryScreen({super.key}); + final int profileId; + final String token; + const EditWorkHistoryScreen( + {super.key, required this.profileId, required this.token}); @override State createState() => _EditWorkHistoryScreenState(); @@ -73,756 +76,695 @@ class _EditWorkHistoryScreenState extends State { @override Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, state) { - if (state is UserLoggedIn) { - profileId = state.userData!.user!.login!.user!.profileId; - token = state.userData!.user!.login!.token; - return BlocBuilder( - builder: (context, state) { - if (state is ProfileLoaded) { - return BlocConsumer( - listener: (context, state) { - if (state is AddWorkHistoryState) { - final progress = ProgressHUD.of(context); - progress!.dismiss(); - } - }, builder: (context, state) { - if (state is EditWorkHistoryState) { - oldPositionController.text = - state.workHistory.position!.title!; - oldAppointmentStatusController.text = - state.workHistory.appointmentStatus!; - oldAgencyController.text = state.workHistory.agency!.name!; - currentlyEmployed = - state.workHistory.toDate == null ? true : false; - showSalaryGradeAndSalaryStep = - !state.workHistory.agency!.privateEntity!; - fromDateController.text = - state.workHistory.fromDate.toString(); - toDateController.text = state.workHistory.toDate.toString(); - currentlyEmployed = - state.workHistory.toDate == null ? true : false; + return BlocBuilder(builder: (context, state) { + return BlocBuilder( + builder: (context, state) { + if (state is EditWorkHistoryState) { + oldPositionController.text = state.workHistory.position!.title!; + oldAppointmentStatusController.text = + state.workHistory.appointmentStatus!; + oldAgencyController.text = state.workHistory.agency!.name!; + currentlyEmployed = state.workHistory.toDate == null ? true : false; + showSalaryGradeAndSalaryStep = + !state.workHistory.agency!.privateEntity!; + fromDateController.text = state.workHistory.fromDate.toString(); + toDateController.text = state.workHistory.toDate.toString(); + currentlyEmployed = state.workHistory.toDate == null ? true : false; - return SingleChildScrollView( - child: SizedBox( - height: blockSizeVertical * 90, - child: FormBuilder( - key: _formKey, - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 25, horizontal: 18), - child: Column( - children: [ - ////POSITIONS - StatefulBuilder(builder: (context, setState) { - return SearchField( - controller: oldPositionController, - itemHeight: 50, - suggestionsDecoration: box1(), - suggestions: state.agencyPositions - .map((Position position) => - SearchFieldListItem(position.title!, - item: position, - child: Padding( - padding: const EdgeInsets - .symmetric( - horizontal: 10), - child: ListTile( - title: - Text(position.title!), - )))) - .toList(), - focusNode: positionFocusNode, - searchInputDecoration: - normalTextFieldStyle("Position *", "") - .copyWith( - suffixIcon: const Icon( - Icons.arrow_drop_down)), - onSuggestionTap: (position) { - setState(() { - selectedPosition = position.item; - positionFocusNode.unfocus(); - }); - }, - emptyWidget: Container( - decoration: box1(), - height: 100, - child: Column( - mainAxisAlignment: - MainAxisAlignment.center, - crossAxisAlignment: - CrossAxisAlignment.center, - children: [ - const SizedBox( - height: 20, - ), - const Text("No result found..."), - const SizedBox( - height: 10, - ), - TextButton( - onPressed: () { - showDialog( - context: context, - builder: (BuildContext - context) { - return AlertDialog( - title: const Text( - "Add Position?"), - content: SizedBox( - height: 130, - child: Column( - children: [ - TextFormField( - controller: - addPositionController, - decoration: - normalTextFieldStyle( - "", - ""), - ), - const SizedBox( - height: 12, - ), - SizedBox( - width: double - .infinity, - height: 50, - child: ElevatedButton( - style: mainBtnStyle(primary, Colors.transparent, second), - onPressed: () { + return SingleChildScrollView( + child: SizedBox( + height: blockSizeVertical * 90, + child: FormBuilder( + key: _formKey, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, horizontal: 18), + child: Column( + children: [ + ////POSITIONS + StatefulBuilder(builder: (context, setState) { + return SearchField( + controller: oldPositionController, + itemHeight: 50, + suggestionsDecoration: box1(), + suggestions: state.agencyPositions + .map((Position position) => SearchFieldListItem( + position.title!, + item: position, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10), + child: ListTile( + title: Text(position.title!), + )))) + .toList(), + focusNode: positionFocusNode, + searchInputDecoration: + normalTextFieldStyle("Position *", "").copyWith( + suffixIcon: + const Icon(Icons.arrow_drop_down)), + onSuggestionTap: (position) { + setState(() { + selectedPosition = position.item; + positionFocusNode.unfocus(); + }); + }, + emptyWidget: Container( + decoration: box1(), + height: 100, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox( + height: 20, + ), + const Text("No result found..."), + const SizedBox( + height: 10, + ), + TextButton( + onPressed: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text( + "Add Position?"), + content: SizedBox( + height: 130, + child: Column( + children: [ + TextFormField( + controller: + addPositionController, + decoration: + normalTextFieldStyle( + "", ""), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: + double.infinity, + height: 50, + child: + ElevatedButton( + style: mainBtnStyle( + primary, + Colors + .transparent, + second), + onPressed: + () { + setState( + () { + Position + newAgencyPosition = + Position( + id: null, + title: addPositionController.text.toUpperCase()); + state.agencyPositions.insert( + 0, + newAgencyPosition); + selectedPosition = + newAgencyPosition; + addPositionController.text = + ""; + Navigator.pop( + context); + }); + }, + child: const Text( + "Add"))), + ], + ), + ), + ); + }); + }, + child: const Text("Add position")) + ]), + ), + validator: (position) { + if (position!.isEmpty) { + return "This field is required"; + } + return null; + }, + ); + }), + const SizedBox( + height: 12, + ), + ////APPOINTMENT STATUS' + SearchField( + controller: oldAppointmentStatusController, + suggestions: state.appointmentStatus + .map((AppoinemtStatus status) => + SearchFieldListItem(status.label, + item: status)) + .toList(), + focusNode: appointmentStatusNode, + validator: (value) { + if (value!.isEmpty) { + return "This field is required"; + } + return null; + }, + onSuggestionTap: (status) { + selectedStatus = status.item; + appointmentStatusNode.unfocus(); + }, + searchInputDecoration: + normalTextFieldStyle("Appointment Status", "") + .copyWith( + suffixIcon: + const Icon(Icons.arrow_drop_down)), + ), + + const SizedBox( + height: 12, + ), + + ////AGENCY + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + SearchField( + controller: oldAgencyController, + itemHeight: 70, + focusNode: agencyFocusNode, + suggestions: state.agencies + .map((Agency agency) => + SearchFieldListItem(agency.name!, + item: agency, + child: ListTile( + title: Text( + agency.name!, + overflow: TextOverflow.ellipsis, + ), + subtitle: Text( + agency.privateEntity == true + ? "Private" + : "Government"), + ))) + .toList(), + searchInputDecoration: normalTextFieldStyle( + "Agency *", "") + .copyWith( + suffixIcon: + const Icon(Icons.arrow_drop_down)), + onSuggestionTap: (agency) { + setState(() { + selectedAgency = agency.item; + if (selectedAgency!.privateEntity == null) { + showIsPrivateRadio = true; + } else { + showIsPrivateRadio = false; + } + if (selectedAgency!.privateEntity == true) { + showSalaryGradeAndSalaryStep = false; + } + if (selectedAgency!.privateEntity == + false) { + showSalaryGradeAndSalaryStep = true; + } + agencyFocusNode.unfocus(); + }); + }, + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + emptyWidget: Container( + decoration: box1(), + height: 100, + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.center, + children: [ + const SizedBox( + height: 20, + ), + const Text("No result found"), + const SizedBox( + height: 10, + ), + TextButton( + onPressed: () { + showDialog( + context: context, + builder: + (BuildContext context) { + return AlertDialog( + title: const Text( + "Add Position"), + content: SizedBox( + height: 130, + child: Column( + children: [ + TextFormField( + controller: + addAgencyController, + decoration: + normalTextFieldStyle( + "", ""), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: double + .infinity, + height: 50, + child: + ElevatedButton( + style: mainBtnStyle( + primary, + Colors + .transparent, + second), + onPressed: + () { setState( () { - Position - newAgencyPosition = - Position(id: null, title: addPositionController.text.toUpperCase()); - state.agencyPositions.insert(0, - newAgencyPosition); - selectedPosition = - newAgencyPosition; - addPositionController.text = + Agency newAgency = Agency( + id: null, + name: addAgencyController.text.toUpperCase(), + category: null, + privateEntity: null); + state.agencies.insert(0, + newAgency); + selectedAgency = + newAgency; + addAgencyController.text = ""; + showAgencyCategory = + true; + + showIsPrivateRadio = + true; + Navigator.pop(context); }); }, - child: const Text("Add"))), - ], - ), - ), - ); - }); - }, - child: - const Text("Add position")) - ]), - ), - validator: (position) { - if (position!.isEmpty) { - return "This field is required"; - } - return null; - }, - ); - }), - const SizedBox( - height: 12, - ), - ////APPOINTMENT STATUS' - SearchField( - controller: oldAppointmentStatusController, - suggestions: state.appointmentStatus - .map((AppoinemtStatus status) => - SearchFieldListItem(status.label, - item: status)) - .toList(), - focusNode: appointmentStatusNode, - validator: (value) { - if (value!.isEmpty) { - return "This field is required"; - } - return null; - }, - onSuggestionTap: (status) { - selectedStatus = status.item; - appointmentStatusNode.unfocus(); - }, - searchInputDecoration: normalTextFieldStyle( - "Appointment Status", "") - .copyWith( - suffixIcon: const Icon( - Icons.arrow_drop_down)), - ), - - const SizedBox( - height: 12, - ), - - ////AGENCY - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - SearchField( - controller: oldAgencyController, - itemHeight: 70, - focusNode: agencyFocusNode, - suggestions: state.agencies - .map((Agency agency) => - SearchFieldListItem( - agency.name!, - item: agency, - child: ListTile( - title: Text( - agency.name!, - overflow: TextOverflow - .ellipsis, + child: const Text( + "Add"))), + ], + ), ), - subtitle: Text( - agency.privateEntity == - true - ? "Private" - : "Government"), + ); + }); + }, + child: const Text("Add Agency")) + ]), + ), + ), + + SizedBox( + height: showAgencyCategory ? 12 : 0, + ), + ////SHOW AGENCY CATEGORY + SizedBox( + child: showAgencyCategory + ? SearchField( + focusNode: agencyCategoryFocusNode, + itemHeight: 70, + suggestions: state.agencyCategory + .map((Category category) => + SearchFieldListItem( + category.name!, + item: category, + child: ListTile( + title: + Text(category.name!), + subtitle: Text(category + .industryClass! + .name!), ))) .toList(), + emptyWidget: Container( + height: 100, + decoration: box1(), + child: const Center( + child: + Text("No result found ...")), + ), + onSuggestionTap: (agencyCategory) { + setState(() { + selectedAgencyCategory = + agencyCategory.item; + agencyCategoryFocusNode.unfocus(); + selectedAgency = Agency( + id: null, + name: selectedAgency!.name, + category: + selectedAgencyCategory, + privateEntity: null); + }); + }, searchInputDecoration: - normalTextFieldStyle("Agency *", "") + normalTextFieldStyle( + "Category *", "") .copyWith( suffixIcon: const Icon( Icons.arrow_drop_down)), - onSuggestionTap: (agency) { - setState(() { - selectedAgency = agency.item; - if (selectedAgency!.privateEntity == - null) { - showIsPrivateRadio = true; - } else { - showIsPrivateRadio = false; - } - if (selectedAgency!.privateEntity == - true) { - showSalaryGradeAndSalaryStep = - false; - } - if (selectedAgency!.privateEntity == - false) { - showSalaryGradeAndSalaryStep = - true; - } - agencyFocusNode.unfocus(); - }); - }, - validator: (agency) { - if (agency!.isEmpty) { + validator: (value) { + if (value!.isEmpty) { return "This field is required"; } return null; }, - emptyWidget: Container( - decoration: box1(), - height: 100, - child: Column( - mainAxisAlignment: - MainAxisAlignment.center, - crossAxisAlignment: - CrossAxisAlignment.center, + ) + : const SizedBox(), + ), + + ////PRVIATE SECTOR + SizedBox( + child: showIsPrivateRadio + ? FormBuilderRadioGroup( + decoration: InputDecoration( + border: InputBorder.none, + label: Row( children: [ - const SizedBox( - height: 20, + Text( + "Is this private sector? ", + style: Theme.of(context) + .textTheme + .headlineSmall! + .copyWith(fontSize: 24), ), - const Text("No result found"), - const SizedBox( - height: 10, - ), - TextButton( - onPressed: () { - showDialog( - context: context, - builder: (BuildContext - context) { - return AlertDialog( - title: const Text( - "Add Position"), - content: SizedBox( - height: 130, - child: Column( - children: [ - TextFormField( - controller: - addAgencyController, - decoration: - normalTextFieldStyle( - "", - ""), - ), - const SizedBox( - height: - 12, - ), - SizedBox( - width: double - .infinity, - height: - 50, - child: ElevatedButton( - style: mainBtnStyle(primary, Colors.transparent, second), - onPressed: () { - setState(() { - Agency newAgency = Agency(id: null, name: addAgencyController.text.toUpperCase(), category: null, privateEntity: null); - state.agencies.insert(0, newAgency); - selectedAgency = newAgency; - addAgencyController.text = ""; - showAgencyCategory = true; + const Icon( + FontAwesome.help_circled) + ], + ), + ), - showIsPrivateRadio = true; + ////onvhange private sector + onChanged: (value) { + setState(() { + if (value.toString() == "YES") { + isPrivate = true; + showSalaryGradeAndSalaryStep = + false; + } else { + isPrivate = false; + showSalaryGradeAndSalaryStep = + true; + } + selectedAgency = Agency( + id: null, + name: selectedAgency!.name, + category: + selectedAgencyCategory, + privateEntity: value == "YES" + ? true + : false); + agencyFocusNode.unfocus(); + agencyCategoryFocusNode.unfocus(); + }); + }, - Navigator.pop(context); - }); - }, - child: const Text("Add"))), - ], - ), - ), - ); - }); - }, - child: const Text( - "Add Agency")) - ]), - ), - ), - - SizedBox( - height: showAgencyCategory ? 12 : 0, - ), - ////SHOW AGENCY CATEGORY - SizedBox( - child: showAgencyCategory - ? SearchField( - focusNode: - agencyCategoryFocusNode, - itemHeight: 70, - suggestions: state - .agencyCategory - .map((Category category) => - SearchFieldListItem( - category.name!, - item: category, - child: ListTile( - title: Text( - category - .name!), - subtitle: Text( - category - .industryClass! - .name!), - ))) - .toList(), - emptyWidget: Container( - height: 100, - decoration: box1(), - child: const Center( - child: Text( - "No result found ...")), - ), - onSuggestionTap: - (agencyCategory) { - setState(() { - selectedAgencyCategory = - agencyCategory.item; - agencyCategoryFocusNode - .unfocus(); - selectedAgency = Agency( - id: null, - name: selectedAgency! - .name, - category: - selectedAgencyCategory, - privateEntity: null); - }); - }, - searchInputDecoration: - normalTextFieldStyle( - "Category *", "") - .copyWith( - suffixIcon: - const Icon(Icons - .arrow_drop_down)), - validator: (value) { - if (value!.isEmpty) { - return "This field is required"; - } - return null; - }, - ) - : const SizedBox(), - ), - - ////PRVIATE SECTOR - SizedBox( - child: showIsPrivateRadio - ? FormBuilderRadioGroup( - decoration: InputDecoration( - border: InputBorder.none, - label: Row( - children: [ - Text( - "Is this private sector? ", - style: Theme.of( - context) - .textTheme - .headlineSmall! - .copyWith( - fontSize: 24), - ), - const Icon(FontAwesome - .help_circled) - ], - ), - ), - - ////onvhange private sector - onChanged: (value) { - setState(() { - if (value.toString() == - "YES") { - isPrivate = true; - showSalaryGradeAndSalaryStep = - false; - } else { - isPrivate = false; - showSalaryGradeAndSalaryStep = - true; - } - selectedAgency = Agency( - id: null, - name: selectedAgency! - .name, - category: - selectedAgencyCategory, - privateEntity: - value == "YES" - ? true - : false); - agencyFocusNode.unfocus(); - agencyCategoryFocusNode - .unfocus(); - }); - }, - - name: 'isPrivate', - validator: - FormBuilderValidators - .required(), - options: ["YES", "NO"] - .map((lang) => - FormBuilderFieldOption( - value: lang)) - .toList(growable: false), - ) - : const SizedBox()), - SizedBox( - height: showSalaryGradeAndSalaryStep - ? 12 - : 0, - ), - ////SALARY GRADE AND SALARY GRADE STEP - SizedBox( - child: showSalaryGradeAndSalaryStep - ? Column( - children: [ - Row( - children: [ - ////SALARY GRADE - Flexible( - flex: 1, - child: - FormBuilderTextField( - initialValue: state - .workHistory - .salaryGrade - ?.toString(), - name: - 'salary_grade', - keyboardType: - TextInputType - .number, - decoration: - normalTextFieldStyle( - "Salary Grade (SG)", - "0"), - validator: - integerAndNumeric, - autovalidateMode: - AutovalidateMode - .onUserInteraction, - ), - ), - const SizedBox( - width: 12, - ), - //// SALARY STEP - Flexible( - flex: 1, - child: - FormBuilderTextField( - initialValue: state - .workHistory - .sgStep - ?.toString(), - name: 'salary_step', - keyboardType: - TextInputType - .number, - decoration: - normalTextFieldStyle( - "SG Step (SG)", - "0"), - validator: - integerAndNumeric, - autovalidateMode: - AutovalidateMode - .onUserInteraction, - ), - ) - ], - ) - ], - ) - : null), - ], - ); - }), - const SizedBox( - height: 12, - ), - ////MONTHLY SALARY - FormBuilderTextField( - initialValue: state.workHistory.monthlySalary - .toString(), - onChanged: (value) { - setState(() { - salary = value; - }); - }, - validator: numericRequired, - name: "salary", - decoration: normalTextFieldStyle( - "Monthly Salary *", "") - .copyWith(prefix: const Text("₱ ")), - ), - - const SizedBox( - height: 12, - ), - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - ////CURRENTLY EMPLOYED - FormBuilderSwitch( - initialValue: currentlyEmployed, - activeColor: second, - onChanged: (value) { - setState(() { - if (value == true) { - currentlyEmployed = true; - toDateController.text = "PRESENT"; - } else { - currentlyEmployed = false; - toDateController.text = ""; - } - }); - }, - decoration: - normalTextFieldStyle("", ''), - name: 'overseas', - title: - const Text("Currently Employed?"), - ), - const SizedBox( - height: 12, - ), - SizedBox( - width: screenWidth, - child: Row( + name: 'isPrivate', + validator: + FormBuilderValidators.required(), + options: ["YES", "NO"] + .map((lang) => + FormBuilderFieldOption( + value: lang)) + .toList(growable: false), + ) + : const SizedBox()), + SizedBox( + height: showSalaryGradeAndSalaryStep ? 12 : 0, + ), + ////SALARY GRADE AND SALARY GRADE STEP + SizedBox( + child: showSalaryGradeAndSalaryStep + ? Column( children: [ - //// FROM DATE - Flexible( - flex: 1, - child: DateTimePicker( - validator: (value) { - if (value == null) { - return "This field is required"; - } - return null; - }, - use24HourFormat: false, - icon: const Icon( - Icons.date_range), - controller: - fromDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), - timeHintText: - "Date of Examination/Conferment", - decoration: - normalTextFieldStyle( - "From *", - "From *") - .copyWith( - prefixIcon: - const Icon( - Icons.date_range, - color: Colors.black87, - )), - initialValue: null, - )), - const SizedBox( - width: 12, - ), - //// TO DATE - Flexible( - flex: 1, - child: currentlyEmployed - ? TextFormField( - enabled: false, - initialValue: "PRESENT", - style: const TextStyle( - color: - Colors.black45), - decoration: - normalTextFieldStyle( - "", "") - .copyWith( - prefixIcon: - const Icon( - Icons.date_range, - color: Colors.black45, - )), - ) - : DateTimePicker( - validator: (val) { - return null; - }, - controller: - toDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), - decoration: normalTextFieldStyle( - "To *", "To *") - .copyWith( - prefixIcon: - const Icon( - Icons - .date_range, - color: Colors - .black87, - ), - prefixText: - currentlyEmployed - ? "PRESENT" - : ""), - initialValue: null, - ), - ), + Row( + children: [ + ////SALARY GRADE + Flexible( + flex: 1, + child: FormBuilderTextField( + initialValue: state + .workHistory.salaryGrade + ?.toString(), + name: 'salary_grade', + keyboardType: + TextInputType.number, + decoration: + normalTextFieldStyle( + "Salary Grade (SG)", + "0"), + validator: + integerAndNumeric, + autovalidateMode: + AutovalidateMode + .onUserInteraction, + ), + ), + const SizedBox( + width: 12, + ), + //// SALARY STEP + Flexible( + flex: 1, + child: FormBuilderTextField( + initialValue: state + .workHistory.sgStep + ?.toString(), + name: 'salary_step', + keyboardType: + TextInputType.number, + decoration: + normalTextFieldStyle( + "SG Step (SG)", + "0"), + validator: + integerAndNumeric, + autovalidateMode: + AutovalidateMode + .onUserInteraction, + ), + ) + ], + ) ], - ), - ), - ], - ); - }), - const Expanded(child: SizedBox()), - ////SUBMIT BUTTON - SizedBox( - width: double.infinity, - height: 60, - child: ElevatedButton( - style: mainBtnStyle( - primary, Colors.transparent, second), - onPressed: () { - if (_formKey.currentState! - .saveAndValidate()) { - salary = _formKey - .currentState!.value['salary']; - selectedPosition ??= - state.workHistory.position; - salaryGrade = _formKey.currentState! - .value['salary_grade']; - salaryGradeStep = _formKey - .currentState! - .value['salary_step']; - selectedAgency ??= - state.workHistory.agency; - - selectedStatus ??= AppoinemtStatus( - value: state.workHistory - .appointmentStatus!, - label: state.workHistory - .appointmentStatus!); - WorkHistory newWorkHistory = - WorkHistory( - id: state.workHistory.id, - position: selectedPosition, - agency: selectedAgency, - fromDate: fromDateController - .text.isEmpty - ? null - : DateTime.parse( - fromDateController.text), - toDate: toDateController - .text.isEmpty || - toDateController.text - .toUpperCase() == - "PRESENT" || - toDateController.text - .toLowerCase() == - 'null' - ? null - : DateTime.parse( - toDateController.text), - monthlySalary: - double.parse(salary!), - appointmentStatus: - selectedStatus!.value, - salaryGrade: salaryGrade == null - ? null - : int.parse(salaryGrade!), - sgStep: salaryGradeStep == null - ? null - : int.parse(salaryGradeStep!), - ); - context.read().add( - UpdateWorkHistory( - oldWorkHistory: - state.workHistory, - profileId: - profileId.toString(), - token: token!, - workHistory: newWorkHistory)); - } - }, - child: const Text(submit)), - ), - const SizedBox( - height: 20, - ), - ], - ), - ), + ) + : null), + ], + ); + }), + const SizedBox( + height: 12, ), - ), - ); - } - return Container(); - }); - } - return Container(); - }, + ////MONTHLY SALARY + FormBuilderTextField( + initialValue: + state.workHistory.monthlySalary.toString(), + onChanged: (value) { + setState(() { + salary = value; + }); + }, + validator: numericRequired, + name: "salary", + decoration: + normalTextFieldStyle("Monthly Salary *", "") + .copyWith(prefix: const Text("₱ ")), + ), + + const SizedBox( + height: 12, + ), + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + ////CURRENTLY EMPLOYED + FormBuilderSwitch( + initialValue: currentlyEmployed, + activeColor: second, + onChanged: (value) { + setState(() { + if (value == true) { + currentlyEmployed = true; + toDateController.text = "PRESENT"; + } else { + currentlyEmployed = false; + toDateController.text = ""; + } + }); + }, + decoration: normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Currently Employed?"), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + //// FROM DATE + Flexible( + flex: 1, + child: DateTimePicker( + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + use24HourFormat: false, + icon: const Icon(Icons.date_range), + controller: fromDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + timeHintText: + "Date of Examination/Conferment", + decoration: normalTextFieldStyle( + "From *", "From *") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + initialValue: null, + )), + const SizedBox( + width: 12, + ), + //// TO DATE + Flexible( + flex: 1, + child: currentlyEmployed + ? TextFormField( + enabled: false, + initialValue: "PRESENT", + style: const TextStyle( + color: Colors.black45), + decoration: normalTextFieldStyle( + "", "") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black45, + )), + ) + : DateTimePicker( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + controller: toDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + decoration: normalTextFieldStyle( + "To *", "To *") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + ), + prefixText: + currentlyEmployed + ? "PRESENT" + : ""), + initialValue: null, + ), + ), + ], + ), + ), + ], + ); + }), + const Expanded(child: SizedBox()), + ////SUBMIT BUTTON + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + if (_formKey.currentState!.saveAndValidate()) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + salary = + _formKey.currentState!.value['salary']; + selectedPosition ??= + state.workHistory.position; + salaryGrade = _formKey + .currentState!.value['salary_grade']; + salaryGradeStep = _formKey + .currentState!.value['salary_step']; + selectedAgency ??= state.workHistory.agency; + + selectedStatus ??= AppoinemtStatus( + value: + state.workHistory.appointmentStatus!, + label: + state.workHistory.appointmentStatus!); + WorkHistory newWorkHistory = WorkHistory( + id: state.workHistory.id, + position: selectedPosition, + agency: selectedAgency, + fromDate: fromDateController.text.isEmpty + ? null + : DateTime.parse( + fromDateController.text), + toDate: toDateController.text.isEmpty || + toDateController.text + .toUpperCase() == + "PRESENT" || + toDateController.text + .toLowerCase() == + 'null' + ? null + : DateTime.parse(toDateController.text), + monthlySalary: double.parse(salary!), + appointmentStatus: selectedStatus!.value, + salaryGrade: salaryGrade == null + ? null + : int.parse(salaryGrade!), + sgStep: salaryGradeStep == null + ? null + : int.parse(salaryGradeStep!), + ); + context.read().add( + UpdateWorkHistory( + oldWorkHistory: state.workHistory, + profileId: profileId.toString(), + token: token!, + workHistory: newWorkHistory)); + } + }, + child: const Text(submit)), + ), + const SizedBox( + height: 20, + ), + ], + ), + ), + ), + ), + ); + } + return const Center( + child: Text("Add Work History"), ); - } - return const Center( - child: Text("Add Work History"), - ); - }, - ); + }, + ); + }); } } diff --git a/lib/screens/profile/components/work_history_screen.dart b/lib/screens/profile/components/work_history_screen.dart index c725291..96de174 100644 --- a/lib/screens/profile/components/work_history_screen.dart +++ b/lib/screens/profile/components/work_history_screen.dart @@ -1,5 +1,4 @@ import 'dart:io'; - import 'package:app_popup_menu/app_popup_menu.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -15,6 +14,7 @@ import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; +import 'package:unit2/widgets/Leadings/close_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; @@ -36,11 +36,15 @@ class WorkHistoryScreen extends StatelessWidget { title: const Text(workHistoryScreenTitle), backgroundColor: primary, centerTitle: true, - actions: [ + actions: context.watch().state is WorkHistoryLoaded?[ AddLeading(onPressed: () { context.read().add(ShowAddWorkHistoryForm()); }) - ], + ]:(context.watch().state is AddWorkHistoryState || context.watch().state is EditWorkHistoryState)?[ + CloseLeading(onPressed: (){ + context.read().add(LoadWorkHistories()); + }) + ]:[], ), body: //UserBloc @@ -68,7 +72,9 @@ class WorkHistoryScreen extends StatelessWidget { } if (state is WorkHistoryLoaded || state is WorkHistoryErrorState || - state is AddWorkHistoryState ||state is WorkHistoryAddedState || state is EditWorkHistoryState) { + state is AddWorkHistoryState || + state is WorkHistoryAddedState || + state is EditWorkHistoryState) { final progress = ProgressHUD.of(context); progress!.dismiss(); } @@ -78,55 +84,59 @@ class WorkHistoryScreen extends StatelessWidget { successAlert(context, "Deletion Successfull", "Work has been deleted successfully", () { Navigator.of(context).pop(); - context.read().add( - LoadWorkHistories( - workHistories: state.workHistories)); + context + .read() + .add(LoadWorkHistories()); }); } else { errorAlert(context, "Deletion Failed", "Error deleting Work History", () { Navigator.of(context).pop(); - context.read().add( - LoadWorkHistories( - workHistories: state.workHistories)); + context + .read() + .add(LoadWorkHistories()); }); } } ////ADDED STATE - if(state is WorkHistoryAddedState){ - if (state.response['success']) { + if (state is WorkHistoryAddedState) { + if (state.response['success']) { successAlert(context, "Adding Successfull!", state.response['message'], () { Navigator.of(context).pop(); - context.read().add(LoadWorkHistories( - workHistories: state.workExperiences)); + context + .read() + .add(LoadWorkHistories()); }); } else { errorAlert(context, "Adding Failed", "Something went wrong. Please try again.", () { Navigator.of(context).pop(); - context.read().add(LoadWorkHistories( - workHistories: state.workExperiences)); + context + .read() + .add(LoadWorkHistories()); }); } } //// EDITED STATE - if(state is WorkHistoryEditedState){ - if (state.response['success']) { + if (state is WorkHistoryEditedState) { + if (state.response['success']) { successAlert(context, "Update Successfull!", state.response['message'], () { Navigator.of(context).pop(); - context.read().add(LoadWorkHistories( - workHistories: state.workExperiences)); + context + .read() + .add(LoadWorkHistories()); }); } else { errorAlert(context, "Update Failed", "Something went wrong. Please try again.", () { Navigator.of(context).pop(); - context.read().add(LoadWorkHistories( - workHistories: state.workExperiences)); + context + .read() + .add(LoadWorkHistories()); }); } } @@ -207,31 +217,41 @@ class WorkHistoryScreen extends StatelessWidget { offset: const Offset(-10, -10), elevation: 3, onSelected: (value) { - final progress = - ProgressHUD.of(context); - progress! - .showWithText("Loading..."); ////delete workhistory-= = = = = = = = =>> if (value == 2) { confirmAlert(context, () { - BlocProvider.of( + final progress = + ProgressHUD.of(context); + progress!.showWithText( + "Loading..."); + BlocProvider.of< + WorkHistoryBloc>( context) .add(DeleteWorkHistory( - profileId: - profileId, - token: token!, - workHistory: state - .workExperiences[ - index], - workHistories: state - .workExperiences)); + profileId: profileId, + token: token!, + workHistory: + state.workExperiences[ + index], + )); }, "Delete?", "Confirm Delete?"); } if (value == 1) { ////edit eligibilty-= = = = = = = = =>> - WorkHistory workHistory = state.workExperiences[index]; - context.read().add(ShowEditWorkHistoryForm(workHistory: workHistory)); + final progress = + ProgressHUD.of(context); + progress!.showWithText( + "Loading..."); + WorkHistory workHistory = + state.workExperiences[ + index]; + context + .read() + .add( + ShowEditWorkHistoryForm( + workHistory: + workHistory)); } }, menuItems: [ @@ -269,14 +289,16 @@ class WorkHistoryScreen extends StatelessWidget { } } if (state is AddWorkHistoryState) { - return const AddWorkHistoryScreen(); + return AddWorkHistoryScreen(profileId: profileId, token: token!,); } - if(state is EditWorkHistoryState){ - return const EditWorkHistoryScreen(); + if (state is EditWorkHistoryState) { + return EditWorkHistoryScreen(profileId: profileId,token: token!,); } if (state is WorkHistoryErrorState) { return SomethingWentWrong( - message: state.message, onpressed: () {}); + message: state.message, onpressed: () { + context.read().add(GetWorkHistories(profileId: profileId,token: token!)); + }); } return Container(); }, diff --git a/lib/screens/unit2/basic-info/components/cover-image.dart b/lib/screens/unit2/basic-info/components/cover-image.dart index 69c734c..f0f5031 100644 --- a/lib/screens/unit2/basic-info/components/cover-image.dart +++ b/lib/screens/unit2/basic-info/components/cover-image.dart @@ -10,7 +10,8 @@ class CoverImage extends StatelessWidget { color: Colors.grey, child: CachedNetworkImage( imageUrl: - 'https://static.vecteezy.com/system/resources/thumbnails/008/074/253/small/tropical-forest-sunset-nature-background-with-coconut-trees-vector.jpg', + "https://live.staticflickr.com/7320/9052838885_af9b21c79b_b.jpg", + // 'https://static.vecteezy.com/system/resources/thumbnails/008/074/253/small/tropical-forest-sunset-nature-background-with-coconut-trees-vector.jpg', width: double.infinity, height: 220, fit: BoxFit.cover, diff --git a/lib/screens/unit2/homepage.dart/components/menu.dart b/lib/screens/unit2/homepage.dart/components/menu.dart index d635fd0..5ddc5cf 100644 --- a/lib/screens/unit2/homepage.dart/components/menu.dart +++ b/lib/screens/unit2/homepage.dart/components/menu.dart @@ -21,12 +21,15 @@ Widget getTile( if (title.toLowerCase() == "logout") { confirmAlert(context, () async{ await CREDENTIALS!.clear(); + await CREDENTIALS!.deleteAll(['username','password','saved']); Navigator.pushReplacementNamed (context,"/"); },"Logout","Are You sure you want to logout?"); }if(title.toLowerCase() == 'profile'){ ProfileArguments profileArguments = ProfileArguments(token: userData.user!.login!.token!, userID:userData.user!.login!.user!.profileId!); Navigator.pushNamed(context, route,arguments: profileArguments); + }if(title.toLowerCase() == 'basic info'){ + Navigator.pushNamed(context, '/basic-info'); } }, diff --git a/lib/screens/unit2/login/login.dart b/lib/screens/unit2/login/login.dart index 11e7dce..7e2c7d9 100644 --- a/lib/screens/unit2/login/login.dart +++ b/lib/screens/unit2/login/login.dart @@ -36,6 +36,7 @@ class _UniT2LoginState extends State { bool showSuffixIcon = false; bool _showPassword = true; String? password; + String? username; @override Widget build(BuildContext context) { return WillPopScope( @@ -236,7 +237,7 @@ class _UniT2LoginState extends State { TextStyle(color: Colors.white), ), onPressed: () { - password = "nav071394"; + final progress = ProgressHUD.of(context); @@ -244,20 +245,21 @@ class _UniT2LoginState extends State { if (_formKey.currentState! .saveAndValidate()) { + password = _formKey + .currentState! + .value['password']; + username = _formKey + .currentState! + .value['username']; progress?.showWithText( 'Logging in...', ); BlocProvider.of(context) .add(UserLogin( - username: "rjvincentlopeplopez", - password: "shesthequ33n", - // username: _formKey - // .currentState! - // .value['username'], - // password: _formKey - // .currentState! - // .value['password']) + + username:username, + password: password )); } }, diff --git a/lib/sevices/profile/contact_services.dart b/lib/sevices/profile/contact_services.dart index 6e23edf..f3fd302 100644 --- a/lib/sevices/profile/contact_services.dart +++ b/lib/sevices/profile/contact_services.dart @@ -9,11 +9,11 @@ class ContactService { static final ContactService _instance = ContactService(); static ContactService get instance => _instance; - Future> getServiceProvider( + Future> getServiceProvider( {required int serviceTypeId}) async { - String path = Url.instance.getServiceType(); + String path = Url.instance.getCommunicationProvider(); Map params = {"service_type__id": serviceTypeId.toString()}; - List serviceProviders = []; + List serviceProviders = []; Map headers = { 'Content-Type': 'application/json; charset=UTF-8', }; @@ -25,7 +25,7 @@ class ContactService { if (data['data'] != null) { for (var element in data['data']) { CommService commService = CommService.fromJson(element); - serviceProviders.add(commService.serviceProvider!); + serviceProviders.add(commService); } } } @@ -52,7 +52,7 @@ class ContactService { "_numbermail": contactInfo.numbermail, "_active": contactInfo.active, "_primary": contactInfo.primary, - "_commServiceId": contactInfo.commService!.serviceProvider!.id! + "_commServiceId": contactInfo.commService!.id }; Map responseStatus = {}; try { @@ -71,24 +71,38 @@ class ContactService { } //// add -// Future> update( -// {required int profileId, -// required String token, -// required ContactInfo contactInfo}) async { -// String path = "${Url.instance.contactPath()}$profileId/"; -// String authToken = "Token $token"; -// Map headers = { -// 'Content-Type': 'application/json; charset=UTF-8', -// 'Authorization': authToken -// }; -// Map body ={ -// "personid": profileId, -// "_numbermail": contactInfo.numbermail, -// "_active": contactInfo.active, -// "_primary": contactInfo.primary, -// "_commServiceId": contactInfo -// } - // } + Future> add( + {required int profileId, + required String token, + required ContactInfo contactInfo}) async { + String path = "${Url.instance.contactPath()}$profileId/"; + String authToken = "Token $token"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + Map responseStatus = {}; + Map body = { + "personid": profileId, + "_numbermail": contactInfo.numbermail, + "_active": contactInfo.active, + "_primary": contactInfo.primary, + "_commServiceId": contactInfo.commService!.id + }; + try { + http.Response response = await Request.instance + .postRequest(path: path, headers: headers, body: body, param: {}); + if (response.statusCode == 201) { + Map data = jsonDecode(response.body); + responseStatus = data; + } else { + responseStatus.addAll({'success': false}); + } + return responseStatus; + } catch (e) { + throw e.toString(); + } + } ////delete Future deleteContact( @@ -116,7 +130,7 @@ class ContactService { try { http.Response response = await Request.instance.deleteRequest( path: path, headers: headers, body: body, param: params); - if (response.statusCode == 20) { + if (response.statusCode == 200) { Map data = jsonDecode(response.body); success = data['success']; } diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index e872d95..8849535 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -4,10 +4,10 @@ class Url { String host() { // return '192.168.10.221:3003'; - // return 'agusandelnorte.gov.ph'; + return 'agusandelnorte.gov.ph'; // return "192.168.10.219:3000"; // return "devweb.agusandelnorte.gov.ph"; - return 'devapi.agusandelnorte.gov.ph:3004'; + // return 'devapi.agusandelnorte.gov.ph:3004'; } String authentication() { @@ -111,7 +111,7 @@ String getServiceTypes(){ String contactPath(){ return "/api/jobnet_app/profile/pds/basic/contact/"; } -String getServiceType(){ +String getCommunicationProvider(){ return "/api/jobnet_app/comm_services/"; } String deleteContact (){ diff --git a/lib/widgets/splash_screen.dart b/lib/widgets/splash_screen.dart index 69eaaca..364c8ac 100644 --- a/lib/widgets/splash_screen.dart +++ b/lib/widgets/splash_screen.dart @@ -45,8 +45,8 @@ class UniTSplashScreen extends StatelessWidget { height: 1, color: Colors.black)), ), - const SizedBox(height: 5,), - SpinKitThreeBounce(color: Colors.black,size: 32,) + const SizedBox(height: 150,), + const SpinKitCircle(color: primary,size: 42,) // Row( // mainAxisAlignment: MainAxisAlignment.center, // crossAxisAlignment: CrossAxisAlignment.center, From fc8d13d9920f7a06a23ceabfa94ac8e16ceb0e78 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Tue, 11 Apr 2023 09:38:58 +0800 Subject: [PATCH 61/86] commit before merge --- ios/Flutter/Debug.xcconfig | 1 + ios/Flutter/Release.xcconfig | 1 + ios/Podfile | 41 +++ macos/Flutter/Flutter-Debug.xcconfig | 1 + macos/Flutter/Flutter-Release.xcconfig | 1 + macos/Podfile | 40 +++ pubspec.lock | 378 ++++++++++++++++--------- 7 files changed, 333 insertions(+), 130 deletions(-) create mode 100644 ios/Podfile create mode 100644 macos/Podfile diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig index 592ceee..ec97fc6 100644 --- a/ios/Flutter/Debug.xcconfig +++ b/ios/Flutter/Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig index 592ceee..c4855bf 100644 --- a/ios/Flutter/Release.xcconfig +++ b/ios/Flutter/Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/ios/Podfile b/ios/Podfile new file mode 100644 index 0000000..88359b2 --- /dev/null +++ b/ios/Podfile @@ -0,0 +1,41 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '11.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/macos/Flutter/Flutter-Debug.xcconfig b/macos/Flutter/Flutter-Debug.xcconfig index c2efd0b..4b81f9b 100644 --- a/macos/Flutter/Flutter-Debug.xcconfig +++ b/macos/Flutter/Flutter-Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/Flutter-Release.xcconfig b/macos/Flutter/Flutter-Release.xcconfig index c2efd0b..5caa9d1 100644 --- a/macos/Flutter/Flutter-Release.xcconfig +++ b/macos/Flutter/Flutter-Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Podfile b/macos/Podfile new file mode 100644 index 0000000..049abe2 --- /dev/null +++ b/macos/Podfile @@ -0,0 +1,40 @@ +platform :osx, '10.14' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/pubspec.lock b/pubspec.lock index 42cf4b1..fdc4170 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,224 +5,256 @@ packages: dependency: "direct main" description: name: animate_do - url: "https://pub.dartlang.org" + sha256: "9aeacc1a7238f971c039bdf45d13c628be554a242e0251c4ddda09d19a1a923f" + url: "https://pub.dev" source: hosted version: "3.0.2" archive: dependency: transitive description: name: archive - url: "https://pub.dartlang.org" + sha256: ed7cc591a948744994714375caf9a2ce89e1d82e8243997c8a2994d57181c212 + url: "https://pub.dev" source: hosted version: "3.3.5" async: dependency: transitive description: name: async - url: "https://pub.dartlang.org" + sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + url: "https://pub.dev" source: hosted - version: "2.9.0" + version: "2.10.0" auto_size_text: dependency: "direct main" description: name: auto_size_text - url: "https://pub.dartlang.org" + sha256: "3f5261cd3fb5f2a9ab4e2fc3fba84fd9fcaac8821f20a1d4e71f557521b22599" + url: "https://pub.dev" source: hosted version: "3.0.0" awesome_dialog: dependency: "direct main" description: name: awesome_dialog - url: "https://pub.dartlang.org" + sha256: ac08268b991f228fc6b8880b68ec030d2941bcc8df8b6a6551fb79f2bd36b7da + url: "https://pub.dev" source: hosted version: "3.0.2" azlistview: dependency: "direct main" description: name: azlistview - url: "https://pub.dartlang.org" + sha256: "93e865f11777a271b439f0d6b00799c0797e9daeec2e082a2e01373809c4b90d" + url: "https://pub.dev" source: hosted version: "2.0.0" barcode_scan2: dependency: "direct main" description: name: barcode_scan2 - url: "https://pub.dartlang.org" + sha256: f9af9252b8f3f5fa446f5456fd45f8871d09f883d8389a1d608b39231bfbc3fa + url: "https://pub.dev" source: hosted version: "4.2.3" bloc: dependency: transitive description: name: bloc - url: "https://pub.dartlang.org" + sha256: bd4f8027bfa60d96c8046dec5ce74c463b2c918dce1b0d36593575995344534a + url: "https://pub.dev" source: hosted version: "8.1.0" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.dartlang.org" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" cached_network_image: dependency: "direct main" description: name: cached_network_image - url: "https://pub.dartlang.org" + sha256: fd3d0dc1d451f9a252b32d95d3f0c3c487bc41a75eba2e6097cb0b9c71491b15 + url: "https://pub.dev" source: hosted version: "3.2.3" cached_network_image_platform_interface: dependency: transitive description: name: cached_network_image_platform_interface - url: "https://pub.dartlang.org" + sha256: bb2b8403b4ccdc60ef5f25c70dead1f3d32d24b9d6117cfc087f496b178594a7 + url: "https://pub.dev" source: hosted version: "2.0.0" cached_network_image_web: dependency: transitive description: name: cached_network_image_web - url: "https://pub.dartlang.org" + sha256: b8eb814ebfcb4dea049680f8c1ffb2df399e4d03bf7a352c775e26fa06e02fa0 + url: "https://pub.dev" source: hosted version: "1.0.2" characters: dependency: transitive description: name: characters - url: "https://pub.dartlang.org" + sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + url: "https://pub.dev" source: hosted version: "1.2.1" clock: dependency: transitive description: name: clock - url: "https://pub.dartlang.org" + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" source: hosted version: "1.1.1" collection: dependency: transitive description: name: collection - url: "https://pub.dartlang.org" + sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.17.0" convert: dependency: transitive description: name: convert - url: "https://pub.dartlang.org" + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" source: hosted version: "3.1.1" convex_bottom_bar: dependency: "direct main" description: name: convex_bottom_bar - url: "https://pub.dartlang.org" + sha256: "6c9d23fa309b2cf2cb4804a6318e34694f4714c29fac79621ff88b71266fbe57" + url: "https://pub.dev" source: hosted version: "3.1.0+1" cool_alert: dependency: "direct main" description: name: cool_alert - url: "https://pub.dartlang.org" + sha256: "48a0b6c04914b6dc7e8d7eaccf2adf21bace6533a3eda0aeb412e0d7a03dc00a" + url: "https://pub.dev" source: hosted version: "1.1.0" crypto: dependency: transitive description: name: crypto - url: "https://pub.dartlang.org" + sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 + url: "https://pub.dev" source: hosted version: "3.0.2" date_time_picker: dependency: "direct main" description: name: date_time_picker - url: "https://pub.dartlang.org" + sha256: "6923c568bcb67a66ab7e083708d0adbcae8214b41bb84d49febc17e89e06fc4a" + url: "https://pub.dev" source: hosted version: "2.1.0" device_frame: dependency: transitive description: name: device_frame - url: "https://pub.dartlang.org" + sha256: afe76182aec178d171953d9b4a50a43c57c7cf3c77d8b09a48bf30c8fa04dd9d + url: "https://pub.dev" source: hosted version: "1.1.0" device_preview: dependency: "direct main" description: name: device_preview - url: "https://pub.dartlang.org" + sha256: "2f097bf31b929e15e6756dbe0ec1bcb63952ab9ed51c25dc5a2c722d2b21fdaf" + url: "https://pub.dev" source: hosted version: "1.1.0" dio: dependency: "direct main" description: name: dio - url: "https://pub.dartlang.org" + sha256: "7d328c4d898a61efc3cd93655a0955858e29a0aa647f0f9e02d59b3bb275e2e8" + url: "https://pub.dev" source: hosted version: "4.0.6" easy_app_installer: dependency: "direct main" description: name: easy_app_installer - url: "https://pub.dartlang.org" + sha256: d7287bf247fe6bc85ad07dfb85804757a6dd2f47e61b0e7ce9195ec7f13e09eb + url: "https://pub.dev" source: hosted version: "1.0.0" equatable: dependency: "direct main" description: name: equatable - url: "https://pub.dartlang.org" + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + url: "https://pub.dev" source: hosted version: "2.0.5" expandable_group: dependency: "direct main" description: name: expandable_group - url: "https://pub.dartlang.org" + sha256: "874f9c2daef8a21366fb1df85405f80ee8e8be6e3c2ced727c303641b33b9a95" + url: "https://pub.dev" source: hosted version: "0.0.8" fake_async: dependency: transitive description: name: fake_async - url: "https://pub.dartlang.org" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" source: hosted version: "1.3.1" ffi: dependency: transitive description: name: ffi - url: "https://pub.dartlang.org" + sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 + url: "https://pub.dev" source: hosted version: "2.0.1" file: dependency: transitive description: name: file - url: "https://pub.dartlang.org" + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" source: hosted version: "6.1.4" file_utils: dependency: transitive description: name: file_utils - url: "https://pub.dartlang.org" + sha256: d1e64389a22649095c8405c9e177272caf05139255931c9ff30d53b5c9bcaa34 + url: "https://pub.dev" source: hosted version: "1.0.1" fixnum: dependency: transitive description: name: fixnum - url: "https://pub.dartlang.org" + sha256: "04be3e934c52e082558cc9ee21f42f5c1cd7a1262f4c63cd0357c08d5bba81ec" + url: "https://pub.dev" source: hosted version: "1.0.1" flare_flutter: dependency: transitive description: name: flare_flutter - url: "https://pub.dartlang.org" + sha256: "99d63c60f00fac81249ce6410ee015d7b125c63d8278a30da81edf3317a1f6a0" + url: "https://pub.dev" source: hosted version: "3.0.2" flutter: @@ -234,42 +266,48 @@ packages: dependency: "direct main" description: name: flutter_bloc - url: "https://pub.dartlang.org" + sha256: "890c51c8007f0182360e523518a0c732efb89876cb4669307af7efada5b55557" + url: "https://pub.dev" source: hosted version: "8.1.1" flutter_blurhash: dependency: transitive description: name: flutter_blurhash - url: "https://pub.dartlang.org" + sha256: "05001537bd3fac7644fa6558b09ec8c0a3f2eba78c0765f88912882b1331a5c6" + url: "https://pub.dev" source: hosted version: "0.7.0" flutter_cache_manager: dependency: transitive description: name: flutter_cache_manager - url: "https://pub.dartlang.org" + sha256: "32cd900555219333326a2d0653aaaf8671264c29befa65bbd9856d204a4c9fb3" + url: "https://pub.dev" source: hosted version: "3.3.0" flutter_custom_clippers: dependency: "direct main" description: name: flutter_custom_clippers - url: "https://pub.dartlang.org" + sha256: "473e3daf61c2a6cee0ad137393259a25223239d519a131c7ec1cac04d06e5407" + url: "https://pub.dev" source: hosted version: "2.1.0" flutter_form_builder: dependency: "direct main" description: name: flutter_form_builder - url: "https://pub.dartlang.org" + sha256: "768b11307e71c60cb66351a87984815bd438b50aa58b5de02c9256a8f2964bee" + url: "https://pub.dev" source: hosted version: "7.7.0" flutter_lints: dependency: "direct dev" description: name: flutter_lints - url: "https://pub.dartlang.org" + sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + url: "https://pub.dev" source: hosted version: "2.0.1" flutter_localizations: @@ -281,28 +319,32 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - url: "https://pub.dartlang.org" + sha256: "60fc7b78455b94e6de2333d2f95196d32cf5c22f4b0b0520a628804cb463503b" + url: "https://pub.dev" source: hosted version: "2.0.7" flutter_progress_hud: dependency: "direct main" description: name: flutter_progress_hud - url: "https://pub.dartlang.org" + sha256: "19a4889460b7482c5026a936b768996dade4daad8570e8c0493e292d57121dbb" + url: "https://pub.dev" source: hosted version: "2.0.2" flutter_spinkit: dependency: "direct main" description: name: flutter_spinkit - url: "https://pub.dartlang.org" + sha256: "77a2117c0517ff909221f3160b8eb20052ab5216107581168af574ac1f05dff8" + url: "https://pub.dev" source: hosted version: "5.1.0" flutter_svg: dependency: "direct main" description: name: flutter_svg - url: "https://pub.dartlang.org" + sha256: "6ff9fa12892ae074092de2fa6a9938fb21dbabfdaa2ff57dc697ff912fc8d4b2" + url: "https://pub.dev" source: hosted version: "1.1.6" flutter_test: @@ -319,413 +361,472 @@ packages: dependency: "direct main" description: name: flutter_zoom_drawer - url: "https://pub.dartlang.org" + sha256: "93c4f2cfe1edfe2f9e48b94a7f9538520d2f2593e202b8e89c394b7d27137a8e" + url: "https://pub.dev" source: hosted version: "3.0.3" fluttericon: dependency: "direct main" description: name: fluttericon - url: "https://pub.dartlang.org" + sha256: "252fa8043826e93d972a602497a260cb3d62b5aea6d045793e4381590f2c1e99" + url: "https://pub.dev" source: hosted version: "2.0.0" fluttertoast: dependency: "direct main" description: name: fluttertoast - url: "https://pub.dartlang.org" + sha256: "7cc92eabe01e3f1babe1571c5560b135dfc762a34e41e9056881e2196b178ec1" + url: "https://pub.dev" source: hosted version: "8.1.2" form_builder_validators: dependency: "direct main" description: name: form_builder_validators - url: "https://pub.dartlang.org" + sha256: e4d54c0c513e3e36ae4e4905994873a0a907585407212effeef39a68e759670c + url: "https://pub.dev" source: hosted version: "8.4.0" freezed_annotation: dependency: transitive description: name: freezed_annotation - url: "https://pub.dartlang.org" + sha256: aeac15850ef1b38ee368d4c53ba9a847e900bb2c53a4db3f6881cbb3cb684338 + url: "https://pub.dev" source: hosted version: "2.2.0" globbing: dependency: transitive description: name: globbing - url: "https://pub.dartlang.org" + sha256: "4f89cfaf6fa74c9c1740a96259da06bd45411ede56744e28017cc534a12b6e2d" + url: "https://pub.dev" source: hosted version: "1.0.0" graphs: dependency: transitive description: name: graphs - url: "https://pub.dartlang.org" + sha256: f9e130f3259f52d26f0cfc0e964513796dafed572fa52e45d2f8d6ca14db39b2 + url: "https://pub.dev" source: hosted version: "2.2.0" http: dependency: transitive description: name: http - url: "https://pub.dartlang.org" + sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482" + url: "https://pub.dev" source: hosted version: "0.13.5" http_parser: dependency: transitive description: name: http_parser - url: "https://pub.dartlang.org" + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" source: hosted version: "4.0.2" image: dependency: transitive description: name: image - url: "https://pub.dartlang.org" + sha256: "8e9d133755c3e84c73288363e6343157c383a0c6c56fc51afcc5d4d7180306d6" + url: "https://pub.dev" source: hosted version: "3.3.0" intl: dependency: "direct main" description: name: intl - url: "https://pub.dartlang.org" + sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" + url: "https://pub.dev" source: hosted version: "0.17.0" js: dependency: transitive description: name: js - url: "https://pub.dartlang.org" + sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + url: "https://pub.dev" source: hosted - version: "0.6.4" + version: "0.6.5" json_annotation: dependency: transitive description: name: json_annotation - url: "https://pub.dartlang.org" + sha256: "3520fa844009431b5d4491a5a778603520cdc399ab3406332dcc50f93547258c" + url: "https://pub.dev" source: hosted version: "4.7.0" lints: dependency: transitive description: name: lints - url: "https://pub.dartlang.org" + sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + url: "https://pub.dev" source: hosted version: "2.0.1" lottie: dependency: transitive description: name: lottie - url: "https://pub.dartlang.org" + sha256: "893da7a0022ec2fcaa616f34529a081f617e86cc501105b856e5a3184c58c7c2" + url: "https://pub.dev" source: hosted version: "1.4.3" matcher: dependency: transitive description: name: matcher - url: "https://pub.dartlang.org" + sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + url: "https://pub.dev" source: hosted - version: "0.12.12" + version: "0.12.13" material_color_utilities: dependency: transitive description: name: material_color_utilities - url: "https://pub.dartlang.org" + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + url: "https://pub.dev" source: hosted - version: "0.1.5" + version: "0.2.0" meta: dependency: transitive description: name: meta - url: "https://pub.dartlang.org" + sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + url: "https://pub.dev" source: hosted version: "1.8.0" nested: dependency: transitive description: name: nested - url: "https://pub.dartlang.org" + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" source: hosted version: "1.0.0" octo_image: dependency: transitive description: name: octo_image - url: "https://pub.dartlang.org" + sha256: "107f3ed1330006a3bea63615e81cf637433f5135a52466c7caa0e7152bca9143" + url: "https://pub.dev" source: hosted version: "1.0.2" package_info_plus: dependency: "direct main" description: name: package_info_plus - url: "https://pub.dartlang.org" + sha256: f619162573096d428ccde2e33f92e05b5a179cd6f0e3120c1005f181bee8ed16 + url: "https://pub.dev" source: hosted version: "3.0.2" package_info_plus_platform_interface: dependency: transitive description: name: package_info_plus_platform_interface - url: "https://pub.dartlang.org" + sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6" + url: "https://pub.dev" source: hosted version: "2.0.1" path: dependency: transitive description: name: path - url: "https://pub.dartlang.org" + sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + url: "https://pub.dev" source: hosted version: "1.8.2" path_drawing: dependency: transitive description: name: path_drawing - url: "https://pub.dartlang.org" + sha256: bbb1934c0cbb03091af082a6389ca2080345291ef07a5fa6d6e078ba8682f977 + url: "https://pub.dev" source: hosted version: "1.0.1" path_parsing: dependency: transitive description: name: path_parsing - url: "https://pub.dartlang.org" + sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf + url: "https://pub.dev" source: hosted version: "1.0.1" path_provider: dependency: "direct main" description: name: path_provider - url: "https://pub.dartlang.org" + sha256: "050e8e85e4b7fecdf2bb3682c1c64c4887a183720c802d323de8a5fd76d372dd" + url: "https://pub.dev" source: hosted version: "2.0.11" path_provider_android: dependency: transitive description: name: path_provider_android - url: "https://pub.dartlang.org" + sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e + url: "https://pub.dev" source: hosted version: "2.0.22" path_provider_ios: dependency: transitive description: name: path_provider_ios - url: "https://pub.dartlang.org" + sha256: "03d639406f5343478352433f00d3c4394d52dac8df3d847869c5e2333e0bbce8" + url: "https://pub.dev" source: hosted version: "2.0.11" path_provider_linux: dependency: transitive description: name: path_provider_linux - url: "https://pub.dartlang.org" + sha256: ab0987bf95bc591da42dffb38c77398fc43309f0b9b894dcc5d6f40c4b26c379 + url: "https://pub.dev" source: hosted version: "2.1.7" path_provider_macos: dependency: transitive description: name: path_provider_macos - url: "https://pub.dartlang.org" + sha256: "2a97e7fbb7ae9dcd0dfc1220a78e9ec3e71da691912e617e8715ff2a13086ae8" + url: "https://pub.dev" source: hosted version: "2.0.6" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - url: "https://pub.dartlang.org" + sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76 + url: "https://pub.dev" source: hosted version: "2.0.5" path_provider_windows: dependency: transitive description: name: path_provider_windows - url: "https://pub.dartlang.org" + sha256: bcabbe399d4042b8ee687e17548d5d3f527255253b4a639f5f8d2094a9c2b45c + url: "https://pub.dev" source: hosted version: "2.1.3" pedantic: dependency: transitive description: name: pedantic - url: "https://pub.dartlang.org" + sha256: "67fc27ed9639506c856c840ccce7594d0bdcd91bc8d53d6e52359449a1d50602" + url: "https://pub.dev" source: hosted version: "1.11.1" permission_handler: dependency: "direct main" description: name: permission_handler - url: "https://pub.dartlang.org" + sha256: "33c6a1253d1f95fd06fa74b65b7ba907ae9811f9d5c1d3150e51417d04b8d6a8" + url: "https://pub.dev" source: hosted version: "10.2.0" permission_handler_android: dependency: transitive description: name: permission_handler_android - url: "https://pub.dartlang.org" + sha256: "8028362b40c4a45298f1cbfccd227c8dd6caf0e27088a69f2ba2ab15464159e2" + url: "https://pub.dev" source: hosted version: "10.2.0" permission_handler_apple: dependency: transitive description: name: permission_handler_apple - url: "https://pub.dartlang.org" + sha256: "9c370ef6a18b1c4b2f7f35944d644a56aa23576f23abee654cf73968de93f163" + url: "https://pub.dev" source: hosted version: "9.0.7" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface - url: "https://pub.dartlang.org" + sha256: "68abbc472002b5e6dfce47fe9898c6b7d8328d58b5d2524f75e277c07a97eb84" + url: "https://pub.dev" source: hosted version: "3.9.0" permission_handler_windows: dependency: transitive description: name: permission_handler_windows - url: "https://pub.dartlang.org" + sha256: f67cab14b4328574938ecea2db3475dad7af7ead6afab6338772c5f88963e38b + url: "https://pub.dev" source: hosted version: "0.1.2" petitparser: dependency: transitive description: name: petitparser - url: "https://pub.dartlang.org" + sha256: "49392a45ced973e8d94a85fdb21293fbb40ba805fc49f2965101ae748a3683b4" + url: "https://pub.dev" source: hosted version: "5.1.0" platform: dependency: transitive description: name: platform - url: "https://pub.dartlang.org" + sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + url: "https://pub.dev" source: hosted version: "3.1.0" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - url: "https://pub.dartlang.org" + sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a + url: "https://pub.dev" source: hosted version: "2.1.3" pointycastle: dependency: transitive description: name: pointycastle - url: "https://pub.dartlang.org" + sha256: db7306cf0249f838d1a24af52b5a5887c5bf7f31d8bb4e827d071dc0939ad346 + url: "https://pub.dev" source: hosted version: "3.6.2" process: dependency: transitive description: name: process - url: "https://pub.dartlang.org" + sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + url: "https://pub.dev" source: hosted version: "4.2.4" protobuf: dependency: transitive description: name: protobuf - url: "https://pub.dartlang.org" + sha256: "01dd9bd0fa02548bf2ceee13545d4a0ec6046459d847b6b061d8a27237108a08" + url: "https://pub.dev" source: hosted version: "2.1.0" provider: dependency: transitive description: name: provider - url: "https://pub.dartlang.org" + sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + url: "https://pub.dev" source: hosted version: "6.0.5" qr: dependency: transitive description: name: qr - url: "https://pub.dartlang.org" + sha256: "5c4208b4dc0d55c3184d10d83ee0ded6212dc2b5e2ba17c5a0c0aab279128d21" + url: "https://pub.dev" source: hosted version: "2.1.0" qr_flutter: dependency: "direct main" description: name: qr_flutter - url: "https://pub.dartlang.org" + sha256: c5c121c54cb6dd837b9b9d57eb7bc7ec6df4aee741032060c8833a678c80b87e + url: "https://pub.dev" source: hosted version: "4.0.0" rive: dependency: transitive description: name: rive - url: "https://pub.dartlang.org" + sha256: "22e3755b75f4ea4492d2fecf4fc2acf1c8d0073df39781d290a20cbfe74c3760" + url: "https://pub.dev" source: hosted version: "0.9.1" rxdart: dependency: transitive description: name: rxdart - url: "https://pub.dartlang.org" + sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" + url: "https://pub.dev" source: hosted version: "0.27.7" scrollable_positioned_list: dependency: transitive description: name: scrollable_positioned_list - url: "https://pub.dartlang.org" + sha256: "9566352ab9ba05794ee6c8864f154afba5d36c5637d0e3e32c615ba4ceb92772" + url: "https://pub.dev" source: hosted version: "0.2.3" shared_preferences: dependency: transitive description: name: shared_preferences - url: "https://pub.dartlang.org" + sha256: "76917b7d4b9526b2ba416808a7eb9fb2863c1a09cf63ec85f1453da240fa818a" + url: "https://pub.dev" source: hosted version: "2.0.15" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - url: "https://pub.dartlang.org" + sha256: "8e251f3c986002b65fed6396bce81f379fb63c27317d49743cf289fd0fd1ab97" + url: "https://pub.dev" source: hosted version: "2.0.14" shared_preferences_ios: dependency: transitive description: name: shared_preferences_ios - url: "https://pub.dartlang.org" + sha256: "585a14cefec7da8c9c2fb8cd283a3bb726b4155c0952afe6a0caaa7b2272de34" + url: "https://pub.dev" source: hosted version: "2.1.1" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux - url: "https://pub.dartlang.org" + sha256: fbc3cd6826896b66a5f576b025e4f344f780c84ea7f8203097a353370607a2c8 + url: "https://pub.dev" source: hosted version: "2.1.2" shared_preferences_macos: dependency: transitive description: name: shared_preferences_macos - url: "https://pub.dartlang.org" + sha256: "81b6a60b2d27020eb0fc41f4cebc91353047309967901a79ee8203e40c42ed46" + url: "https://pub.dev" source: hosted version: "2.0.5" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface - url: "https://pub.dartlang.org" + sha256: da9431745ede5ece47bc26d5d73a9d3c6936ef6945c101a5aca46f62e52c1cf3 + url: "https://pub.dev" source: hosted version: "2.1.0" shared_preferences_web: dependency: transitive description: name: shared_preferences_web - url: "https://pub.dartlang.org" + sha256: a4b5bc37fe1b368bbc81f953197d55e12f49d0296e7e412dfe2d2d77d6929958 + url: "https://pub.dev" source: hosted version: "2.0.4" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows - url: "https://pub.dartlang.org" + sha256: "07c274c2115d4d5e4280622abb09f0980e2c5b1fcdc98ae9f59a3bad5bfc1f26" + url: "https://pub.dev" source: hosted version: "2.1.2" signature: dependency: "direct main" description: name: signature - url: "https://pub.dartlang.org" + sha256: ad23383717dfa926204695ef6928ff513a77387be1b4a8c685099ce3ec35e5f8 + url: "https://pub.dev" source: hosted version: "5.3.0" sky_engine: @@ -737,119 +838,136 @@ packages: dependency: transitive description: name: source_span - url: "https://pub.dartlang.org" + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" sqflite: dependency: transitive description: name: sqflite - url: "https://pub.dartlang.org" + sha256: "2b1697c7b78576fdc722c358f16f62171bd56e92dc13422d9e44be3fc446c276" + url: "https://pub.dev" source: hosted version: "2.2.2" sqflite_common: dependency: transitive description: name: sqflite_common - url: "https://pub.dartlang.org" + sha256: "0c21a187d645aa65da5be6997c0c713eed61e049158870ae2de157e6897067ab" + url: "https://pub.dev" source: hosted version: "2.4.0+2" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.dartlang.org" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.dartlang.org" + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.dartlang.org" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.2.0" synchronized: dependency: transitive description: name: synchronized - url: "https://pub.dartlang.org" + sha256: "7b530acd9cb7c71b0019a1e7fa22c4105e675557a4400b6a401c71c5e0ade1ac" + url: "https://pub.dev" source: hosted version: "3.0.0+3" system_info2: dependency: "direct main" description: name: system_info2 - url: "https://pub.dartlang.org" + sha256: "90621f3ba586e1f268e38cc7951b172cd4d997e43dc1fbed12eb334c8a22a886" + url: "https://pub.dev" source: hosted version: "2.0.4" term_glyph: dependency: transitive description: name: term_glyph - url: "https://pub.dartlang.org" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" source: hosted version: "1.2.1" test_api: dependency: transitive description: name: test_api - url: "https://pub.dartlang.org" + sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + url: "https://pub.dev" source: hosted - version: "0.4.12" + version: "0.4.16" toggle_switch: dependency: "direct main" description: name: toggle_switch - url: "https://pub.dartlang.org" + sha256: "82c778c4bfe93af154a41e346ccd1d5b0cb6a50f8f187941412edd0a9bbf51ee" + url: "https://pub.dev" source: hosted version: "2.0.1" typed_data: dependency: transitive description: name: typed_data - url: "https://pub.dartlang.org" + sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + url: "https://pub.dev" source: hosted version: "1.3.1" uuid: dependency: transitive description: name: uuid - url: "https://pub.dartlang.org" + sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" + url: "https://pub.dev" source: hosted version: "3.0.7" vector_math: dependency: transitive description: name: vector_math - url: "https://pub.dartlang.org" + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" win32: dependency: transitive description: name: win32 - url: "https://pub.dartlang.org" + sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46 + url: "https://pub.dev" source: hosted version: "3.1.3" xdg_directories: dependency: transitive description: name: xdg_directories - url: "https://pub.dartlang.org" + sha256: "11541eedefbcaec9de35aa82650b695297ce668662bbd6e3911a7fabdbde589f" + url: "https://pub.dev" source: hosted version: "0.2.0+2" xml: dependency: transitive description: name: xml - url: "https://pub.dartlang.org" + sha256: ac0e3f4bf00ba2708c33fbabbbe766300e509f8c82dbd4ab6525039813f7e2fb + url: "https://pub.dev" source: hosted version: "6.1.0" sdks: From 7f0f9009ec8cb10ceb1fa319d52e4bfd5e2184d3 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Tue, 11 Apr 2023 14:08:10 +0800 Subject: [PATCH 62/86] commit stash --- lib/bloc/sos/sos_bloc.dart | 22 + lib/bloc/sos/sos_event.dart | 13 + lib/bloc/sos/sos_state.dart | 27 + lib/screens/sos/components/add_mobile.dart | 136 ++ lib/screens/sos/components/request_sos.dart | 109 ++ lib/screens/sos/components/sos_received.dart | 133 ++ lib/screens/sos/index.dart | 56 + lib/screens/unit2/login/login.dart | 2 +- lib/sevices/sos/sos_service.dart | 31 + lib/utils/app_router.dart | 16 +- pubspec.lock | 1327 ++++++++++++++++++ 11 files changed, 1868 insertions(+), 4 deletions(-) create mode 100644 lib/bloc/sos/sos_bloc.dart create mode 100644 lib/bloc/sos/sos_event.dart create mode 100644 lib/bloc/sos/sos_state.dart create mode 100644 lib/screens/sos/components/add_mobile.dart create mode 100644 lib/screens/sos/components/request_sos.dart create mode 100644 lib/screens/sos/components/sos_received.dart create mode 100644 lib/screens/sos/index.dart create mode 100644 lib/sevices/sos/sos_service.dart create mode 100644 pubspec.lock diff --git a/lib/bloc/sos/sos_bloc.dart b/lib/bloc/sos/sos_bloc.dart new file mode 100644 index 0000000..03e5341 --- /dev/null +++ b/lib/bloc/sos/sos_bloc.dart @@ -0,0 +1,22 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:location/location.dart'; +import 'package:unit2/sevices/sos/sos_service.dart'; + +part 'sos_event.dart'; +part 'sos_state.dart'; + +class SosBloc extends Bloc { + SosBloc() : super(SosInitial()) { + LocationData? locationData; + on((event, emit)async { + emit(LoadingState()); + + LocationData? newLocationData = + await SosService.instance.getUserLocation(); + locationData = newLocationData; + // User location Loaded and there is no mobile numbers in cache + emit(UserLocationLoaded(locationData: locationData!)); + }); + } +} diff --git a/lib/bloc/sos/sos_event.dart b/lib/bloc/sos/sos_event.dart new file mode 100644 index 0000000..0880459 --- /dev/null +++ b/lib/bloc/sos/sos_event.dart @@ -0,0 +1,13 @@ +part of 'sos_bloc.dart'; + +abstract class SosEvent extends Equatable { + const SosEvent(); + + @override + List get props => []; +} + +class LoadUserLocation extends SosEvent { + @override + List get props => []; +} diff --git a/lib/bloc/sos/sos_state.dart b/lib/bloc/sos/sos_state.dart new file mode 100644 index 0000000..ee325b9 --- /dev/null +++ b/lib/bloc/sos/sos_state.dart @@ -0,0 +1,27 @@ +part of 'sos_bloc.dart'; + +abstract class SosState extends Equatable { + const SosState(); + + @override + List get props => []; +} + +class SosInitial extends SosState {} + +class UserLocationLoaded extends SosState { + final LocationData locationData; + const UserLocationLoaded({required this.locationData}); + @override + List get props => [locationData]; +} +class ErrorState extends SosState{ + final String message; + const ErrorState({required this.message}); + @override + List get props => [message]; +} + +class LoadingState extends SosState{ + +} diff --git a/lib/screens/sos/components/add_mobile.dart b/lib/screens/sos/components/add_mobile.dart new file mode 100644 index 0000000..80b16b4 --- /dev/null +++ b/lib/screens/sos/components/add_mobile.dart @@ -0,0 +1,136 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:unit2/screens/sos/components/request_sos.dart'; +import 'package:unit2/theme-data.dart/text-styles.dart'; +import 'package:unit2/utils/screen_info.dart'; +import '../../../theme-data.dart/btn-style.dart'; +import '../../../theme-data.dart/colors.dart'; +import '../../../theme-data.dart/form-style.dart'; +import '../../../utils/global.dart'; +import '../../../utils/text_container.dart'; +import '../../../utils/validators.dart'; +import '../../../widgets/wave.dart'; + +class AddMobile extends StatelessWidget { + AddMobile({super.key}); + final _formKey = GlobalKey(); + @override + Widget build(BuildContext context) { + return WillPopScope( + onWillPop: () async { + return true; + }, + child: SafeArea( + child: Scaffold( + appBar: AppBar( + title: const Text("Add contact info"), + centerTitle: true, + backgroundColor: primary, + elevation: 0, + ), + resizeToAvoidBottomInset: true, + body: SingleChildScrollView( + child: SizedBox( + height: screenHeight * .90, + child: Stack( + children: [ + Wave(height: blockSizeVertical * 8), + Positioned( + bottom: 0, + right: 0, + child: WaveReverse(height: blockSizeVertical * 8)), + Container( + height: screenHeight, + padding: isMobile() + ? const EdgeInsets.symmetric(horizontal: 24) + : const EdgeInsets.symmetric(horizontal: 60), + width: double.infinity, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + height: isMobile() + ? screenHeight * .12 + : screenHeight * .20), + SvgPicture.asset( + 'assets/svgs/add_mobile.svg', + height: isMobile() + ? blockSizeVertical * 22 + : blockSizeVertical * 30, + allowDrawingOutsideViewBox: true, + ), + const SizedBox( + height: 24, + ), + Text(addMobile, style: titleTextStyle()), + const SizedBox( + height: 8, + ), + Text(addMobileCaption, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.caption), + const SizedBox( + height: 24, + ), + FormBuilder( + key: _formKey, + child: Column( + children: [ + // Mobile number 1 + FormBuilderTextField( + name: 'mobile1', + validator: mobileNumberValidator, + maxLength: 11, + decoration: + normalTextFieldStyle(mobile1, "+63")), + const SizedBox( + height: 12, + ), + FormBuilderTextField( + name: 'mobile2', + maxLength: 11, + decoration: + normalTextFieldStyle(mobile2, "+63")), + + SizedBox( + height: isMobile() + ? blockSizeVertical * 3 + : blockSizeHorizontal * 5), + SizedBox( + width: double.infinity, + height: screenHeight * .06, + child: ElevatedButton( + style: secondaryBtnStyle(second, + Colors.transparent, Colors.white54), + child: const Text( + submit, + style: TextStyle(color: Colors.white), + ), + onPressed: () { + if (_formKey.currentState! + .saveAndValidate()) { + + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + return RequestSOS(); + })); + + } + + // } + }, + ), + ), + ], + )) + ]), + ), + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/screens/sos/components/request_sos.dart b/lib/screens/sos/components/request_sos.dart new file mode 100644 index 0000000..f6e6f6d --- /dev/null +++ b/lib/screens/sos/components/request_sos.dart @@ -0,0 +1,109 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/container.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:fluttericon/font_awesome5_icons.dart'; +import 'package:fluttericon/typicons_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:unit2/screens/sos/components/mobile.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/text_container.dart'; + +import '../../../theme-data.dart/btn-style.dart'; +import '../../../theme-data.dart/form-style.dart'; +import '../../../utils/global.dart'; + +class RequestSOS extends StatefulWidget { + const RequestSOS({super.key}); + + @override + State createState() => _RequestSOSState(); +} + +class _RequestSOSState extends State { + @override + Widget build(BuildContext context) { + return SafeArea( + child: Scaffold( + appBar: AppBar( + centerTitle: true, + title: const Text(sOSTitle), + backgroundColor: second, + ), + body: SingleChildScrollView( + child: Container( + height: screenHeight * .84, + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 10), + child: Column( + children: [ + SizedBox( + height: blockSizeVertical * 2, + ), + SvgPicture.asset( + 'assets/svgs/request_sos.svg', + height: blockSizeVertical * 22, + allowDrawingOutsideViewBox: true, + ), + Mobile( + title: "09661548775", + subtitle: mobile1, + onPressed: () {}), + const Divider(), + Mobile( + title: "09661548775", + subtitle: mobile2, + onPressed: () {}), + const Divider(), + ListTile( + leading: const Icon( + Typicons.location, + color: second, + ), + title: Text("Latitude/Longitude"), + subtitle: Text( + currentLocation, + style: Theme.of(context).textTheme.caption, + ), + ), + FormBuilderTextField( + name: "message", + validator: FormBuilderValidators.compose([ + FormBuilderValidators.required( + errorText: messageRequired) + ]), + autovalidateMode: AutovalidateMode.onUserInteraction, + maxLines: 5, + decoration: normalTextFieldStyle("", sosMessage), + ), + const Expanded( + child: SizedBox(), + ), + SizedBox( + width: double.infinity, + height: screenHeight * .06, + child: ElevatedButton( + style: secondaryBtnStyle( + second, Colors.transparent, Colors.white54), + child: const Text( + submit, + style: TextStyle(color: Colors.white), + ), + onPressed: () { + // if (_formKey.currentState.validate()) { + // _formKey.currentState.save(); + // BlocProvider.of(context) + // .add(UserWebLogin( + // password: password, + // username: username)); + // } + }, + ), + ), + ], + )), + ), + ), + ); + } +} diff --git a/lib/screens/sos/components/sos_received.dart b/lib/screens/sos/components/sos_received.dart new file mode 100644 index 0000000..3d9192d --- /dev/null +++ b/lib/screens/sos/components/sos_received.dart @@ -0,0 +1,133 @@ +import 'package:animate_do/animate_do.dart'; +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:unit2/theme-data.dart/btn-style.dart'; +import 'package:unit2/utils/text_container.dart'; +import '../../../theme-data.dart/colors.dart'; +import '../../../utils/global.dart'; + +class SOSreceived extends StatelessWidget { + final Function onpressed; + const SOSreceived({required Key key, required this.onpressed}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 35), + height: screenHeight, + child: Stack( + children: [ + Positioned( + bottom: 0, + child: SizedBox( + width: screenWidth, + height: screenHeight / 2, + child: Opacity( + opacity: .2, + child: Image.asset( + "assets/emergency.png", + fit: BoxFit.cover, + ), + ), + )), + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + height: blockSizeVertical * 6, + ), + Bounce( + from: 20, + infinite: true, + delay: const Duration(milliseconds: 800), + child: Stack( + alignment: AlignmentDirectional.topCenter, + children: [ + Container( + margin: const EdgeInsets.only(top: 20), + child: CircleAvatar( + radius: blockSizeVertical * 8, + backgroundColor: second, + child: Container( + margin: const EdgeInsets.only(top: 25), + child: SvgPicture.asset( + 'assets/sos.svg', + height: blockSizeHorizontal * 17, + color: Colors.white, + allowDrawingOutsideViewBox: true, + alignment: Alignment.bottomCenter, + ), + ), + ), + ), + Positioned( + top: blockSizeVertical * 3, + child: const SpinKitPulse( + color: primary, + size: 120, + ), + ), + ], + ), + ), + const SizedBox(height: 16), + SlideInUp( + from: 50, + child: AutoSizeText( + sosReceived, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.displayMedium!.copyWith( + fontSize: 40, + color: third, + fontWeight: FontWeight.w500, + ), + ), + ), + const SizedBox( + height: 8, + ), + SlideInUp( + from: 50, + child: Container( + padding: const EdgeInsets.all(15), + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(15))), + child: AutoSizeText( + sOSReceivedMessage, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.caption!.copyWith( + fontSize: 16, + color: Colors.black87, + ), + ), + ), + ), + const SizedBox( + height: 40, + ), + Expanded(child: Container()), + SlideInUp( + child: SizedBox( + height: 50, + width: 200, + child: TextButton( + style: mainBtnStyle(second, Colors.transparent, second), + onPressed: () {}, + child: const Text(cancelRequest, + style: TextStyle( + color: Colors.white, + )), + ), + ), + ) + ], + ), + ], + ), + ); + } +} diff --git a/lib/screens/sos/index.dart b/lib/screens/sos/index.dart new file mode 100644 index 0000000..6fe5db8 --- /dev/null +++ b/lib/screens/sos/index.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:unit2/bloc/sos/sos_bloc.dart'; +import 'package:unit2/screens/sos/components/add_mobile.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; + +class SosScreen extends StatefulWidget { + const SosScreen({super.key}); + + @override + State createState() => _SosScreenState(); +} + +class _SosScreenState extends State { + @override + Widget build(BuildContext context) { + return SafeArea( + child: Scaffold( + resizeToAvoidBottomInset: true, + appBar: AppBar( + title: const Text("SOS"), + centerTitle: true, + backgroundColor: primary, + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocConsumer( + listener: (context, state) { + //// user location loaded + if (state is UserLocationLoaded) { + Navigator.push(context, + MaterialPageRoute(builder: (BuildContext context) { + return AddMobile(); + })); + } + ////loading state + if (state is LoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + }, + builder: (context, state) { + if (state is ErrorState) {} + return Container(); + }, + ), + ), + )); + } +} diff --git a/lib/screens/unit2/login/login.dart b/lib/screens/unit2/login/login.dart index 7e2c7d9..4c4ef35 100644 --- a/lib/screens/unit2/login/login.dart +++ b/lib/screens/unit2/login/login.dart @@ -317,7 +317,7 @@ class _UniT2LoginState extends State { Colors.transparent, Colors.white38), onPressed: () { - + Navigator.pushNamed(context, '/sos'); }, label: const Text( requestSOS, diff --git a/lib/sevices/sos/sos_service.dart b/lib/sevices/sos/sos_service.dart new file mode 100644 index 0000000..40eb9c8 --- /dev/null +++ b/lib/sevices/sos/sos_service.dart @@ -0,0 +1,31 @@ +import 'package:intl/intl.dart'; +import 'package:location/location.dart'; + +class SosService{ + static final SosService _instance = SosService(); + static SosService get instance => _instance; + DateFormat todayFormat = DateFormat("yMdHms"); + Future getUserLocation() async { + Location location = Location(); + LocationData? locationData; + bool serviceEnabled = false; + PermissionStatus permissionGranted; + serviceEnabled = await location.serviceEnabled(); + if (!serviceEnabled) { + serviceEnabled = await location.requestService(); + if (!serviceEnabled) { + return null; + } + } + permissionGranted = await location.hasPermission(); + if (permissionGranted == PermissionStatus.denied) { + permissionGranted = await location.requestPermission(); + if (permissionGranted != PermissionStatus.granted) { + return null; + } + } + LocationData newLocationData = await location.getLocation(); + locationData = newLocationData; + return locationData; + } +} \ No newline at end of file diff --git a/lib/utils/app_router.dart b/lib/utils/app_router.dart index 7e6d33c..3bd227a 100644 --- a/lib/utils/app_router.dart +++ b/lib/utils/app_router.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/sos/sos_bloc.dart'; +import 'package:unit2/screens/sos/index.dart'; import 'package:unit2/screens/unit2/homepage.dart/components/menu.dart'; import 'package:unit2/screens/unit2/login/login.dart'; import 'package:unit2/utils/global_context.dart'; @@ -34,13 +36,21 @@ class AppRouter { return const QRLogin(); }); case '/profile': - ProfileArguments arguments = routeSettings.arguments as ProfileArguments; - BlocProvider.of( + ProfileArguments arguments = + routeSettings.arguments as ProfileArguments; + BlocProvider.of( NavigationService.navigatorKey.currentContext!) - .add(LoadProfile(token:arguments.token ,userID:arguments.userID )); + .add(LoadProfile(token: arguments.token, userID: arguments.userID)); return MaterialPageRoute(builder: (_) { return const ProfileInfo(); }); + case '/sos': + return MaterialPageRoute(builder: (BuildContext context) { + return BlocProvider( + create: (context) => SosBloc()..add(LoadUserLocation()), + child: const SosScreen(), + ); + }); default: return MaterialPageRoute(builder: (context) { return Container(); diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 0000000..3bb1c30 --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,1327 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: "4897882604d919befd350648c7f91926a9d5de99e67b455bf0917cc2362f4bb8" + url: "https://pub.dev" + source: hosted + version: "47.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "690e335554a8385bc9d787117d9eb52c0c03ee207a607e593de3c9d71b1cfe80" + url: "https://pub.dev" + source: hosted + version: "4.7.0" + animate_do: + dependency: "direct main" + description: + name: animate_do + sha256: "9aeacc1a7238f971c039bdf45d13c628be554a242e0251c4ddda09d19a1a923f" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + app_popup_menu: + dependency: "direct main" + description: + name: app_popup_menu + sha256: e05b262b65289431603a84e04e53cb2f3aca6013d3ea61e3f24ddd48d49ef848 + url: "https://pub.dev" + source: hosted + version: "1.0.0" + archive: + dependency: transitive + description: + name: archive + sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a" + url: "https://pub.dev" + source: hosted + version: "3.3.7" + args: + dependency: transitive + description: + name: args + sha256: "4cab82a83ffef80b262ddedf47a0a8e56ee6fbf7fe21e6e768b02792034dd440" + url: "https://pub.dev" + source: hosted + version: "2.4.0" + async: + dependency: transitive + description: + name: async + sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + url: "https://pub.dev" + source: hosted + version: "2.10.0" + auto_size_text: + dependency: "direct main" + description: + name: auto_size_text + sha256: "3f5261cd3fb5f2a9ab4e2fc3fba84fd9fcaac8821f20a1d4e71f557521b22599" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + awesome_dialog: + dependency: "direct main" + description: + name: awesome_dialog + sha256: ac08268b991f228fc6b8880b68ec030d2941bcc8df8b6a6551fb79f2bd36b7da + url: "https://pub.dev" + source: hosted + version: "3.0.2" + azlistview: + dependency: "direct main" + description: + name: azlistview + sha256: "93e865f11777a271b439f0d6b00799c0797e9daeec2e082a2e01373809c4b90d" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + badges: + dependency: "direct main" + description: + name: badges + sha256: d33d4e07197d6e61ddc940640f2b4dc303d3c80887011344940e24d7522f8d26 + url: "https://pub.dev" + source: hosted + version: "3.0.3" + barcode_scan2: + dependency: "direct main" + description: + name: barcode_scan2 + sha256: "0b0625d27841a21e36e896195d86b2aada335e3c486f63647cce701495718e16" + url: "https://pub.dev" + source: hosted + version: "4.2.4" + bloc: + dependency: transitive + description: + name: bloc + sha256: "658a5ae59edcf1e58aac98b000a71c762ad8f46f1394c34a52050cafb3e11a80" + url: "https://pub.dev" + source: hosted + version: "8.1.1" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + build: + dependency: transitive + description: + name: build + sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777" + url: "https://pub.dev" + source: hosted + version: "2.3.1" + build_config: + dependency: transitive + description: + name: build_config + sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + url: "https://pub.dev" + source: hosted + version: "1.1.1" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: "757153e5d9cd88253cb13f28c2fb55a537dc31fefd98137549895b5beb7c6169" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: "687cf90a3951affac1bd5f9ecb5e3e90b60487f3d9cdc359bb310f8876bb02a6" + url: "https://pub.dev" + source: hosted + version: "2.0.10" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727 + url: "https://pub.dev" + source: hosted + version: "2.3.3" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: "14febe0f5bac5ae474117a36099b4de6f1dbc52df6c5e55534b3da9591bf4292" + url: "https://pub.dev" + source: hosted + version: "7.2.7" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: "31b7c748fd4b9adf8d25d72a4c4a59ef119f12876cf414f94f8af5131d5fa2b0" + url: "https://pub.dev" + source: hosted + version: "8.4.4" + cached_network_image: + dependency: "direct main" + description: + name: cached_network_image + sha256: fd3d0dc1d451f9a252b32d95d3f0c3c487bc41a75eba2e6097cb0b9c71491b15 + url: "https://pub.dev" + source: hosted + version: "3.2.3" + cached_network_image_platform_interface: + dependency: transitive + description: + name: cached_network_image_platform_interface + sha256: bb2b8403b4ccdc60ef5f25c70dead1f3d32d24b9d6117cfc087f496b178594a7 + url: "https://pub.dev" + source: hosted + version: "2.0.0" + cached_network_image_web: + dependency: transitive + description: + name: cached_network_image_web + sha256: b8eb814ebfcb4dea049680f8c1ffb2df399e4d03bf7a352c775e26fa06e02fa0 + url: "https://pub.dev" + source: hosted + version: "1.0.2" + characters: + dependency: transitive + description: + name: characters + sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + url: "https://pub.dev" + source: hosted + version: "1.2.1" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: "3d1505d91afa809d177efd4eed5bb0eb65805097a1463abdd2add076effae311" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe" + url: "https://pub.dev" + source: hosted + version: "4.4.0" + collection: + dependency: transitive + description: + name: collection + sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + url: "https://pub.dev" + source: hosted + version: "1.17.0" + convert: + dependency: transitive + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + convex_bottom_bar: + dependency: "direct main" + description: + name: convex_bottom_bar + sha256: ebf0f3deb1e8e99374d844fee7485d2980ec502dedfaad395c12118477933aef + url: "https://pub.dev" + source: hosted + version: "3.2.0" + cool_alert: + dependency: "direct main" + description: + name: cool_alert + sha256: "48a0b6c04914b6dc7e8d7eaccf2adf21bace6533a3eda0aeb412e0d7a03dc00a" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + crypto: + dependency: transitive + description: + name: crypto + sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 + url: "https://pub.dev" + source: hosted + version: "3.0.2" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4" + url: "https://pub.dev" + source: hosted + version: "2.2.4" + date_time_picker: + dependency: "direct main" + description: + name: date_time_picker + sha256: "6923c568bcb67a66ab7e083708d0adbcae8214b41bb84d49febc17e89e06fc4a" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + device_frame: + dependency: transitive + description: + name: device_frame + sha256: afe76182aec178d171953d9b4a50a43c57c7cf3c77d8b09a48bf30c8fa04dd9d + url: "https://pub.dev" + source: hosted + version: "1.1.0" + device_preview: + dependency: "direct main" + description: + name: device_preview + sha256: "2f097bf31b929e15e6756dbe0ec1bcb63952ab9ed51c25dc5a2c722d2b21fdaf" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + dio: + dependency: "direct main" + description: + name: dio + sha256: "7d328c4d898a61efc3cd93655a0955858e29a0aa647f0f9e02d59b3bb275e2e8" + url: "https://pub.dev" + source: hosted + version: "4.0.6" + easy_app_installer: + dependency: "direct main" + description: + name: easy_app_installer + sha256: d7287bf247fe6bc85ad07dfb85804757a6dd2f47e61b0e7ce9195ec7f13e09eb + url: "https://pub.dev" + source: hosted + version: "1.0.0" + equatable: + dependency: "direct main" + description: + name: equatable + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + url: "https://pub.dev" + source: hosted + version: "2.0.5" + expandable_group: + dependency: "direct main" + description: + name: expandable_group + sha256: "874f9c2daef8a21366fb1df85405f80ee8e8be6e3c2ced727c303641b33b9a95" + url: "https://pub.dev" + source: hosted + version: "0.0.8" + 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: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + file: + dependency: transitive + description: + name: file + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" + source: hosted + version: "6.1.4" + file_utils: + dependency: transitive + description: + name: file_utils + sha256: d1e64389a22649095c8405c9e177272caf05139255931c9ff30d53b5c9bcaa34 + url: "https://pub.dev" + source: hosted + version: "1.0.1" + filter_list: + dependency: "direct main" + description: + name: filter_list + sha256: "2d80d6d19beb7847c1176e8bf6fe06d302b23eb7d1bf48c231dd730409ff9b4d" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + flare_flutter: + dependency: transitive + description: + name: flare_flutter + sha256: "99d63c60f00fac81249ce6410ee015d7b125c63d8278a30da81edf3317a1f6a0" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_bloc: + dependency: "direct main" + description: + name: flutter_bloc + sha256: "434951eea948dbe87f737b674281465f610b8259c16c097b8163ce138749a775" + url: "https://pub.dev" + source: hosted + version: "8.1.2" + flutter_blurhash: + dependency: transitive + description: + name: flutter_blurhash + sha256: "05001537bd3fac7644fa6558b09ec8c0a3f2eba78c0765f88912882b1331a5c6" + url: "https://pub.dev" + source: hosted + version: "0.7.0" + flutter_cache_manager: + dependency: transitive + description: + name: flutter_cache_manager + sha256: "32cd900555219333326a2d0653aaaf8671264c29befa65bbd9856d204a4c9fb3" + url: "https://pub.dev" + source: hosted + version: "3.3.0" + flutter_custom_clippers: + dependency: "direct main" + description: + name: flutter_custom_clippers + sha256: "473e3daf61c2a6cee0ad137393259a25223239d519a131c7ec1cac04d06e5407" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + flutter_form_builder: + dependency: "direct main" + description: + name: flutter_form_builder + sha256: "9551c7379adc01a3a3a1100057396407c9534ea8adc937d14a0edd96fcd9e1dc" + url: "https://pub.dev" + source: hosted + version: "7.8.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + url: "https://pub.dev" + source: hosted + version: "2.0.1" + flutter_localizations: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: c224ac897bed083dabf11f238dd11a239809b446740be0c2044608c50029ffdf + url: "https://pub.dev" + source: hosted + version: "2.0.9" + flutter_progress_hud: + dependency: "direct main" + description: + name: flutter_progress_hud + sha256: "19a4889460b7482c5026a936b768996dade4daad8570e8c0493e292d57121dbb" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + flutter_spinkit: + dependency: "direct main" + description: + name: flutter_spinkit + sha256: "77a2117c0517ff909221f3160b8eb20052ab5216107581168af574ac1f05dff8" + url: "https://pub.dev" + source: hosted + version: "5.1.0" + flutter_svg: + dependency: "direct main" + description: + name: flutter_svg + sha256: "6ff9fa12892ae074092de2fa6a9938fb21dbabfdaa2ff57dc697ff912fc8d4b2" + url: "https://pub.dev" + source: hosted + version: "1.1.6" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_zoom_drawer: + dependency: "direct main" + description: + name: flutter_zoom_drawer + sha256: b439c87d63680465582ac301f2502dea6f95ddcba3a9787365dcadb52777c1e1 + url: "https://pub.dev" + source: hosted + version: "3.0.4+1" + fluttericon: + dependency: "direct main" + description: + name: fluttericon + sha256: "252fa8043826e93d972a602497a260cb3d62b5aea6d045793e4381590f2c1e99" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + fluttertoast: + dependency: "direct main" + description: + name: fluttertoast + sha256: "2f9c4d3f4836421f7067a28f8939814597b27614e021da9d63e5d3fb6e212d25" + url: "https://pub.dev" + source: hosted + version: "8.2.1" + form_builder_validators: + dependency: "direct main" + description: + name: form_builder_validators + sha256: d0a940d77231723fcb203ad6f5319ff30cd0c4412a6e74d29d826957b1f9afe0 + url: "https://pub.dev" + source: hosted + version: "8.5.0" + freezed_annotation: + dependency: transitive + description: + name: freezed_annotation + sha256: aeac15850ef1b38ee368d4c53ba9a847e900bb2c53a4db3f6881cbb3cb684338 + url: "https://pub.dev" + source: hosted + version: "2.2.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + glob: + dependency: transitive + description: + name: glob + sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + globbing: + dependency: transitive + description: + name: globbing + sha256: "4f89cfaf6fa74c9c1740a96259da06bd45411ede56744e28017cc534a12b6e2d" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + graphs: + dependency: transitive + description: + name: graphs + sha256: f9e130f3259f52d26f0cfc0e964513796dafed572fa52e45d2f8d6ca14db39b2 + url: "https://pub.dev" + source: hosted + version: "2.2.0" + hive: + dependency: "direct main" + description: + name: hive + sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941" + url: "https://pub.dev" + source: hosted + version: "2.2.3" + hive_flutter: + dependency: "direct main" + description: + name: hive_flutter + sha256: dca1da446b1d808a51689fb5d0c6c9510c0a2ba01e22805d492c73b68e33eecc + url: "https://pub.dev" + source: hosted + version: "1.1.0" + hive_generator: + dependency: "direct dev" + description: + name: hive_generator + sha256: "81fd20125cb2ce8fd23623d7744ffbaf653aae93706c9bd3bf7019ea0ace3938" + url: "https://pub.dev" + source: hosted + version: "1.1.3" + http: + dependency: transitive + description: + name: http + sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482" + url: "https://pub.dev" + source: hosted + version: "0.13.5" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + 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: "8e9d133755c3e84c73288363e6343157c383a0c6c56fc51afcc5d4d7180306d6" + url: "https://pub.dev" + source: hosted + version: "3.3.0" + intl: + dependency: "direct main" + description: + name: intl + sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" + url: "https://pub.dev" + source: hosted + version: "0.17.0" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + js: + dependency: transitive + description: + name: js + sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + url: "https://pub.dev" + source: hosted + version: "0.6.5" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317 + url: "https://pub.dev" + source: hosted + version: "4.8.0" + lints: + dependency: transitive + description: + name: lints + sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + url: "https://pub.dev" + source: hosted + version: "2.0.1" + location: + dependency: "direct main" + description: + name: location + sha256: "9051959f6f2ccadd887b28b66e9cbbcc25b6838e37cf9e894c421ccc0ebf80b5" + url: "https://pub.dev" + source: hosted + version: "4.4.0" + location_platform_interface: + dependency: transitive + description: + name: location_platform_interface + sha256: "62eeaf1658e92e4459b727f55a3c328eccbac8ba043fa6d262ac5286ad48384c" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + location_web: + dependency: transitive + description: + name: location_web + sha256: "6c08c408a040534c0269c4ff9fe17eebb5a36dea16512fbaf116b9c8bc21545b" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + logging: + dependency: transitive + description: + name: logging + sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + lottie: + dependency: transitive + description: + name: lottie + sha256: "893da7a0022ec2fcaa616f34529a081f617e86cc501105b856e5a3184c58c7c2" + url: "https://pub.dev" + source: hosted + version: "1.4.3" + mask_text_input_formatter: + dependency: "direct main" + description: + name: mask_text_input_formatter + sha256: "19bb7809c3c2559277e95521b3ee421e1409eb2cc85efd2feb191696c92490f4" + url: "https://pub.dev" + source: hosted + version: "2.4.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + url: "https://pub.dev" + source: hosted + version: "0.12.13" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + meta: + dependency: transitive + description: + name: meta + sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + url: "https://pub.dev" + source: hosted + version: "1.8.0" + mime: + dependency: transitive + description: + name: mime + sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + url: "https://pub.dev" + source: hosted + version: "1.0.4" + modal_progress_hud_nsn: + dependency: "direct main" + description: + name: modal_progress_hud_nsn + sha256: "408b9bcce97567de94637de932260e50be48db1842edc761aeea61670e5ec30c" + url: "https://pub.dev" + source: hosted + version: "0.3.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + octo_image: + dependency: transitive + description: + name: octo_image + sha256: "107f3ed1330006a3bea63615e81cf637433f5135a52466c7caa0e7152bca9143" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + package_info_plus: + dependency: "direct main" + description: + name: package_info_plus + sha256: cbff87676c352d97116af6dbea05aa28c4d65eb0f6d5677a520c11a69ca9a24d + url: "https://pub.dev" + source: hosted + version: "3.1.0" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6" + url: "https://pub.dev" + source: hosted + version: "2.0.1" + path: + dependency: transitive + description: + name: path + sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + url: "https://pub.dev" + source: hosted + version: "1.8.2" + path_drawing: + dependency: transitive + description: + name: path_drawing + sha256: bbb1934c0cbb03091af082a6389ca2080345291ef07a5fa6d6e078ba8682f977 + url: "https://pub.dev" + source: hosted + version: "1.0.1" + path_parsing: + dependency: transitive + description: + name: path_parsing + sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf + url: "https://pub.dev" + source: hosted + version: "1.0.1" + path_provider: + dependency: "direct main" + description: + name: path_provider + sha256: c7edf82217d4b2952b2129a61d3ad60f1075b9299e629e149a8d2e39c2e6aad4 + url: "https://pub.dev" + source: hosted + version: "2.0.14" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: "019f18c9c10ae370b08dce1f3e3b73bc9f58e7f087bb5e921f06529438ac0ae7" + url: "https://pub.dev" + source: hosted + version: "2.0.24" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "818b2dc38b0f178e0ea3f7cf3b28146faab11375985d815942a68eee11c2d0f7" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: "2ae08f2216225427e64ad224a24354221c2c7907e448e6e0e8b57b1eb9f10ad1" + url: "https://pub.dev" + source: hosted + version: "2.1.10" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec" + url: "https://pub.dev" + source: hosted + version: "2.0.6" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: f53720498d5a543f9607db4b0e997c4b5438884de25b0f73098cc2671a51b130 + url: "https://pub.dev" + source: hosted + version: "2.1.5" + pedantic: + dependency: transitive + description: + name: pedantic + sha256: "67fc27ed9639506c856c840ccce7594d0bdcd91bc8d53d6e52359449a1d50602" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + permission_handler: + dependency: "direct main" + description: + name: permission_handler + sha256: "33c6a1253d1f95fd06fa74b65b7ba907ae9811f9d5c1d3150e51417d04b8d6a8" + url: "https://pub.dev" + source: hosted + version: "10.2.0" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + sha256: "8028362b40c4a45298f1cbfccd227c8dd6caf0e27088a69f2ba2ab15464159e2" + url: "https://pub.dev" + source: hosted + version: "10.2.0" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + sha256: ee96ac32f5a8e6f80756e25b25b9f8e535816c8e6665a96b6d70681f8c4f7e85 + url: "https://pub.dev" + source: hosted + version: "9.0.8" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + sha256: "68abbc472002b5e6dfce47fe9898c6b7d8328d58b5d2524f75e277c07a97eb84" + url: "https://pub.dev" + source: hosted + version: "3.9.0" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + sha256: f67cab14b4328574938ecea2db3475dad7af7ead6afab6338772c5f88963e38b + url: "https://pub.dev" + source: hosted + version: "0.1.2" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: "49392a45ced973e8d94a85fdb21293fbb40ba805fc49f2965101ae748a3683b4" + url: "https://pub.dev" + source: hosted + version: "5.1.0" + platform: + dependency: transitive + description: + name: platform + sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: c3120a968135aead39699267f4c74bc9a08e4e909e86bc1b0af5bfd78691123c + url: "https://pub.dev" + source: hosted + version: "3.7.2" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" + process: + dependency: transitive + description: + name: process + sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + url: "https://pub.dev" + source: hosted + version: "4.2.4" + protobuf: + dependency: transitive + description: + name: protobuf + sha256: "01dd9bd0fa02548bf2ceee13545d4a0ec6046459d847b6b061d8a27237108a08" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + provider: + dependency: transitive + description: + name: provider + sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + url: "https://pub.dev" + source: hosted + version: "6.0.5" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17" + url: "https://pub.dev" + source: hosted + version: "2.1.3" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: ec85d7d55339d85f44ec2b682a82fea340071e8978257e5a43e69f79e98ef50c + url: "https://pub.dev" + source: hosted + version: "1.2.2" + qr: + dependency: transitive + description: + name: qr + sha256: "5c4208b4dc0d55c3184d10d83ee0ded6212dc2b5e2ba17c5a0c0aab279128d21" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + qr_flutter: + dependency: "direct main" + description: + name: qr_flutter + sha256: c5c121c54cb6dd837b9b9d57eb7bc7ec6df4aee741032060c8833a678c80b87e + url: "https://pub.dev" + source: hosted + version: "4.0.0" + rive: + dependency: transitive + description: + name: rive + sha256: "22e3755b75f4ea4492d2fecf4fc2acf1c8d0073df39781d290a20cbfe74c3760" + url: "https://pub.dev" + source: hosted + version: "0.9.1" + rxdart: + dependency: transitive + description: + name: rxdart + sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" + url: "https://pub.dev" + source: hosted + version: "0.27.7" + scrollable_positioned_list: + dependency: transitive + description: + name: scrollable_positioned_list + sha256: "9566352ab9ba05794ee6c8864f154afba5d36c5637d0e3e32c615ba4ceb92772" + url: "https://pub.dev" + source: hosted + version: "0.2.3" + searchfield: + dependency: "direct main" + description: + name: searchfield + sha256: deb363c95b9e64ea9ffd1a3b69926b0fe0344daedab872fc42014755a8199de9 + url: "https://pub.dev" + source: hosted + version: "0.7.5" + shared_preferences: + dependency: transitive + description: + name: shared_preferences + sha256: "858aaa72d8f61637d64e776aca82e1c67e6d9ee07979123c5d17115031c1b13b" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "8304d8a1f7d21a429f91dee552792249362b68a331ac5c3c1caf370f658873f6" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: cf2a42fb20148502022861f71698db12d937c7459345a1bdaa88fc91a91b3603 + url: "https://pub.dev" + source: hosted + version: "2.2.0" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "9d387433ca65717bbf1be88f4d5bb18f10508917a8fa2fb02e0fd0d7479a9afa" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: fb5cf25c0235df2d0640ac1b1174f6466bd311f621574997ac59018a6664548d + url: "https://pub.dev" + source: hosted + version: "2.2.0" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: "74083203a8eae241e0de4a0d597dbedab3b8fef5563f33cf3c12d7e93c655ca5" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "5e588e2efef56916a3b229c3bfe81e6a525665a454519ca51dbcc4236a274173" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + shelf: + dependency: transitive + description: + name: shelf + sha256: c24a96135a2ccd62c64b69315a14adc5c3419df63b4d7c05832a346fdb73682c + url: "https://pub.dev" + source: hosted + version: "1.4.0" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: a988c0e8d8ffbdb8a28aa7ec8e449c260f3deb808781fe1284d22c5bba7156e8 + url: "https://pub.dev" + source: hosted + version: "1.0.3" + signature: + dependency: "direct main" + description: + name: signature + sha256: ad23383717dfa926204695ef6928ff513a77387be1b4a8c685099ce3ec35e5f8 + url: "https://pub.dev" + source: hosted + version: "5.3.0" + simple_chips_input: + dependency: "direct main" + description: + name: simple_chips_input + sha256: "522b2e715fe67f325693e003acfd09fc0b8ab25a2c0c87fb8e5ce5b23a8a2ec1" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: "2d79738b6bbf38a43920e2b8d189e9a3ce6cc201f4b8fc76be5e4fe377b1c38d" + url: "https://pub.dev" + source: hosted + version: "1.2.6" + source_helper: + dependency: transitive + description: + name: source_helper + sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f" + url: "https://pub.dev" + source: hosted + version: "1.3.3" + source_span: + dependency: transitive + description: + name: source_span + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + url: "https://pub.dev" + source: hosted + version: "1.9.1" + sqflite: + dependency: transitive + description: + name: sqflite + sha256: "500d6fec583d2c021f2d25a056d96654f910662c64f836cd2063167b8f1fa758" + url: "https://pub.dev" + source: hosted + version: "2.2.6" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + sha256: "963dad8c4aa2f814ce7d2d5b1da2f36f31bd1a439d8f27e3dc189bb9d26bc684" + url: "https://pub.dev" + source: hosted + version: "2.4.3" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" + source: hosted + version: "1.11.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + 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: "33b31b6beb98100bf9add464a36a8dd03eb10c7a8cf15aeec535e9b054aaf04b" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + system_info2: + dependency: "direct main" + description: + name: system_info2 + sha256: "90621f3ba586e1f268e38cc7951b172cd4d997e43dc1fbed12eb334c8a22a886" + url: "https://pub.dev" + source: hosted + version: "2.0.4" + 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: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + url: "https://pub.dev" + source: hosted + version: "0.4.16" + timing: + dependency: transitive + description: + name: timing + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + toggle_switch: + dependency: "direct main" + description: + name: toggle_switch + sha256: "82c778c4bfe93af154a41e346ccd1d5b0cb6a50f8f187941412edd0a9bbf51ee" + url: "https://pub.dev" + source: hosted + version: "2.0.1" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + uuid: + dependency: transitive + description: + name: uuid + sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" + url: "https://pub.dev" + source: hosted + version: "3.0.7" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + watcher: + dependency: transitive + description: + name: watcher + sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: ca49c0bc209c687b887f30527fb6a9d80040b072cc2990f34b9bec3e7663101b + url: "https://pub.dev" + source: hosted + version: "2.3.0" + win32: + dependency: transitive + description: + name: win32 + sha256: a6f0236dbda0f63aa9a25ad1ff9a9d8a4eaaa5012da0dc59d21afdb1dc361ca4 + url: "https://pub.dev" + source: hosted + version: "3.1.4" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1 + url: "https://pub.dev" + source: hosted + version: "1.0.0" + xml: + dependency: transitive + description: + name: xml + sha256: "979ee37d622dec6365e2efa4d906c37470995871fe9ae080d967e192d88286b5" + url: "https://pub.dev" + source: hosted + version: "6.2.2" + yaml: + dependency: transitive + description: + name: yaml + sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370" + url: "https://pub.dev" + source: hosted + version: "3.1.1" +sdks: + dart: ">2.19.0 <3.0.0" + flutter: ">=3.7.0" From 05427e38871ca0291330978582da7fb89dc33892 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Thu, 13 Apr 2023 16:45:19 +0800 Subject: [PATCH 63/86] Implemented SOS feature --- assets/pngs/emergency.png | Bin 0 -> 163354 bytes ios/Podfile.lock | 22 +- ios/Runner/Info.plist | 9 + lib/bloc/sos/sos_bloc.dart | 96 +++++++- lib/bloc/sos/sos_event.dart | 30 +++ lib/bloc/sos/sos_state.dart | 25 +- lib/bloc/user/user_bloc.dart | 41 ++-- lib/bloc/user/user_event.dart | 6 +- lib/bloc/user/user_state.dart | 4 +- lib/main.dart | 2 +- lib/model/sos/session.dart | 69 ++++++ lib/screens/sos/add_mobile.dart | 4 +- lib/screens/sos/components/acknnowledge.dart | 123 ++++++++++ lib/screens/sos/components/add_mobile.dart | 60 +++-- lib/screens/sos/components/edit_mobile.dart | 86 +++++++ lib/screens/sos/components/request_sos.dart | 215 +++++++++++------- lib/screens/sos/components/sos_received.dart | 204 ++++++++--------- lib/screens/sos/index.dart | 104 +++++++-- lib/screens/sos/request_sos.dart | 109 --------- lib/screens/sos/sos_received.dart | 133 ----------- lib/screens/unit2/basic-info/basic-info.dart | 5 + .../homepage.dart/components/menu-screen.dart | 2 +- .../unit2/homepage.dart/components/menu.dart | 2 + lib/screens/unit2/login/login.dart | 14 +- lib/sevices/sos/sos_service.dart | 96 +++++++- lib/utils/app_router.dart | 2 +- lib/utils/global.dart | 2 +- lib/utils/urls.dart | 11 +- linux/flutter/generated_plugin_registrant.cc | 4 + linux/flutter/generated_plugins.cmake | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 6 + macos/Podfile.lock | 22 +- pubspec.lock | 64 ++++++ pubspec.yaml | 2 + .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 36 files changed, 1067 insertions(+), 512 deletions(-) create mode 100644 assets/pngs/emergency.png create mode 100644 lib/model/sos/session.dart create mode 100644 lib/screens/sos/components/acknnowledge.dart create mode 100644 lib/screens/sos/components/edit_mobile.dart delete mode 100644 lib/screens/sos/request_sos.dart delete mode 100644 lib/screens/sos/sos_received.dart diff --git a/assets/pngs/emergency.png b/assets/pngs/emergency.png new file mode 100644 index 0000000000000000000000000000000000000000..5a1201577c01d70058cf91a2bd8fa4742cf50c66 GIT binary patch literal 163354 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�V%6lst{EdZE2SkF03j&5vUj-sgica&6I1TWkq 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�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 z zvLD=#p&)=Pltuy5Wtttf?;9d#R>Q`VwGHj;bg`P31$?q%XkeUZTt_y_5E>%~3@iS^ ziIcr@B3kXV?+nkS;o|f`>y!RgqIGi+DnuoE9}EitB6+#SQQ2Wx%q9PK-J??1)+WWs zZT#g0d&&O04i-;n0qV0w0Cr-iDOQi+0w5{Jn6W@8XkRY%FS0^ak=}`Pe1sT z(`JvSP-}&ir`~?^^$Lj3>Ms4$rB(2Hn1os^Z@n;m#wt`G?}|1D=`*rB)?%z=ei-VS zg7r15^=1Lp99z3}ZFazZyEMQ4)_bUHmhcSd-}e`odIw|rVYsmKh3>jw48C~gS$XHh z7o-wwfdhqY?EpIkNc#@>C~`G~gmhHNgj!HG!YAc+S3zq$u}x^<;<6>tSV?IlcF)I8 zy#C4d$Lz_m>0JvaX?q(cYLH_IBqJu_%wTWAWQkfb8;ySElGO z#F}PNtVj%mu_X`zm>#UC>I)VKdI+BfAOx!$peRe|yjH)!HII$3lL9wWpJarVh{OOf zkaBKk#+8~%@dsE%fE5G~u{EROebH~QtFjLhPrpI16DXGYm2ZGSTLE!dLR05`&bWN? z%-izS{H2n_U3b#n+sW}K9VBirKWP9WH-;|O7LmfDawPKM63Z};49A2Ev>hJpE(*(2 zPP4=bLnQ>g1ZCJ6+tz){#w&BXuPwc5Pyc=5jgrBAtBxoyD>)j)q6au|odx_psjjY) zhaY)J7R;Y7H{NuE3>-92e(}p+$T{bnBR{|3LJfJ{dFP#S`Q?{`9hxP-{q66hPoHX8 zx@;+g(uYDPHW;b$f0ya6Oz-OX#*?YUs)G+Z=Hk0;WV{47Hoc+b_ZIuOO5#{qO^}Ysh6ETl+S9?*4+*HzeYDEqPE?f2!?2Obs7MVP2yCKpdjfe-o(uMhJi z4Cuf%q%es(F|o7IY9hs2QYi^KeKwCh)4!p1<$jA=TLJ}5^$)!Hz|-&S@sq<>=GxzA z0xHO9Ez)kbYsqc_Gu$ZdWx5reXBh$mOW;Dr7K{#E0286+W*IYdsQmiE-C%||U6!wC zlZkzTa`I^h$l=HLha>b?Vh=Y;#8xT(nlVUip9DGn7)-bTh*7dP02>fCGup(y<`isd z(g8s5f(`&6WD?u41ckX>M@kV}WvG3aji(uT6sNPzR!Uc6vxF8bl9dQirkYwHpJ|gO zge@ED8#@;=wXO8C@yF zQ?`^8B4$NvXoEe{6@R7}IzyWxU{jKKjSfM88pgFlMK>=fOM8=r>*$EhZR}u~7`kBi zO{25uKUd{WQe9ptt*gG2Ca^)V_mHX2FO0Z8EU_fY#BMH{S77aamE( zQB{@YKW}br9>$P*l*Wb_r;Go1v5XxzR<63{D%6RaBsbr5i#+?xRJrs|e*z11iOiid zUw-#ya~OaCm}ZM!X8+uLO9*zt1X4L8Y6H{Gm2stvDc5+pA@ z0&u;z8DN~(g=T#rD2hAQiiz9r@ry$bJ7MN?k3ZghSHbsaPk8{;#lb%1PZoD}>2S9MKOjU5vZIHd zt!+4f&4#*BtP|y~m9)DFhjQX?7DMe1P=Y)yW|_dqZ_n-Q^2<{P%X{yyki+-eM)o^+ z0L+b-NV*ZX0?#od#s&66NUvw8jIF^$p26tAu^M^cO8=x$Y&1P%hAjBvE1CZ_F8SrF#N7b#_NqG6 z%c@7Pi2w;812aI7-~`VGJ~L}f8(>Zls`tU~DU4+u_FW5h2#z_DK>e%*Wu=1Hv^e_r zkv`+bNcF^V5*$AkemrO^TnJIvPj|#n94zbruAngj;6Nh0ae|gEmB@{0>?B<9t4A3j zA|O`Y#<5t6L>oonbp;%qi>0t^1YD#SO04-UI75FfevHo{J66ge6N<3SJOEQ3gU-?r zSMygM59#lcF4kh1WSR{7B)wA2lsHB8IJDW{R!4MpG3NHZ!oJ)#{r+tAz@u-MxZSox zM+_eL%Vex=tes+c3EOE% zYyp7%rI%hR|Gnov`OR;CEpNW@w(Pk5PVnopNn>M+R9E$tOD_JS3?DH{1=0%_EKu;{ zphp$1EC-VTp!K7uABkV7a3UESJbc364mjvXGtNEb*zbiNv?s2(JOJ8&Uj5aDKU(mA zSKRsUTW`4~xN^z-qfk823449bm3{c32dnIMXWh}q9zPUQ>$li_=Ku&PE2&Hp-=|~? zGRf+}O1uoSEZCvtmo98jQk{-C>V%In9*1;CTzS(GO(10b{NB6 zI3Y`2!xJO<13k3@fF#0XE==4Wq-}c@A7FAvrizp7OKV74mMxdiyah7*l~?4;_dk+S zR+ooJ-G;Cv<=b(HU7Ya0sX@k`9c*0&YYJn^s`f^$RI?Tq8%OYEvV6p3AoGk0@z{_W zr8YRY+gWD|V5)KE*RuGvHzb4*S-hxF#_zGK4Eo_7QZRU+l%a@eF+`|ZmW=89U=XSN z$$;+S`SU4XpSkr(+zk7vlOkVJJ4fwj{S#2n7;AhG?%V~{LlFW4vsG6otxe0N7{JSe zHe^tU6-sAQJZ0=lQ}~sfGU_|Ei>2DH6>WvN^vD&Z3M&qKH(s7w^ser__a#>(+R}fg zop<@O*W(-z0l@dblY?Cp3%nAZxn`3R993#am3+Ay?EuNXV4ZUzieO+bSrK>V@vX8*f6P$sWj*cGC$rwqsXg9J%pcC=HN!X4G z@rbYBNir@4Ma@!Jxl+QJCDMorjTD)E8xF9yIV25>0^fbjx^Ag>o-h{Z_35@DcNK_Y>w^AVJx2xqSzxHmgu z(u@k}&9<1dIO13nXmdh>$j%Ak^Imwc6e3hwh1p{W!dW#9A({91b29ntU&%8kpCcb% za=9#f>^ZT24KXqboZ2y|r4u1ZAr-w14$)0OC9UWO>oJ=EFo|m_f$IW+Fuh9HHE140 z;S9Z!onh~hRRfSK1qiD8fdq=jgB3$)EROIAYts?X@ghVR(9Uc&e!kl?+q-lN$umG} zbXP3VR{r06|9#d258aiB#v9xvr2&M#B4B-*)X^CU!3tanglK+AAaw4z=gRf}{HIKx z{)(J-+G(2g^~C?4kZ}MuWQV3qnIfg7CHfiq|M;LeWOf*Z`=Pdw3?DW^{`=qm%4w&Z zrly$;(YpYGP=T9hyv^?P%st?sLvFa?+Dp5XXw#)D;d`=io&nm3fxhh0b7uYS4_Ex< z-n;*uURk^1KwPOoH!?L^Tbe8Gy7SI`@g(}m;YS>`5QkWAWp;-xlPW3*e2XuX7qW&K z(NsL2%9z8r?D4}Lm%seV2M@KR;`6I&DlYK2>^r(LnFQ)0kL-oX?v>=cr5r3I8{;{W)v1s=IA{4IqnIQ>8l!4a^TiZK@Sw0b3`491ds) zwL><>?gel~*3Fu(;Hs5jc@yk|YaD9|%#XA|3x>VB0LnZF14hFkWMko&2#r<*3MJ)s zN_csLtaxggG`{+dEE?2b229#b$`9E~+#?341G57_C4=;Hg9|hg`zZ^e=>Qc?NY+<2 zj6hKPL>Vb+jWkzFfwaSpG9kqiCFrVv6~ioPtc?Pg0%!qTF|FUpa4R)j#6k?mb+5Ni zS1zYK1C-TUx86O)`PkzR?)&!iXHK3u^KJLamEX!aXPzyCYKGw+A5zwa0ERw3Tp|$w zc3gWE6%}&u!3Rqv^k@`6H#9WB7ibW|U;jmV{9kmX7@8q5S~xT|HtMznL3~*9;yPyq zASBhu=rN<^|Byw-_rMUfq|&zVq`mk3%`SUPnsx7e|LMxu=C@r-599&RMqUW6L5o0mqEbnXliI2df$KVJ=5uQ)xPxFM{n*oX$RKsTI*t0&O6mw zyK+Tqupk)o`+OVYIkS;xv}JvK-+gv!n*7omudWI;EgRCm&qcwKvZGr*{5&)5j(ZDj zbuCimFBH^ulS1f!su1$wB!dzgI%%&d$sxV0PBI#^;^bw7Sf~ebsqhfKLHIqHju%H7 zn}?-Z+K_U!ZUDroFl+6xj%8Z50w631A~9%ZfFNpa*ls?hQZS706jQ@9Db(=-{SYi2 zE53^DP*$oBK7sUilh0CdFL%b!89G3Xm&}Mvf+QJN4b=cpafO)KXO^y@o+Sn!ly9D$ zCSSk&x`07xg{zD&(FGs`fM_=82_g*Hcp?dto7 zHvxqBGs74d!&__gq>4q^TkqiX)anaZ_{!gh@QNRJaLj z3z$7HgANGhlMq=qe^w`NefE*0P|9G~{(H)ZqYjs%i4#Qp0MK+22e4M5JwC4@aDo-9 zEfy%`3yKCgCeUH=SFm&TB=lzhT^^TT3NSXIrmp~^a9x4@0UTGht-X_boHAVJrI-zE zd+d7MQg%Fd{+y!Mr@wH#zc`2zU(Nk)zx@uGH)oEVcg8Q|hkNX$*BV(Lf*4qzC>Saz zC{SQTR*0o9Zo26vl*4G&XDXPw%GFn24bk&hWsnHk`T6#;7zD!T9PW$zBr|mQkw?mq z;UnaVE3az$}Thkd2&8N&njEjDG?|<*P zJ)MFU1u4`nkITDq`I7Dad-s2SQC3>A;^pZde7^7AJFPXgj@WYggAYIG_rzn5?Y_sJ zlNym4X+2PPL+jSQi|1Z?H&9t!UI46SZ)s_bH8wUi9ewbgTciN;sQrEzedtA0_)I1K z9A3EemCq+XecCr)&D?HWVMQ?~DNd{b2x6Vc++e*cXohfu`R**Ele#q#>2EsA>ojM_ zEX`LGIHfZ_O#EdlSJu+Fk|vdFU6fU#metrz4=&n5Js9DEk=H~Fjp1Z1**JwPtj`7T zWB0*RrUd4FDCbZ%9?Z`mY#qSXR1UE^xI>fDA3PL8YAdObp?JHfm;q&}#)f&{6nM zx6BWhZm>^u>;^c9(Z>hB)6qG_T1Rw?X2TH9;;HF4nXhRV8(A@ihK8&W(v!JO%7<_u z%1;EfJFqW+pcK;R;~|(pwxPZmz)=DsfZWP`s}uw zOgQc+aZZ3f3ZXXklbwJPyNp7Y;ewzZ-=PjP-41z)o(X`cS;F~JBFK~Q)QG=N1b{D& zqN>RJ!Z{U!HBKinpCRR+44pwI_2>oDaV?r-ZImBDvLaGXH^h&o2QTi;w(EF2mTjxfqV0%S)zQ-^cx21Ls55r)r@i2nWG4~flHsc;YoCJ+tu>Q#>C*Rh$|A3c& zasE&9em~uGRpbFsH+AIQcU}Mfsb~E1%m?nh_YS144o1kN2w6z}RkbU3xcvVvyZV}I zuQ?ru`cCH1^DnJvIpZ&Se)2OfB^~Z`<=`RqhLw$D zi){9aKv|)NP+A~7MAC)$!7wBd0@F+zvv&+Jt;K8@FB$c`08?V7fF#H9oLyKe*EpS4G=$0TS9e}M&0f68R;w{?^ zlW{4wQ5!{kilHk8bc}Q-mGttnmdaV(i~&Tv^qgjxip&M1!qEAo5e}%G^0IvO!2hIe z{%mMrLNbbibYwKyT3e9lg!a(A*$*uim^gb8z*r%QNZ@@RKp6pI6dpV&P)})00x2>w zI6Mna0265Fj^{^p6QPGr);e(5-ULJJVZ$Sk%$;GZ7()enrje;PBSL8|OkxhOF@Z!t z0@yLxp)mAE)Lo$FA2QxrA>4LKKeXKuM^@O^OQd1e6Y|bG07CoiAp?&;Ok5)eNGQ?> zQV47nCNJXXG!l~S0E)B;u*|28YbVRsh(Hb|l6Kg9**pWlydeaLA&NH5m9{wchj%=1 zeNLnFg%L*Epi5yRKLqrH;%wbfq;n%1(kniG(^Y?L+5gBBZg}IBsiUH?)}5Vhn=c9R z7M+`a_xs<;+_`h*!V51n!mhZ+844r&!%$dJQIUe2Fl-I@I^XBt)cvek_JXe&wg&hf z(%wN$JgK3FB?m4Nn=8{aeB|i*gAPkQUEkP5L)`q*O+AnoL3dL}ayR$hb;F0po%G}L zaSvaJ+i+VNg*Q{lczJ7E)A(zzzVghw?tB1lX&uYdX>U5Gzx=}RyKcYj7Y(acY=dG; z?&XW;?|aXG?mY2tmtPO--R@srfA7;_Lx%SMd1YDf7e2TB&{QJ1o5$@qa$rsAb;E}Z zIpwW)zZ%s2N8NOLJm{#yRt%W1&9vb^+~dkj-#%})d0lf7K35`*Fh<4?p)i<*2*!n; zFWISC{3387)>>LwvkzGdt^^qhD8Npr#sv~tM=Ir?dhfJ#irlk^A45FSlM7Sesrv!= zVL~^BwK|i+NjT#y0%$1)ds88{Ac~k)?hG(90&mkb8g>Af^j%uuAp(FA!nrX+1+Yj( zDcA>V6hTL02l{h?vG4&b72_Cw^uw}`KauBu{Tq1`*)KM@*;d7(Qi+M(1yGTRw5qDt zQHac^zJ*9IE0cm@)e;y~DfaT9BrqlfK4G-G4dJ>r>^p>GM$pe80MsaeMT)ElLRAjv zmHc3_3gD6)1dHWIYkf#P^MYCNL$Dn%S8fx4laCKS35ZxOg8(lMpQi{wTLhY$2-M06 zA4a^?C!RjVQh@dp4zAGOZO|lyr`%u1vR_CyJ!NmSjAwyU}FF# zC_(m1k=!O)l=TEiG^_+106-o9pv?lmHqjY(=`b%&d35$EKRf$^($cEQ$+k?G1>wln zgiu8Jh8u5?^Ugm<5Fb-eLy*JFFfu&^J_JS#W$D9-0`!ib*^RJDU?{`P`x04Oy=Iuv zhzGaUw)sn!E&F#{Bvya^X~(WpO3|eg%WrISalRkAw+9}%@8)-oIQrQ0aWOAN47VON z!w~m(m&}~?#re0~eB-{4J~|mW*v2yT`DaGofB%1fUAtn*UT%-80FTP>VYB&{ES!DX zV~;+x^DVbO(7tVVwDRqbW{e&`e$=HUMS&l|#(D^-i6EYp{dnrA#C>pdMMdC(Q6mSR z`TmEq26xBtHrGD(IR23Of-$3CAFV1MJ0yC0AIS?3Yy8$cQ01x0b zv=3PPGsU!|08SBCVghWA2TVyZ1U#;V%jDx5Zk4Am{Eald^S)GK+cJOvCmf7ZFp`XC z5H5mt*j^cw;PAfU8(J-yzL@BPD9DO!qey?Jp<#*wW<3@|UNm{*SisZT>4!dtdKsS- zp#4SI8A_W;x*5z}ld&cnx#D={-`4B=nQD%r!~}|pB0T4fX2c6d#{(ec=J-M@M4@yv ziTyB9s{qHSE|-%20B3zG&}NhXDR2NNK?aRbl^=kqIE^wDAAKYrTz`|i|JN%dj`KGt z9MlJc#!UGtfJBA|nejllOzAkxI#nyB_<~tqyy(&}KFm}C91aw3`FJTTAB}6O5W;kP z3M>KHvQ4&V2#ib`!9A;w*nOC^H(>)d*_m$ef$q5R+L>pcebL3+?zAf&ZavF9USGXo zZXxp2QpqAhi=2l0W2jzj{}-Lf~Q0>Icxcfm5$gLKL7M;ykGaPTOa-4=jWZU zQASt)d-@go&|!nd4H{H)so&?>7pGjpTO94;JiOsD43f;?=}!h17yC{gIG|trwb#D9 zZ_=c3^LwCsHpwn`J$9eyyHnqrUD3yPPs1$NH;d-XKd~Vc+P=i??H4ErxYH2EQoPH> zdfcTJowN`Eh2bo1Ob+Cum7!)RjFTCK&`_>|+72NUvZj_g`~3JR&A9K`lO|KcmFy2M z6+=T$L3bQ*CI)uI73_yp?6G1i8Vc}-3^3SJF| zLrX)Evo8R$3p$$cs~^hSx7;R8UwtJ55LOI84C-@404xCpkfP3X`CvtrxXXa)!Sb{a zc)AdBq9TR@bo9FyXuyYZ6)gJ6iG%)38Qk~@EkV#}>5B}_6vN z4vz)~dHq1uW56Jd(Gk`08p8QfE`?5Y0Nf$A0cH(4DrkaWhbIx#0ds<7qky}|8N=^f zki1mWAT?8{64SM+8iTMX(+P&2(I#+-RE;YR(?Y> z`5@mB-P!}E{P?i=y$?)zx3#J9jOnkwd>b-p`Xt~Fl+N5fyjC^pG#A^OL$9+plZ{mV=JCl4PwF#7hJGauV+kI`E!2-@Y~ zA8;00KI`#^pLZ9ReY^CVSqHSNYB(Iu!sFl#1OW{Cfvk^)-wn{C-Ylf6bu>&=Ksk)5 z;xYVzY%x67humQJcK`97>$EuF$FMOUGI}(mU}dT)hteLdBmV$rV+4dUE|j+@A1;pK zp_q>g0Di!l*uu~=G205wV@j5h)}#yTb|9Iafg{S0Rru>?VNqPC3m%&+pWlAJ1mN1- zAGNvC2y=PB>cqf6MZp$%3IeDhRxEZ@sZImX$51qkp{EoYNtTUZhenldbhb-#>-nS$ z0X6=Pv__=4rA?yv37cIQA_BaiO1+bn&>>XLK)^+xAwGO-g>2oW^^AcW9@e(|HtE|f)KT1^C*YAhy_F2E52Wl{{*s(D#0 zN{O^W%x#wvf&-+>&G~koym-y^vdiKnGUlXX#ZghFZr2L(4A~c?K=xLNuXr$ouMG&DECKU{b0(ulZj8)~+A{FFN+C7Rn^Z;=o^D=T-(%MXF$h+aY%*2T`6JMt+&ozy|Z7OcXH^idnUgp!R1x6Lu3IPnzhx&Cp*MxjhmroixT}~0TOM9 zL}Tx-sI9$gRei(K3r;^_6UIe*^)Pg6Y zB{S(71RKiInM82@yxF69jZ^lItTAKqMW`%_38npul1}@Q>Cm>He)!7$x858&>%NDd zc>k1>j_J~)+4Pq`^;TAuOc*h;|20VVnt=OeF$+gh+rikfog!B*W+?$>g;*5SVH?_~ zZ}423qr(3B>+e3a=bpPP?4fSjB)c7X+)0sFo_gV{ih|0z=9%AqzG~*I^K0CmodJCM z;MpB47(+!#X~tyL7|@ca2$!T$?f{c0CSD&)O29}jju3_0ACP!D(+p;1dBK4GbJuqQ zxV7o5h53e&wVbA?4e1aE;yBJISRAr>E{692a2#G}oysL{A1gy zhPlB0(X20S)^;EULZ~N96GBeFXb1vh5=7QkA%xCSyIj7y`$75Uv1g^IF(PHiQUQMg zEZ~NQCn+HSlVJZUDJm}khBr_p#mEi-7;Iz866lwn(Kq?uc&IW4> z+Fu7Dw6hh}+`$T!f#pHi0vVhN%$S9i!FhSB$|MZNDAd#}ZRjT`b!3~APa5YemrwqF ztt8-VJ>mQ_#4`Y)Mb_@}7V7s4l|CSWLXlQODXsc3>N?Um7enP?I0-pWKTJw?hNh{n z#Fl)4%(PF@*hO04*5l$?qdjrdWJ`iIN|QOl$vR-1hxawq32}8Ui-vZ!T#33PwX>bt z5;|=&0OoYrLZMiGvRy}qp7GP8h-km~)x}r-`N4bd`FiE@W#>Q}SBg7%QA2Z++0)S$hxkFg5@E?)EdzJfZoNQ%-po#qRzU3=}SpC1Mcx zr+K$B;!aBA^ZR53Ta3jFyx?`r86YasgFkxzt^4o&@0~k6{J*DiPLH4V+=u>}zE!)7 z95LWus5!I)2D(^z4f?j|1Y$iku6_v^DbRc@%nU3qFFa-NkecJBzw&C$mOhgE9C1Kq z(wRSAQ8sM!M>S){Umtb*-b}cC^W%0$x(VSh)YgGv9JS6~0RR9=L_t(q zvEAWBwwFDKFkKnEenyKUFa+aSMsH~5ZJC!J%YmqcpMlJ9MMg21#&IJ?E)s?x{1HMm zb^xUG%=z-^<=4m;k324g5O)SJnWs}>@EXwI0C#!I3#D>sKXI1?Au={n*JJ3<1Xvsb zpdg@<50N=97#%N)X=Or5XKfrGR^c=;d5KC90rdY9GVl;U~k=AN}ox#Y|`Sgh=#^hl#q8*7 z3fv8sNu1+sjSNak>xiU!G-t7^E$*G$|MQAJEc(^&F1>xnU4M82 zP~%4s>o>suB<%$A^ZKi=%g;{#nLPE(6r{u#;J$+I9UiEt>CucvAR%UZ>8;P($G_xtdm6K|i*h+E zGhl3Xrw;`iD_(zP`h5>R@W5_QKJ#*?tlhNdJ}MkFYUlxjhgMySC+G3_UacXfUiWNl zg?g9@i?q#=)z#HM9WZdvzAsFB7b!zq{<8adCpPt&F#hARv7@eR@p+zHmP~w>DlBex z6qjOBcStFWZYn}xA)+?%GX#Stcstk>J2N}Hp5`WxduFmv<@3Ig!=BpZdu=r8_?;YhnK(!(1NphJ+PbD)v+j+HgM z08lXf7T~G003s*qsZ#(2AKiMJeD>1I$aumrpbZLvVRBReWCg)rlq2L-T`EPzg($X8 zH)BlceA30y=%Vbz%1{)b23(jUvTD&XSq^i=mX!?%BF4l6n#7MuA&AL=p9e!K;S_>& zh>wMUKwG^jY4RCs8}vm9AXKi&&erTYC+q8c;y)_|8Epwe`_s6xPF5l8Rkw7dM3Lg3 zK{lG->5-c9YUx)|ErrPR5imxzsTTDz0yiU{!}0oM2&9|d`4Hd-I09NJC+nI4!z9(v z5$DD*vzaGPD25vE+DB?FP3(bmX?JBG@gp2&b5x_OP9eq;d*@CFgzT6Lp|#S1;i7C# zr?-QyUV1%$y>_)ly>$EE|MKMK58r<7!$Xfe`fRU1_z3Fk)uCQS5}|QfFmJJ3{D({B zs;jSppHHilmK8(a8AE6c4*)E>oMNUWO%Pe*4&NIjT7ZA!-R6t>RrR;8SW)}VS;rrh zuL#h?W8dxHo*r)R7PtF3b7ogOH+AZyrsl@}v^b}ETCP4DO5Nir(*qYQq*Z#RKKsnQ z!GhwSOnLtG_l`Vh?WToqzV=ngz=3@a?^{#+TQEo?pd00Vb}h^LL6DigX>&Q9!v_r< za8W$wXnJAVM{gc*;0{}SCc?N=4-d`w-;19og5J7Sb7sH2qQ2ooS2(e4VbamJDq#yy z4bG=@WipuF>Vy$TD!p=LJp7HTy6=AwN$wAv^`tN`(iH2KHC>EL_7_ix5pyOvR zlQ*vYr!0Q;IjJcDz(J@m8b)E$fK9>xB*AJJD#Fhv&DUcPRijKb!>Y`5V!=p8Y7!6_ zN5a(ggb?bAU`35B00R$&&j{IRSWJlr%HIHNpfe%^#gf-_osN?MB1XVi1i)?uneCqg z@uBub8uZF-2q?^UqAgB3WixdiMv1YOu*3k`JOQNM<4L}xq(sUBCE~4g01P)tsJ=m( zTN~7E+>g#&I{htqy{=VuJNpbN-+v!~QuHBGqtjp(DNiR2$ig2g3gbB_`Adxm+exn^ z8aW~xukW@JT^N!!6p1Z_i=)#C&_fo8;78To1VknPBB08pLvpxS*(`I-ZR`A2+5=M_ zyKC;57ySMoPfdC9)A|)le}_^zeI0IZprO7={)w7kix)4I^Uph9`u87*dcaNUL&j8k zO|GCOOeGM^OLfd{ZH+wi<(J<~Ipeq^n)2P-^F5FUKt0&UxBdHpe%Jl;nxnt`>T{%? z7niiQwE+C&F5}Tm0^B6&DPecGT{Ph#hzV^v6hfZSs$@XRHv=53H zva0D{^hZQ6sxg4l9FcwLxy~?&v~aT$+3|+Ow~E=X+4{z5mrg3p*64-04%_0&{Zw2LRNMk z4*CHp*PL=OPZIc0(rkFz8uh&lZH(4Fmm-LR)6EUyaKoc#<~(`z&;O97uV>0IfFd_0 z@_N+rauva~8KJH?GR15aaFvE|EQNIPBvuceVp$ASGwHxyCc&sgnoxHP6L}av-C#Qc z00S%P1eFOC7{sC`h=1oY-}9PJ)1f&>0HiL=tougS zUaZUxOHii-*Jz$c3i>?$PG??bkVQ)TQdaDh+Ih<)vZ_`p3d&(CR4BEx=E@s4-YokU z`Xspf4hUm0Cl7icJcm%Yt@H}d$@Hn@>4W7}JxG!TQQ=?YaQ71z3a9$OxFyrGk)c_O zvw&#b!ATPzpqPZ=OD(v{4xvt{+b*@-5aY&Qwp-O6?!5V`WmjB(=cA82^uX-TKYIUW z4BQZh%j+tc%AC(0Q7s{WZ3)*(uZ7`m6eP&1ukeJ#ZfNzvgS`tYlrY&5!>`{Q9 z&O+;3Ro`vSlgR_19_sy@ZoRMnEw|iw=$Bu8{8yhR7z~A5;m}X%kMWe7%VN0u@=HSD zC?^KHyRD^h;)D17_cmnHob}4vpMJK_Zrf^%_=VR#DcNq^u(OH_fCfOQs*Vj5>o)>1$DdQpurWtM!N5nQ>VT2{=oeA#yPkeL-1_9xw{Ci5 z`SP#6daI$W`3LrNq8}2k-H3?A6D~(>(&wF1GHCG3vY{gu?|k(B+&=I|4*&*lA)TEfX%?7uJCK?Vk#T~?loSv1YRBvJqo!96x7X{heYHUY zlwr=^5RjoOsjSjjtd4Wf(*5IK^R(~5^+{T%7?dV z`uCAYx?YmdFu`y@$`SrpHg~=}d({=Puj@BbzQ+!59mn&6yQ~AEbevE-K4X~@!~2@S zLi{TkBLxWaH7;ub>(+>(u|D7)7RzKAR^CP^US}2=+qyvsqFuhu@*h}#4_tiFnXT8| zbnm+_R`vby8`EFDA`))d9T2`CSXgTN^s~?8^fOMEU;gR>IpKs8z|eXT;si9N*d8gF z0KuO%G}Pa@blHl$)AJ_0(DDGN2Ri%8YyLg>iogHulx552oe!UgU@{eRB@m0!RMW0J zsVM*=Gj3|1Rj6oprxLM#k3RgsU-8|!^7&WZnQccV&iFCIPA)GiI0w6@+q6;E5+A0o zi1EAiV8?x#Gs{}e-AIXDU2Dyc#uJ$UK+w)3M-BW-GM&13%CoP2a^#_VZ8ZYD-7h?m z^>tQ!`uOuRB9T^qI2H!RbYy&iK-}i_hj%{wzzr!rxJdvIjdxNGKgx(tKbmaAa0K>Ffpwj{XQ4-F}d;(=f@=Su-66lA%o-H5VdWS50{dE~u1jpj0 zdN3BKU4{9%s1Wx4)euud_Ywx<16~Bc2N71**}~1-4@M%5`d@8L4O$X_Aul^Xh!6fa z4uBpTR^>s+Mv=t8^~{2xafSeR(JJ5uOM=h1aZacVVN|7OCOF<{@z|spfGHQBV(#As zK2br71ZyrvE`mZsl}fWE7SvLE2!bQ*D$)kAbOb;o6GWY-LR6IxBJ<4# z=juiYBjX1g8X{C4`4Yx>@7#5}OoALMu>B5Tl<3dI8f6H1Kog}#-E3Nx;-&+xG1Ciu zUpQ2}t;?jju0i>*q%EfD@zh~iZ8gxXHNI>4LU-+@d!#e|_Pp6ll#j`Sjmh z^!#(ro!wBk{FrpcQE7L&eXHu~<%KW}WZUu||85WD0Z=zz75}*A_R^bg{@1q4mMu6Ps2-j>DJEH~&rUST+kNhf z1JpQVl6ot4;t3HJ9r`;GHG<8JwR=2p-#xDCK2;A~c)_^`0l18UiDJ1+gI=92U1zyn z@L)mPQ9M;f4<1x=0YKEXk3ap&jN=a9XRCVr-1fMGXf@wmcJrGA05N35au*qtws02= zfeRs4DZ!+R$YS&n8oq)?>2yzT2 z$M{tzesVq$fF}n$c-YJ+Ya)U9@q&$^kXkiL1Ro{{qA1-)sw;=fm&c*sPZ$P=tlG_j zlUxi>mhUb`inx)c(2ghgl71-b`N0H7&9#j%X^cuiag|gT0~kW6lmrvh7)1#LxM7c| zDVGmlcnz6^DLL?pE5$!32rC7nG=eTX5bwUkFJ_;#VI^B|9Io|k3I6>oaIaAo}IQi2I3o7 zj2e;h$i0ur(&fwKx4*quwwbVF%Zg?7_kI2K><`X3`KYe2%lU5Zh91aY?A_3b*}Hh_ z%{S9a7A~lS!69!_F8VwqNbmDzzLpB)ogJR{Nv2H3AewVS7)q)h#fm7jaPPj`u0xMG z>hOOQ6!;JKdEMi{SmanbW5XWwn97L>q`auWv;TmBeb1?`svJ4x`8V^|(7Fdvc?EOr zY^XQ*{WO@Dc%lS`ej~+QjL;WqeldWioR0d*IXUSu*=BIE^!WJi%vH9n27Dhva(a7F-yR#7BfA`KyF zTG=RV4XrRoOelNfVLCev6}6ZtI|6zk03xQP$AP3108Va~A9^6vXM?q54DE{JdlLeN zZ^E_S=ypm2+?%a6iSrD^oDz%}YaqJ%@7aI*kuRTsuaKF|s6W@m{&au{gMm_q3Nv-y zU}dZ{dgFE0iwmaU=YmokOHdMHVQnp>Rz4{mJ^)i3zJkCw2|VqO^aZ5_g;d|Z<_1Y2 z-CrG^0gwctLr_JU9i{?r6VS1CJXZ?RGtiD8FJ7EQ!z6%^W-?ul`#>Syg?>ua?7Xzh zH3vZ0g^MeTlj_2!@|%9A2d??YFP8k`qKhBecKbapv}c@O;>#FvB9!SwDEZBo-;(ps zIe+nU&%JQV{P{~KClYz5=gn~m$Z&jG`R4n~nB8tT(fiwu#qX5ApWa?rJI z%L$Bb)iCVBlRx%c8VvgO8$Ej1nZ+f@+08HO>H$;2+a=V3T4o6$~*1MnjzhN>CtM3!h*7)Kr-<`C0h<#HE~}sCXEz&M1)@ zgeIq^LXyb#LjD#i$}?Ra#Z56)yI@x6Low1QJaksU%O~6z#)XLDq==XKVE*R8b`0Or z<(a`;W^yHIXoxkAhXMXg?;hKm|hHwg8cw{9)T}u$Yn~$QUb5VBhm*MUV{MGWkF{n0+FaEavzM1gY>VJ`Xy3Ej7yW#R@1@&^XtR#0e~9_nL}ejjsEgy()mSwXqn zKat;Zy*==&3yx|&<+QWkKlG@R&h{6SK9|ne7u((LR$obB-Qu|mypKOV`74(@Pu2H%PKGBC zf@*4O#P3cy`>b>2SHHeUMhqW{iUIIT2{r3Y-ljDSt?!Y=^6;mjZOAEhu+ILVPX&Ei zz$c*n5J(R!D)1gTWXRyGB`c@878Z>%COL)yNDhnrc<)O6H2)_f^ zghfx?XqpEC>NbD@YS~7PA1@`Nhe};6q$bt6D>D?8V#XOWBRlI&Y|r!Xzw@Ouqjpye zp*8A$2!a?6Ti0TkM0PCN&NcKkTIHQ)qA+cAFf5Fxd73jWgN49~6;Y|F?jwbj<#6Uk ze<3Bl6yonb5RQNT;>%Jsc(4pP{9sg^2OwiIgc)!{%fO}KKzu+#8`CUP|-1@0YTb+N_LHq)Keg3a6`|U#y z-gR1I?UEBPxVByQIpE5l{p`%Ak2!Rgts}d#_0e0!!OF=I+A3EVV=t_-A9G3bi_`xI*iV;s=ah6ZSz*HNo z0Sk&VT>z%|P zj&4bCMv;sZeP@ScGBW=6-Xp0v&3;>J*fmpQAyM`b!={c~b;UnT;QBxZ16+!zm0)~gfaD_l4qHe>O%6aLi z*9;L4j1U>l0dj?o7%!nBy8~L3wv^0x;6X`#^s!XIhBOWW1{v!^o+V^ z04fQ_SaCF5pI|a2l9YG@{C#Tc#EUSO4a|)K9ZknIoO(txE{+ z#cff%1}zV~ff`UkA_T2b35t_)qqwq3U_n86sOfQsrn{5NhhM6=L<@q_h?KWVlqVoV z<-tA~5=;X1#2Ly}3jhkOiLj84Ol{3N7R!6c`WfSJO1w%U(f@u#0R z;N-Z<^k*L3YR_{^IkE1e2)2|n-h}Nu|BRDcPe0>~w+4+GeHkiJBd;1J`5LA&7fu?= zdK)59()6qhZOYPiHDcds)20<|!Yus_J;g4W07uYxB} zKWs<$93$o3$PTio4GLzt5)>HS#Q=;K)XU0u-g5bm0X)IzuK?El zPGo|GA-<1C;bY8NM%)K}HrCfm-Ktdzz!eZEQ^DOE*S7i6I>$!0H7ORdQHD0jn!=5< zXPa^Lqcx^5xxJp3`M34@T7Me7pgl%ikJ%O3$Ibo{ZpdRo_BrbJKmjNIhcyxkOJhTm z6ciRp5UKKU03DV=C`GA@Z{B!EK7Hs3EtNsu2L;#x^b9eyVGlrCf~=xB?O$g+Kf#4M zPyn9J1~Ixm6KzP#JN?S!=Y&qXD8Iqh(gXR6y@yBgqI1t|IOpv1rc@6aa&t0~Zd|kW zsXjUL<_YSX`UEmR{M`-0NT)KqdCfJ~$R(FtqK?o^qh&TrZ&@fTMVsJuB16i`$}S!; zV)%Zqz5ae_Z)smIXh&ywU@cALN5~XHI?%qFMKo1};)AA!1vHBwRPGOz8Lgj;#Q+ut ztw=VZqt=(1R?hq|OtJ|SIQ{IA$zp44lL~(Ue0)&a6ikH&>`QS)8T3RROxzUgVw%Rv z(iSO%kLpk-s}xfS#waE0d|H3R=c;q z70dO4?@Yc4`5xFLJ&?cHH$gZ5;i3zcopsi^cUDwXKZ*Ms@<*&2m-RGD$7V7=hCMoe zQn(om2IOD=`j?z{-g){y*`J066p>s{35!i|I_-zB%;oq3ZuTBz*o+uFc*xa-g~9$$ zJpFne1esAFYYywu*f8S`;Kx`wD{>{Up=1_WUa0To^z}h%I-Ot zoCiG)US>V5B;sM@R2&SYLBPED#n)xd^tYuDzXvRhj@KprQiRk>id3}B(5oAvDGx$i zEEUnx*d&ce?@mKg<8!m%Dq}QU4cQ1YroPXMWg=?m`BM5_O+xo)4|pVjjJ5n#BE{T|5f=*j#(wum0c1E5VbO#gSuZx@|$*7?_ayoH})nqpaJ^YNVF2}o3_ zr7&HX2`t8r9W8g?{U15=%rmux)=jmvv~-FT`$OBOyn1aCc>C=6e zT=Iv1J^aW6`#tl*JNa-}hn_a);jscZm@)xP$o5h2YKFj|`AFN~`kcmO=tJnsI{-&0 zHYP|zfrCvAc7|C-Og~{E3z`?$A)KYSHumK=GV95y(iiFP)vPogg|OC#)b&Et5d&Kh zqlg(m99GyyrWV3$^(*V8enp+`o93J`=ye#XF|K1ATS1TkgubtfRXT>>haf7(^ZU^V z(C9!YF%5938Y&dX90==x#*aRi&!;}GWiA*BQ@3|^ou%8-KQL|>cWRZ4J_g|NREon> z1)m@oT{?rR8|vmIKPPl^hvs+JdwU>%v2Vh$x%{%&=bnc{F88_v4K`+h;Kpa? zQ@wfwv3hg@;Af07KgS$%tcD*frp1RkLj)Xama5oPr@(gb$<}hywW`{)jcnID@4N$x z_dirmd;Xc-r#|(>B`?18{Dc=?{UAqk#!Yn=oBTLk)&MDpQCX{HbZ2G5*y*;0aRE+;fT z4hUGH>9_>T!A^j!h$|amRHbr3dE8dtjJjSSWKfYoqGXv6h?!j`;ITqqJ)=q4@>kDPSUN%G-`A96GMu#rP+4m@a|p9~yaGw_)g-(a%y_i?fGKV6Pe zPzPc#EY`}afwl-_b&w|%sDLPzY3~3(T0LE7O>^=zv_J}}b%F$NO@vuOMcQW1h4}eB zDZ#$o35uIx&A`bSUO#r!_+sG}3Wo`FTqsjvhcRLc1igs4Ap;OBB|sC~=0ZZFik`8Z z77Qh1`~H$YM(>*%Abw97vbI1_1Poh}Zzx+!ts(NOW=C#k{x#1QEs~Rrn3F_-t)&^ulp}m)#c+}cIEn?`X+OHV) zvBtzy)0L(G9>$1bD-~z38lvc86q2PbtXuoLd-v9@P0Q~r-viy-19G)q1*p z;DHC^^wUq5y1F`LsV42Um)v^Wt@6X2ccvj;prWi~5*(|4=k_>9O`iH{w?D4-j^63! zRw#f0++c|dfFMKQ%qr+)dceMTgW^O=wAd(y21kG?i_nxQ?Cp~$e&we)?b^uj0JNs& zEs&WnzY6vN^}T2mgp?!5QBZOU^@$2$?B|47+BDO&A>%940&~Jf6u^MqCj;TLR%Yj3 zc^I&jtB1fWL3DopT=QyhAT{0ru#cIC>@nguk&1gKe{+Mi>B)yP&kYH9)IVj@^7&=XaOyf!@{wc>uJTM&yTkjZ6Re zPk()4_uYQ@+!2SJu!w@^frAEWYghuqiLjD`g-?6QLh!>-S8Hpl0wHQduD||zx!{5e zq!|S@xg2%WQF7h2*U1jsZ6^_cI`xgQXR5p&*Y0CR4?m}}vU1dvso%Tesa+0#@rm9D zFCpYa2S5ctWPz9o1ZDF3scZm<>E)uA{6(f)22JnIaI^K$^<|u6Hgy|u*Kosg4>Dg=<3xXP0mSH-r zqopA*!-puptlk44eZ&P2M1UH0Wh4ZqEd#I_fEK6#MP!%0^^vUjVwSYDwnz&ZIo_4q z2h;q8*M#*tuM1BR08p`-PImnQTki0n=bD-Q7}A(mtOk&cSesL zeoO&eYoicLB4nfhBMyCrhpEPfl4G;?jSW>jA4w@^S1^oBI!Zv#$xHfX+J=;y(P0c47CL~W!X0DKh!u_T2agErOty+a2?rYM(1 zNe((xn_(PFOVn|_Z_IU14V7U~sDQFvpw!+|9qKA>*F_Cej;E;IpibWCsU#UT^c#VU z1AQGtX^u*VSC&JNUN?7vga`JOa`-P*1k0@MRKRIiM4J7r>=0XvEI=0o@JNl%z`>g? z;hHR_2c#td0komZV(l_D*2I^uJ83@;elM@$1j!=yxQbCD1`oLj$JUX^7bT|$HfukTZ4c3M{4U2$j6?p7}J@1T@ ziA&4E*WZ%nPd<_nbQ%UKXu#_*^O4fBLWya%kyE??RK6&xyVo^KxV{ZKA*N=t`~_;x zF_>l=^s$;bfe%4|QK+;7bUCmOJ8M?tuw>AV7>kyn-Kf;1K**TvO}2~f)aHH^6Xvkb z>`k0#hZD8ZxOXF`&M1FDQ8@qE@VGqBjdHLm+9D@e;9X)hd|H}Y=MiOKY{BSWcz3}v^F0Y&ZUPO>X0Ayu*QP;!O@HA$U$2dTk+Av^$ zEL)MLa2hjd5_%S{Df-l4^5vSr?9Plb1xWm8p%`0@khTIYf-?R@1E>uGIC6U&GXI-z zB#G}=5)RS}7tJ#YwZifs2`5V@*gh>4qK1eBpIrQ4fCfSj)*s9rzg^g#V3J^#+98@T zu2w#T!Yu?!^8lzTmr#D&t-J^F4A3SX;At7-I}_Ap6w7RC0%E43tH4K#iBkWAqM?PQc%;`7>IPW0kbxCO zGDlT;72T0jLKwR*KloS%27)LG8V8dE5wzbe0gf???=oZp=FW+UINaPOA&kFMK@YRU zl1Pm=N?jO$qT{m3h5^>=1U$wj*0WW12-o@qw=k-mvyBdX*h$egLq%2?hjV?_6}lk+ zC`p|YcA6mA!^(XVu+bemh1bqs8p(|s`NVG_lXR#^&n#1FI+D0awubxL=o0xCIUdj zYXzY&pgX&=dRcvM8C9-7OOFlh^7YzV?pyhG>MR{TEjLOlB1+HNb+gG9!#(9V0wsgqk{A$q*6vATbJI zD1*srCs(r36|b8vk1N?7FjKmAFjZu&_<9PZMvThv29w2~(5iLXW(}d~+VvJ$r_c@q zmE22+&jlefLXrz+eFGMb0`rV~`|Y>V($dmygv#&a+U^w&W&}bIokDFeP=h8YBU{7~ zH0#H)_Z0Y;uH9Yk%lrrOJ+O85Kpp^XqH%rgov%GZhYi}PreD>+yk6fV9JYV}gKKYE znC?zc!y7K$ooCFLq0GsT&+-$9fO5-Od zRiys&*P7DO!V?D%8Flo6;W)uGxUjmpCXJuWc;W)W?{hnkv_ zD?w>b0AS!O)=p{nrt0owZ!D9;rluwpJiqwjiz;@msHhP7{+xR1sqN0xR!Tg>Uz{+h z1M+%V{lelPRHf1me#kUHa*oN~%3^4VvfDd4&K>Z|3@Ll4y( zuk{pa6-4FE`I<%5ukB#QXF5b4ID{KvoFT)95BxRe*On)qed(=Z4&5)DQ$x3$jk>O@ zz-&(-nqdgiMYhK+VThCcHC6ZRy-A`|BeY32hq@o3p$`Ae1gIVe{p|K)9BKLG0y-XcmQ@>5ap-i(A1#1 zxr07c%xp4Yu0~imDeIV^WjRivs=J|Z7yHU+YByjPuMZXKu0B!ouXS;y77%H_Bq$yS z5TI$JErx3q`X30Nw9Rfez$95J&`o5uTm*lrcd|;4bbeu}x`u7$1#D(UlBp>uC@?_D z8jE@-;ba+NGZy1#9D;Nk%iQps0X!3lHfde4T&lM*s>#O@PF@UMP#_S{Bm=7^po-%$ z21aEZlrVAQJtwq4$n;KD8EI%2eyPB$OE7r z9Hm#^`K);O@WFeOmIN=tEp9ySTftl>haPO!dUs9shaiSp9^PmVIph%4BJ}ImPj=a5 z7yT@6@)i}p&Yi8H;po`T3}&?JRx+r{;21G*K%aBrSR8)(#aBN%eE+?WqrKHE8#bvh zBtts{h7IWo?et@^gk}ffFP15#gx9bl{$!XXhJ8R1V*o2%5Y}S7FBC%MiFjB8OaVMayk>S- z7FJ19OO#uvMOzxfXOpOln+USgR|_I=eowKVU$YF93E6o=LHg z!~o{VW~Fc(Ef9+q5EdADRqB+}*-rI+y@DCqGm`{Z8iwGWpo?3*x1|ghm_n`2x>A5w3{(NO%354|cCi`Q22ci3M6ha>c9Z;;* zFr&oaY55aolIitfu(1k&)-Qt2>HPB><$GXj>47`|>Y^5PAaPogYx%HR2vOMpaekm2`}+k|1xFC9I-#*=U=lP>jn+`9}I zP;)^v7QOnZ7p8xH!~uKvE^*X`T_+o)0WxMe0w6PV$gCsO|FTg7glW=nIy-!JHQe#u*+Y=;8N;?2i== zUgtitvc{M#$53MmB4{@b3$8|ay+tUHx+ClC!334xMTW;22IKx=8m7mvJi3hIpzmD8 z2x*pppbU7*VX_miRH(dfBzE1|o0zwsv!Q{ zZSbIh=U_g$0WW{O;rrggKgnx_p%k(pU|*rh7Sfb2whkBcgKjpoW#w)a_+|b zhWQ@YntLD*fVz2f-}z|Xz@bBHP74;ekH>9j1a5Wcu;DbYg5l)uojtq1p)v5{rlH6zW@0*ckH~)sJY#CVw-PA zU75DqTbWUz3cv@#IGoh%@QPt7ydfyh1_z9LY3ysYg(ik|%a_SAc*^vL9wv$mFgKi^ z6PWGYn6%X?njt5AE{e&QIwGoiICo|@m?`ruWGRs0mjLS%0qC)LAlxkgqXeB1O$A{{ zZ5Rt;bpaXC=+D|m_GMr-(ZUuC(Nz0U&=ZAM({P~nAeEb0YOzclF4Sfy$=aBJp^Ia< zG8q#)bW~`;DfFTsslfnIZ$wcz*f>0`SX7?bqMR@G%oG?ip!AIE;){2G3Eo z&~gQQE2X%wSXr_-1nD$GvYxA*M0^N1C_wZC#`SI+Zx z$G*@fbR}ju2w7m^DD)rzKyHYjS+mRf(keMZU_g`&CWCqsClCW<%9>T=SW%~L(n+#< zD0$JA42utL&<j6M()uK@# zjBt?@vq|_4mK7O*O&4oOy3o$-6T-(@t14t+G$O?SRdms2jN1mriCS|C!HqKl{6XKS z^cm+tMGlQARi%X{Bw1Dj;IA!$!p8tGG1hj&>n*e9Nh!c)GwvY?ux%0OuQK{T;*b?f zYo)TZLP{zCf~e7B$kGOO5MpRGa%5Q&wRj}d;b#!;)c&NO5XPC}_;oKE*daNOc7DTr z5A>=Y=qjt+s~X>%8dF?cROIove}Fs4fR1p)n)j3Z{iS09vN()&;De0xFD!7KJaXjF zLq2$SPGxTz>rK-Z!)He4E(m$;PC9NIef6Cl<8}lQG%d84mrztf5I|dh8~ef6T_$RUT zVZxAIpMUoK;!Z!eHEy7qEQCkQQV8oh#k8h9oA2ThOGA^gY~ftN5>ApmE*@`1nPoXT3ckv z(j_4I;_Bf_n)2}>HOZeq#novpl%wiSS%S&r7{h;-GmvD z=+IMUGabD<1FW06qKmuHTfDihezV5ItrzqDM7P`ahh2BwX2|>Re2bXI7P)K?he3eC z>B>pn74y&6B6il^Ff}L260m+3ORT{2rw$AdJ$$ruh3dEPJH2bj_>jE-N{=9n(@~Da zU>NdC_x6AV@e~A^tLk+KU96u9V~mx$J7!sO95rN2_RTDO%duj)nH9@dplbXgToPo5 zD1`PPv%H&!2YoR}fkU_Gpi7x(y)HhcPy@4x?6zyCe{63uYF7mFoGXSSB6Ot(*w z-#z_ygF9j%h||dgu=W@0YjGumpbo;|DK(fv&}53-En~~vRmR_o?(g=KWWTqk^5c@X ztOGQ%l?9B*{%GbU#nOx}sv#ycL-1cxb4-j~8~|x4SfFL_2%_UNGXxa?G4#c7CdEix ztepEivhDWq%ZUO~Z2ui23l}VVxpr0CG~BJ4c<(a8V4P^oqLoxa=4YAj z^te?ZmHos8phvsO!Jwj|$xQJTS6)5tvcLV|_JyGOxzmKh_2l4=@ z8^?0j9fmCi2>MS;OZ+X&F%eEiTKbB#H0^-ClL6I@2j7Bs#Tz^)VargCPr3Zvnh1XO zvoq!9n{OP3k{sinoBGr(Z@=^Uw$H!%{+0lOzDsT8I8LmYM%G8YU`U5);it7n(u`1w zQ6GwZX6SsF4b6(@coP7KwXqC$o(?kx4Y6rB%*wvYPmnndPnS1S5TxT|@OUb3#&w=# z%`ar{Sz%1`nMt)-w4bn8Kn1v3w!{FKI%e2cOLL1XU9=3PN|1dAuP8h3Gluee_3K5| z)j5st28C<8+#&f76;Z4J9i zVS?t$lzaLQ@urWAI$61Lh5YRF)8yX!{v)kzjrL$cu;8L!U0Cp!OMm}=Lx&C+dCx;n z(%7Z9FB=SkHu%KM8Phb9H{A2jKTjr4enKmFd*BfKo8SCWF1h6Q{gSEp*qW-ct9Rac`!P>V zn@-_FZ(cSj05Ux{HQk!v$0}~B;K#z?6hPx*WC$!pj~P>IlE*(icNw*Zz|3$M`#On! zPjU>v_NC}ZZE1lRz0#Wn-J(tH);gNTk{How65p3Pgu-HyLJB=gr_d5%b;z4D@Y$r- zYtQv~e$V+H=)4E=0I2gm>hcyx9I{8t;zdheUa(;Ces9t{eJ$K1(e)}u98l#Y>(@_dIIi_F#zJKc5x6jB& zCQs|FhMsFEWH#X_l03*_e%2P(Aj3cgLOp~wVbi{EhssWYx9XuPNvFWaVBu8#V`3DF zo()eQ1E`XrvqE9neaFoms2AsgS!Sp$Dy^+yO@*hyrQtTc=BV-$osM*;8+_l!FMqyU zZx7@FP!Emmk%v#JTeNuTqw^Ooy*VCF&9pmh^rFu%Yv}rX#2TBF7(stv=kNU^pA%jOvnfhjLwAfbfK+0}% ze*V_8CKbTbu`<65_h3WUVWdSHiz%ArPliz1-!Pnrt!<2=m}VL@Ue@vqIs+Td#KvT~ zb`Yd)=F}r;Aylp%1Gt(9+Gv>`)vQ95)6aH2jB$o*T{b3oll@uiSY2t~+O}Qod)z*< zR>x!yHc_Z?O8P@j(0aje+GdPg+sNV7w`f;@LZ@Ux$^K}qGO$FXb*vHQc65No^}tkl z^?+i(Ls0ETjkb*2sM%*+BBt_7-M!lzv5ub3znt%Z?^h4x0Z!{f6S z*It84L|?^FnThq3sEp1a^CJo7L+3TiqQksfZ_Ew92Ty0CcWqUXSKSHL*49>2>`C^= zE$_bjuAF@GDf0gN??VKgkm19J%gs04B*z_ll4gcE95j1#O3>?*Vz5FUr(*#6ZsO3P zgU=r{WWdPBpL@MmfuICkdQpoanuv26>zMpH>q+|d*vljy@c{?LP|gYUrcfCQCBlu$ zql&8dFN8g@VicK#2n>YdG!e$%FvoNIqzz>cl0E<&FGRMEgrcxigjLK@!h**@#*hQ^ zq!+9KVlfJcRzqr-Rh=lxT99bN9@`}5j-sME^(iV=!9G;qk8&Mo1~@pDu;2i2QqMG< zgJ0!e|Futbvh5~Jwrz8=ak4epwlQh4YqD*-Cfnv@|N7p~>-iV<2Yc_euC?A@f-aLc zC2$Lz{##1O*+h3VV<@C<8x*NLNEqb*%fWvb)c*8W+sX1MV~r}>As>Z~hjG-X01IR) znfs8UG$_{$t{_a$NU#CHv}eLcfGAxUzd=dmm?%^h#b$jLu&}T1Fv$z!I#Z_aSNok* znmV@zH)yP)`ulFWBUZe5Bya4VbZ#En#?Gi`C@k@fRK{qKM9NWZAZb>W2ueWh(0kGcs#BOf50#H`RG^pFAOcxN?Z7q2P8)K(V zXzrL@`&EWc_Plx3o5uf1SlJt=X!51H;wK|cFM+h87ZaWhxlu1*Dof1nXmHtVz(Aso zNUwCFzrl6KnL9ATKB>&ubV(H`q zYoTZAsJYm8OgIwNtVRKqt6leu>dQ^_7yqt~yg*S`0SeuSI&ZiSf#_WBkB)!7#<$&? zA5I!PGx9n6MS+LHj<>`*&1LK!GYrXk_TnBc0wc3pQDAP|zzI)%ixskH1YcLCO%5m0 zUshvzFR9!%Tv9*ww{$C2ZP8xJb(j+X^#OWFuQPojS4u(9h)<@DhCAUpS6*!zx=Xf| zwkKkI=+Y%B=}657x-W1}C{wes@ByAE-b6y_Wpkm-`ol&OYCMD)U;0g0DJs*Uu02!k zed|2BRA=do^<4zX@l+A+GKIJxbc} zuV2R-K-tWv+9u=-TsW4D(s<#u-`2k;<8VvI3Vr-hlP`?gg*K1j|nJ85Brs(v97@6bYC!!XnBEKZ~K%1dJDjIs08k>cGHl9P$bhHEf({f4fk~HA!vb8kVK%*6M=xv_-y)jL2VlP~hY^PcEQf z_rAL^pPtBWzvF0LY7Xz>i~w3cIRw6525<$~+6sjy0kHq<@(K55xot++RNtD8CX$zf ztvXHfAIwUA`xSvsdD^6|j^oEEFl}z&^I@BVqUj0T;L0uSaXC(#?Vn<#K|pAKXbR(e zZ6u5lFEfB#TsV5H(vu(S@F)MC2iQRG$HNn3PDK*x`72h*^wf*r8C59kkS04ETfvmnWv=X$^)b+t*`TMHmDy3B z4Ma$UMDK?6g4t`Ecj%m{(LNp%(NrHcZT{<74S%sHz_C(zk7zT@g*t%~9#MArC60^L z4&fp#!f(tRJG`BbcsjrI0(Sc45{@3lUx&`^&bo5_XwE{Rffa;44N#zSyunOpVZGPl z!{*IEFHrptG#!U6m)4V#9CuJZwt>&>KwuFqQOx%_r%{Ppie?4Bfy#4rF@MoV?a&Sj zXZK-3H{!~7G|d;rh2MPr`i~oKId@lE23wRuyXJJ1x>+zYT1avOK%vFI%~PgBe_iFp z%RWc4T$c~m!605@TP{ARf8W1Axr?%m>2Lq!CA3mlE()0mx}0=&l<@qzr&G+~P;||- z{s zyxgHX@2F!vgWkk*%q{;6?bo_1pmf$rdbTJHe{01px-_sem?!52}Q0tNxqfZwZSSP;vC94gvS zRK=8?9upB@4Hu19h{XvXv&C+t z2Bl_Fhq((;Vg@MuEXXNqYEsIKf?p1~bwYEV^+(oV!G*=zJGNQ4foKus@E^5v+&#qZ zd*Z;UPcKLs5%g~a7~?QTdavr`CD6e=Fd(7~TpLT8ZMl)-rXf+Uirte1PpV4_&X*%r^+!=XK6?mBV^V!7P{HUgeK@ihg z&`sfbT`|d9S|_5jVieMa#h_c-_6?k5^*!f5gYrN8>gE2d6Cp4--X0pZK@^;i*3q<^RYS}tVF>cT_vP*KEQ_e{Y5aHx zs$z5wSzE>Ms*Abj)a{e@3$o^YNtozJJwXAL&`+^=@v;H85x0JS_SCA3(V8p+49TXN*;WBDK#QFSW50H{SJ_oP@S|`0->Y z;dJSgvndq1KLl*MQ=odWD>qcakcqUm>KDqzD@r!l(wynVQI-8w8D(1r`gVsk2YdQV z)_^r3ZTLKP1x|W^B3MoJx}qRIp+> z2w{Ve5(|yV&m>0!Z0?yl8o`QhA!Ri1-t5^9RYnR*`64Csw&7wbII+xhazT;nd zTHpU8>8OUkH7)gxGIn|ObL>f$ysBYm1)Hhf9+o{C-&R#ax#&f0*%#N3zSmSZHcWD? zjbLi%l61c;RGS=SxutwfD705rS1(3q>HXn4VqNJq^)soyEzFlHnw)gGunqQ2iP=Qx zK4NrYU{UAF@9^?gLBK}qvhuu%o?ZlW-`eqa&K%9O>W1Z??amdc=TS(o znmn^x^zW<0HtOu=Q<@;jEpdCIL!bNVbY>ssj$kDJC)uQ6WPg>&npGAN_e-C_SZ*hU z>GQ716~izgEsNB6GxCoF5BqMi2d)?9lw^P$#5=%_TGxCXT~uzKjXJFd9>~#3XJ5+)OT&03pQuo{BOg~AU?&@2-m>Bm2qn1@0XN2jq$*;F8g}jdAVGzL$SnGf10w^k znLl?*Q|qAz2)1a}`lQV|mIBtSTIj0I`~X8>Rkcd)Veb#en9;dmu?yML83l1?8fVjt z`kxk&%(BQG);8a2a;@JElzKU4?NGATVpDmECDVb=Vq29>Dsc9-p^A zmJAcLc(WorLY2D3lz3%^gZZb85y1yY1H1~(;idyWSusa>e*exfEanr8vUgSTXiZjC zlXl)Qq6Vsn0`>={aVTsIRvk8rtsQ7kRa80~!H5wn@)*%a)OSfXV44Tj#S2?qxkb1$ zg621ifI|9WFBZJX5fpQwR_3zN4HHW*>8=B-uXy=zKJ5rNDj=bci8TTy?%EudO(;krHXid9 zn>e|cO6g5skZqVbIzY*N5fY0MFVKCvIBt)buRlbe zZ65ok1o}8CG<@;bZ+_Lq%e88&SkBlrf;_EUXo@JHAs0@An2j}WyZJWnMO0=t>T!+; z6EP&fjUhNo227QJD;7T&O<4Lu5^riX6%L8ZN{v<03Zcs8UI@}w?}0$N>e^h zc$7;OP~FO-*HGm5Hi!Y$pJ3tLPR{N!$XVXMXQdT1lGOL{Igh5Y>p)S!mw1 z((J;RDCiNkkqh`@v-EC30a_fbaJ@fR57O&4yUiJS?7{jEU!RAEIuCz$`+O)`5_}nM zLYLqjk9>axp)sm!=qoM82~XR>&xfY%Ut z-I5A&adb!;jOiH22a3aGY1z_Bu%L1tCQHf@JR2y$<1p+##A(*57r(n8+0S*w>eG_T zl)g*tA9iB@J`)Nxk^#%h#wWvVnAnVs9nozcQ{01}F7^dkSeMGbIaUL`g-!jB*$1u> zI~`UXf2|N8t^sgoeur9qMZ0Pc`lL;~)+>~&@`xNHDFC|<2?XPRS)wYE36%K|{|w^* zU|2ugwJAzjnO9bOR7>{4kQ;gO0W_$v`SErGNUovUgf5-M%->&xW<8EdF`-1OtRVm&R0Kr9g~xV+!Yn2WntSc+Ih%YQWXIB*pbVTLu4x-WeHim`zB zJJfv0k#f<6f#VWa=~BPHqn1R5?I~jg|7BajuY;dgP?8*!utumfW-*29HdC|YreWfv zs^1y1hE4vdgj1}F$j$)B1Y&TTy|G$X$K)ct(z<#Up@^8`v-ZvZnsI0{bkaSBVAq_2 z%}y&Ai$_lv3%=?B4-_{3mN;pI$3s{*tHAbqhi))|3rQd(E)dc63tQ2%Vn3pz&s3v7cCCQ!}fAjt}@cUz8tS z9wEx^1dCRsgJvA(A4w#klrTdE+VSz99*4b88ETlmmC#4@85)(1c&s(dQPj`_q=}#e zO#@g#!nEl4e&AI`aOeEhnq%q(FE(t@)1w$SGtI*@sY31V=dm}ySA-Mp$ewC`g+x^*5o3*Q;lY-z zXgPPSaf3V2>~1JKcf}Foz)St59^)AeM^RX~>EL&T4cg)lbFAA5E#PDD7g7K=8dT^| z{v?m#jz($mhPpKzu94&HDoD6SC;k<|+ZM1@Gdysa`~b4q;N*M>4WY&hS6FPw!p1rr zQX0n7z?~i93rS5xA0&q~v(gEqq0w2m+)z&gj?h_@k2-eItw!L4dI9(+*NcE@5G#6@ zByNqqt3}0mQ~19h67CFfj%`o!6ZT!0kN_p`_ayaMC5A>}VVU zxC6ttzl@Wjl?UF#mq5!r^2-P}h+^jX)Z+K3PgC`5fX1ZuKe%!o)_7Db;QoBx+J(dS5(By4l?9!(s~q1T?j zn>Lwy2pK<#?Ej%@?|wlyyl8?Bh!*&YIVb0euNOad>D$ZsVYV^YrUq3`6^sY*HU)%}V; zz=>AO;`EBB;fA2o)j5uB(=M}wj>{Sw)!sgY+M%qi>vfNN9oc&%u%zaVcgTIXl=?-1 zBgCf)+pAGVVynBT5g-H`_bo&-AR##I#3lp2FnSJ&G64?9YI)~H`5$BymL2WQ9vLTd?j1&7*@k7bm)nyF)pz~wY zRK#-w){hiuiqtz@e2)0;nPD%v!c4_lmfJ{EG8U3!e-t<+qn<+RiU~4!$D8yVZz1p( zY~SKf-z4I2iYbVA2^@+T2lN#paALU1_Zj_z-XBXRhB$inhQrNkhq<>x`Vn3&j;|@c_MSZ~_;a-}I zoahffBp`T?8{~aF@Tw5J1>?*11x55R@nuXdnzCB!@lL-j{(r*C^W+#D$o{nkkFDZS zDeuq`q+1q7(dz&7HG-C&B^dep<>%=_Nie8zY~oLK5wEvTRFqnVzi<$SKQy#N9{qaA zzijid4xDkwpLHePtJp-;^AW_k33gZg&!VgB<~Uh= zJ#W;z*cBhASX$!b4>_LXQ`n~VCk7|6X6|s}DG#=vgR%pIR@oDOlkJmUs*~6e&k;eS zr78y>$6_RS@8Ned$cPMS?mYUE!DA?-LawL_F4NI32k9_QM|!#`5um8Fpj}H5lM1j@ zdRDfhC#-7bMexhAN`Hs4L;8{bFilsLBcQCT;0@_!mGhw-_LpV@O78h@ZQ1b)3Ad^L zGKc$6KNj2cNu{f=tph6nV|!YN-t&Vc*2MeHL6*7Tk5G$4rg_Jmufx59|J5;nR!&TH z&(uP|!h44y9|i3OV+xWaM!0Ij)_^k zgyPqR`ZJBsqb~EU;M;hneYYX|PXospPOG;xrLdRxu8+=x#2OqZbx-rvMs&-?L_3ME zM%lX&HfqtHlNk10OgLuqOh`dygTyhBBF6hUFq_0kJ=f&14cB=4{uneTR+SMZO@uJA zxw-WUh4qfAxQ5ZUeYZYuHY1*#BFB$+U zT-(+3m`M&P7JULygN<3x?+UvrP%LNt z?&}8eQ88@I=0Peo<_Va4sPIr3fdwAy<#ryCjyD&Ec@YcRJ~%+TEnT{;*q&!T-(qde z%;RK*zS@vog)E}CVTlc>`0C@oZ*0a6_vV9)@)iea>O4Q+AIGnJU-7kCjnaAO?3*t* ze!kjrE}Yk%J#-Ut+MSm@3f!o>xW%l6o)6E_;qsucif8TvCLDJ$`4J09>G5Z;qP zEQQFm)Ihu$pR@`mXiN+#>SXM|AyMv0@V*q9{AhM(4cx;LEmuc)p^rsoM%P223XI)d zHAz|Ct}PhU)AY{($;BOE<8PU=xRo0>^?{O`@DCVRPGB;DLs)xfIg)m63n!)b%J>BGaBKWRDIgfM$dHobRt zqVS0Zi|1e|l*Hr0d5kO3ejm3v|GsbmiNnd?$d8*X=k1T#EC-{MrfxEax+pAxqg)L=_APvx)fcebi}_PrlU~5 zI1b(4nQ2pe=WQHyR+>9px#=Lsb<>l0Tk(Q<5(2*&c#Gp>B^J=KqG}Sf^}woq4b8|G zF~5SZ3kl~BW84c4)-!{fo$)P|7lFhJ-rp+8gRr6(aH6TX!}yV158&}5dnVEw-g;D- zgvg~ohU415-eCQsUispAnK`Jz&M8@3aAFwBtnMe&Z0G#^iW8Rdf~D|}e#F0RX9)k` z+vii*7hWQwX_NFdn@ssXYx6|Vffz10^w*6Ldth&Yr0R#&MCs0+UAsNao;iNKbD96M z74(>|J7>&=rf)B#iwz6ZhC+k5($wk4h>7)t^p6l4nmZ_xV50(sWDNlNP5)OnKq^}w zYaU7;TUuV8sI&;m=Hfbqz%uY@eS(c;f>VoZ0%^z2d>kr|Z==kkvixuqR6}P#*55as z*?$kv3wu)6a}7m9+=yM_kJfMOhp;gvT$;OYU?pFeoe|x`4PR zd6(s|#vga6xq92a5eCZ{B+Wm03SXRRsNgdJo36m@3~km5Wo+2VXo6T|yc-7fUAtW| zF(xV(F*5QSu3ZQ~R3UT($G|X3Nx2U!4u{d^=_&gkY!x_e!!(I<=N3sF%E|SuqSqf> z{DS#n;sR}gykra^KV<~r2EqwZguE0s<%+-SC9Rr0!ck$nbt15|dK+A9OBM3G{2GA~ z-;G@=ypa57p!SB6Gko*KpfG;iiMlE-_08c>y$e}I?W;4;{?M#1$;~l*q6V)fm>eKY zf)Y9rSwW;6x6X|-mePw0l9vYuIe0-Km38Nh*NGxRmi9~MRyvxv4uUzO?b^%B;=+wV zUHB4~V2Pny&w{b8sgKF{C*$zLXVKx|V+$^lo-av~unAcJ4+QZ5(Qwd6DH#dI^VQUF z0lYMtxrDZfKdXDpf7clJ|8?dTLY+X>+9XBqgKU(G^uAY)MbR{5i^y-fgYNS;Z>hVI z>nY4l{{xxp{!hZTRUzdIsjCS4cV*AcKXcM-_=o#%<*`^SpN(=wRhi?^o49PXWz5hf zWFAq0YmfU>qq>R%=v1mH|F6MW8Ok|Ra=+S6AoYIH5~8U7e`)6lrnnzdf}h^4r{#Z& z1hYFZ=?ua5hLf44LYYpA1NEDY`M7}ohI=LS9Gu=*_7{^ zr|$ykaaMJrfW7Z%V_)U=jjy=^FXht3_+QGyyHT zdBob9Z$r@nrzuj&HEiu=C9`;Tg&~^Sf#=w@C{1V1 zx%hbSk@@~Kxg^J*L(GWVqe@Ifq!s6k-SGRw@tSD1KOkBf!EESb8l=$ujd4a$$U9ot zhu|;F2HjL+;w0md;@&xS?z!f+hA+F?G487q)H|;3c5G_n*n-lj9AtVRxnj>)!wGv! z6D7j0eSb3Dj$@*Iqr)O>vAC!(9C~za6#C(M9EF{SsXV@Q>Gbw;!1~*!;WHGzzmO`iRD$17dwruB=TUhWMT*Ap0b#(+=MH0F$GVS3rNpWom6Sc#jo^*6Y zt6mYm^?vF0^RGOoWeR%wx4@k4kNfH`TZCeA#MfvMeXx5~R<7vp*b#V^{K(#|C#3?8 z;7eqlXlV3$4}(fH2uWzFy@r%&$XkxsTEPPFT^o}?OHevI4_&FSe^?#-dImS+8e269 zvJC?TeF(=?Rh264mpKn!Tgk=42K+W9HT>^1%)>;KV*QbU2z=FWYtaP7L{?Y1Yap66 zIAL})raGwu@yK>$+(E}RA3uN=C@4Tc>93ecW;c)wZbQo)mSSW6wXs5@BH*Wu%LqaG z|3X}3a|F>>vi4-&Z}*_rTZQ6k6Y4o8(q*3Z{P@Va;~@u^pajm$K}njW;8}`jGTSbw zuvSsM3(6ypaEaijuK1Z4~NoLE{Fi%LP;yjZ@kPS5Q<5b*Zi~-5|8(}fK)3fo*mfGfs}r8dsym^PHteSt zL*TKyTitK!{7U92v;b1t)!+d!!I}wN zr_z;qJcB1hwcq}ksH%&6Cxk>Xq2cH&he|Y@rw(L7@tuYe!fIMnB)oxDH$tz{idfEa z#TJ~dQN)#AQqK;w0{QFoytk0u|>N|#W34O6W**04V55SFK zZ{U&0jb08svaMK!*3^r^}po`{>u*!($RpfWpsPgt@m2 zmxaaBvp(|5MqbvYC{q&ZvddI8n&-PJ;I{gTQX zO+%)^g##y{X!)Bc<&eopY4#2NqfwzMZIT$7)MR>`RPVq|-BR2S3^)0hi9ui&f!)Bk zQcXSXPI2j}7}IlLt+l53o%3%TO-;aJ>`NnR zBG?8uN1>4yKa}wGAT>a z30UEvvMh8Wc2|L8Hi9!u-@_KR-$F1j@xkC)y}OuRM5{r9gu;|8Ty2PQqL+Pjs6`Jo zU?RP!sWCKAPaAf$odbMKPBnQ#Ne;%frkIk2H+I{Y9~xWp&hQb zAlD*6)`WI8SM!K?e3~T-`*=zit#%uvnhk`|%hu$I!D~ZDA4x-}nMAq>mv7aLhG3WC zYtvLtD6%kyq2-I4n8eHloZ}=?MFq3qAW6kCYw_;DJ7?Z)8RAEPz^ew&WSP^Kda=u5 zl&DIe-vecC7z%j8L)+`XP^y`&8#fIk!!7j;m@ngqa~F(LQN+d!SGkRpOIo02=0_k{ z2POEr@Owr?Qje&Ti%5qeSGiT%O#mfkjAehdDcncODO z0~zGg%i)J{AguWExygrduB~j^jcaF}eA?ivEQ9&OdL~wUbpZ;%^zZOQZLg*^*Qu+M zY-A0Zp}W%uFk&>SxDrVAqAgQKo;`b9M*&i(IF+^Nc`qB|lJCyq;BJnkxg;pli^AV| zb|8|6IFNja@)+AX%mKRuCmflgK~FaKb0uu_0wxjCco`%#HSIl3E*x}+?_iUQpZ{(} z%n4Jz`fZNE3ha!0S77-=Xxfe@K9JKn6CjB#uu0d$c_XPzZCe#SM z<&ttbhp`9r;Xxv=-J08F!52%;#YPTr+{&|5M z0|BEfvHx&Ji$g}PX$Cw*4sC+|)OgVFF+pg0R*6Z_!3IWff=$N3Z<}ooT$tqS#U@eO zzc?!Wrxp2UA|AzB)8?UU=$amO6&-2V5D?O1?EF4 z{Qb;M(yH!cfHe_s-61|M=7KRtf!ym$b~bCMERGtJU?D)L&qB%!f?on#`Nrt1ryY#3 z&56Yc{I>%CD49EsOuIFFHsCEnER>h?V-@TjX{ZpAIFcf)VmF%&avF-&B$ozj#RYX4 zMI^49x~I3De|a4{Las>dvxgL-z?*sNp@Ql4$a5sK1>)YW~kQUj6UK z8G^LlmeF~%iH_a*ysF{WrW@pAirIzkpi(t&8w0!k7Q$M=%i7QsqG1eIJaJeq)IR$YjP zT({QB#;XxsS1*X8vM3*dG5rjW#*qKwg&3#L}3aIS*ua1P4V3(gFChKt?h>{v>>>;z`p*ftyD7W(PrgAod)l?vBc= zdn6>|v6WA(n~;YLwN0%`kd~lMz}8!XF-=~!1DdvV;PDGQjcAGtS~XlxZ5atM(~R`gs@RTyNJ!* z)ObElsCzerj%qjfrlqt*+#5(L@AhNclf2YKQ(JVwNF-KAkr{% zt%kopJ35ZJW9Y1>6m=1Ge!R@7t3E*hC6^uvT7t-Sn(>+FN#IVX$}QiTyantW2DN;9k8aX9bqp=!>X4 z7`Kv;?~cQ=6%=w+3vp&m$fe)EENaScowPCy)h?DlxW-!R8@s@11F8k8aTNqAFs34= z(~1_Ta6fNB`OzEki%0#VW)+?xV!R_3A4qFAWp*a zVcf|Ije{Kme+bQsKv=fLoCj>r;o*MUh>pBOygsso(!uImR=KtjZO^)csEf~D7`31a zzyNb&Nmw*0@yQeY?S2fV%;iZXho*v7Q3@=L-d^WJ176DpFz?zWbMLLjPH=qyLQq25 zcc?Bx@Hr3}ErTs(_sK!I-yEOY@LHV-d&b_3CxTX96*dfk-#)5v1+5hg z)WzLpfmznKtO&0t08}|esaQ#u=3xPc^U{B^b0-eFvC!FmmlZh#4Nx#Fph(=|v3teC za~mr^=c)pSjoiHGVCVjOjjU)Fv|vpvT#*DEA(1n0CK`YcksyWaGAvD5uj}m}1-j5W zB#`gm(VT_B^CNLuoXlLoN-K6 zbX)Tw_EPKu&la#Tg=6P{*&?uL-|ep$|NFxK%1-YOp2s{1xnmJAL;e7sfci@;11|1{ zts`l|74Th{w zAGEEhNJR@qljT!xIW)s zsYSpmBjII}R6n?nK>9fK&a;VSWf?7WI{GJL7NheZ2P*AUj;ZWjn-NTOh$It6f<;T2 z;b5I^L0Z-~mj7yNJROnu7d|@O9XOgSB?(@FCK7+S82CJ$R0wi%NE-C(VGahn_p^#O zdeTm7H*dEYdY?A2wAHZ-0EJi1;|?C{86?7!5*n>OfBRjR8Tgs2vr<@7E+Sa{^f<5e z-IGxNbnIIu341@vvkVt>p;N#Ia#YaD+$H}7F5IlJW%~bR8Yq}iYZL^z+kk+o-qCE9 z(tqjG@-2WyJ!pljBj8T+(mLbKX!b8qTqR2Eti;F&e?j0BSromHJGwB32wx~@f|+nQ zEo9Q*1=6zQRNE6L>7cMy0NWMS+`LK+qJ^mI9Lv{4!2WA{*ckNMzgRgc`G6nP!EyvJ z!%RUNNQeEg5qgMk$<=)_W@1Y4M`~D*?CRJ}XAnU-%2biKa2~ebCF^_kcryl7NPLc!7wG)2HQUcWTpU~CTuN}q3(Qu#)P7kOjzZ)Pnn%KrteH9RSCbwi0Mvy zPQmqrXemvfit>UikgtDn~v1UX$mJ?6G3l9tNc`J`kHi0{dzj2UOPSWh$T-Y|Px7PWi5Y7jYFA8k;wGwvx}r=UKoS66f1 zvzOgVAj~}Tx6F*+NbAW;Ti#9Zw5&3qDq}K{Mw!4nxtlmQ!s9LRRishRW4Z+`>Tkd} z3Ntep$8pIqybPOy3>5d^a$um~$O$17g%;CtRmH8Hq^zvZikiw`+tNxKNK_ZKkAEGa znP_<+<~6J`?(X-p;?h=er!;|jRgtg5mvs3-iX+6#2RYjclkl_Zl@FlEO2ip+0CrZ( zWDcMGJK>+mp-EqspLNl2w*zo&7A79sIDML^OnOQ%(ORNxkf5aCX%(PUF6v$j9cC!C zNx_75RVwCacre(d5mz%+Q=?S`wg2`9>gKM8M|@Yd&{{vhiP}Hg(;`OOaos1DOU5;$ z0sBrw-=&sf$)wfH37Ji1UcE}VVYL@!1$|N(roP{39>!7h(O4E#M5PV(9Xm#o9T7S30FkG~7L zbB?e?O4m4e(A(~{MmXp-lh^ZZ90+P&h>Dp5=h+?*UjXshK9@vr#MI7qzWdQB~3g?2ojad$RKPJC~ic*X9YLQgCmVh z1Uc0u182h9eN0VV1M%v_C3Y06FtDB2HNV^e8ll}Ys69jfZxC$M4lMP%2*f5ZLFgcr z@lg&Y9Y;%mwspT`pLRY(+qa4f zQ-&IIUmb54OK#%m;>kp^Z7`UI0Ng0)`YrO;9?;L`ASB?Q!(S+3`xufX{xk?jHtJcN zGG;Xs=_F*k&rAeRS;%Yx0xbgAyrRm%j<+I@#?Uw_@|v$k-Wse~8m?ET;!`SnkcnYJ zdKay44>~5uck6l{QRlIh&9mkocgM$BZh|)n!)KjFeNToOEm=)Upa6u{Q>*6oDrQU_ z`#2X@GpHalV^n4KUeO5p9OJiv2#D%f@wKUrtJ&1UuLcBxU_Q7^yOO0U%SrwQlwiY0 z1*(J~I|o7-Kk+pbs6Rn*KMaO?UcdprM8Ja_FauyD^ujuP)<@9|(L#GxhW^39fZdEO zKRgvAYk6Mpju#eT@rtGm|JY$uZs-s&D-^~-n3)Im*3qK|E)J1FaT25< z7Eg?5j8#z&38tNx1rOJ~nCW$sts85iX|57x2X)1};NpLys7EtKWF!{k)uacH=ZZH< zHZlwDM>`IOnPLsRyMpNmSe-$_BOpD1gH=DKJSVOy4hL#X^sq+e{&S!kRu|$trh=Q} z*+U!hnBTe0K9yP7>wz#`zw{yv4`H!dXV4^#VEfaK|8C3n>oqSWVrt&(8}Z(F=!bJ3 za{B;bf{0hr(;T20kiyX@xE(pqbs3&-S3GvMNBg*JyQOs*Egv+i0T67ecTrbsA zT@0%wT3UI)cvN^Y2!my6Ok}({Uu!l46Ae2F!Q48u2VkYT5JK;*4f{q@0@BX@`MSaK zIbL0eupOF5cU&hg%=|~K=)Rr=#*Y;w9FJRa7$gwv=RLFS4u*v@^|R(Xq@b*#NoWNN zkX_5@jZW?%OP_^J4u~*hYDI2XR$t$kkwdyI1O7Bql+IIv<19o{SR1BzOBX!RMDe>C=lT3vjUK@AqAK9d*j>m@JUA5nhL%OL-k)S8uA7tX(KyB!90@@ zjhgWEzD5Zk+U9}fovynaVUVG}2`M6qN(G7_0|*<>abvm1b!Nm^wu7MHKt+BrZ`AB4-`bZ zA(^5GbCpc|mKo6Bj(@}wV$N>75SlKfXEbKFDBiwKJPKX?C-I-^?bS+aJ1tU&al9V| zl&S2PDzn+0_ZaL5@Wfm8WeRGdN!J&w<$$%{L$ym!+5aU7w!8sh16V9{j{xq(e|yTP zF=C$VTTh0_rv+cv6(k8fbLn9&B2w^<%R*!fc#f$7xn@u<^*uC9)7*iPH#;x(D0S3W zUMQqT&SkyYmV85?*QeCTDlpX%WlJsummU~d7OHSO(VGQB2j-9a8pEHT2TNo4pI*u| zfR@PIN^1IwL@X28S;bmFk}urzwV`5^Vi#bYI5=32n?*1NC^mxOw6klZ$3W>K#AOG2 zDZ~((a`*B#pKwUGdcm8wN>o2~qFeb@_;z*v!hO;TW)75x1~6$5CN`Yw`)c6u4-KDi ziLTGajTMDeiDYla=^8jH8zzapPGiAm;dd@8uCL#U`bYZ2ix$yKk1J_js~ZQOZm6KO zxkxJNwyFOh@I1Urxcnof&xs0>((c8%pg35W8pE!>9aq`d#jRiqw@H){;%&JI698Yt zplhoPL@qR99MdBXwiNAE2_PCR^%%OK)~9r5e<;r&xvV4uUnbPR_Hu2s_j5aE>yG;h zb1nWC=Gx?8;a{)%>-Y1z{PoJq_ot(`K^uWe1GK{i|Y?8 z;PZ94aX<)x8B+WF!qR9C?&HkN(!xTqpch>3h2e{OHk?O>D3>O7(KlDFGoGKwwKS0cb5V)oVw3|oUWJB%-9r8fP~)JA^vq@ z->0q@?6kB2vw&~f-*AXTOyZ-j4Vyzty1n3+S-N7M)epUL$NxL5>0Q=U9zjs%{G8bh zyY$u{`jHuOPAKktKPc}8Fo_9}y=HjZWa zHd*!CvU`gV)5?5O&5sFU;t0JdTLqv)hQ{VbB9l?pK>?f=zy)9+Q>2zm>?;%3Tl#BF zuQzw7f^E~eF+m|W7@suGs}&leGz=hJ#U--mp$E(M`|peNcZB>}5e6%-fEN&tqi{*f zRdCg;HLHw?*|*d^@pU^`Iy!gzx`T%e$hqp{Ir*-iS|}jRxZ8dcNZ^2>i1AzA*_MW zu^m1x-8;fMI?9-kdEwxZEx=??_hC%3L3cu0V)YVg1PcR$9eZh^Bwb~yZPDVU)Yh!Q zxacl*B1d8rYj1K+2HTaP_66IH+ItTfgz^s0-E)t;aPNK6+Snw;017UIpgaU2S(l8_ zJ`>;&|0qXgv^fp~L{8*Z^>xd+o1*D87j^b#*wJLE+I2uMA5@ZsAuaPm%JhH@G7f71 zJ2lEw{AIcj^yQ??z`|0&P&~4wG-gc(b zpMTGRo3{rKQ)IJ~KRo0I(cad^-q4pwW|nBxwM zNB^I_>j03eD!bqGKC``M(|hlY8hQ~?5EUCm{Vfz5N(V&{sbWKlG*Ll7rFRG=^pb@1 zUbpw@eg1Rqo4464n`D->O`M10Zf4)SdGEga?mhQ>=R02_7DYO|9obM;HMi(tMv8X0 zidQq8*0x{}8^FhH3qunX0B{Q6uMB;~aSm`#V8~19omgO%&fnVSwS2v70aPAXAw2bqcK>qO zxa)T9nmvRkF>XWEApRD#QH77WnpePD%Xfo+nm*w0zaC(w32i0c8az zB5D9%J#9jTp&vw@oFnydvHSR_|K3xFut#+X`}IbVF$iPAv%yS{lXMD`Gs7zU_d5#~ z$a&|VCyN$;qRz3XsHpLwhyJnrxZ_SZB^i%LO*XTOAsS{LQ6HkhLCEltbgP&l^Du`|&B7Q|E@LYSyo0s|#7SYSePI7UduL|m_xyy%)9F?ni& zTP-Za^EbmF(TIsU2~V0>%K}W)Au*PZ#y*ueP?;7I%K`~ayu#z**vJZFT``q@t+)Yz zAhdxEARvv9PzK&XLywpz=g&Gs-gx3^dF_t-#J^}It^wgTI3p)uGDy~lF4fdYDK!!P z%@7W=qo9CB$c!R#utvxbizAaQf%eiuX4_KTHD-sJ7lyC0m9KcdSrDUui@E^*BuHY~ zIye)065;&>9GcVkUR93^*FbhB!SldR7SE-ta~j!VY5pBdR{|pGPO@KRRWb|LH)hsM zu@n|c2+q$zfF85S0o@Z^ppm6zLYlhOfKVvfRF$|;VJY2?5!YctNfQg&Fwq7O+$cVC zGuX0LDYUi2vY-Rs?MJ&XVu}zK6j5Y-6t0EfnI%6|+mp3|Fj}!->ew0+r(u=c@k)|m zGlm9Hc0@%$=^l>IoyA7gQX+1&q%CPf?4X(#_Uqu0|MGs_0=Yjq{rEEGKlFHMpl(Ca zmZcw!PSkCgAPdroNGg@2*qb(LSTH8WqQ)9^OTH_uy>ZL(*=6qxaQEL29sr~? zBmg}GK`gQw48m&%^|6*MTPCNUe!8@_wyG-hBSws9x%=+B{YM;e1O(+MK7nFg_#B;e z)e1fZjf@F^!h%qT!P+d+x>SN&-ofg>3F6-XfMy5$chGJC2Fa8Y;00bhNNG1^V1~z; zJ`II&nqb7L08j!9LduLX37EPp30D^M?EFDx_$3a&m9bWctzd+hiznf5qi|b`l~Xii zJ_`I8Rq;`{5ObmhI8tBO71vUV8#V1ROp`>mm4&(+Y#mG2$=WBMl}G;Z52=GD%8m)U z5Maqd@BzRC$^<7KS2ekqDV0#ni#7}q1hG&k`xrB>xas~r%J?tK&S-AW+TXNO(*L&2 zBZxv~Cr~AIOG&Uy)By2olmHsPyWD77fTI{dPZZ~q!SB=^MRBi#$X_q487SlD94f;n zPZRHef$#~kN(avgVJ`A?3f?f2OHpLdATiXXCFp0_9=%#STM+k)fDq1kQOjvMOOL0g$HLP!rC!t!Ed~>4O8hYi-xe z1V6eqg%hR=HM3rQ^;P-$*T1fq1->_U@L)f{k7f4k*>q2y zas_~l%^-o6N~mEm{@sQ!6^fYx=-BAJgV0(W3=l0@j9?Z_2$`r_SqcD=D$?{MNqGGb z0Fjl+6#%JoLWm`t0)t~L7I(=wI2ca_2&w@y<5LVoHoyY5BwJ^T_gljp+1syq^(a;f zEChwk%)TNRv4TC3cE4=;WHCZwugm+dy(X=jHc0|?!&pAT3!vd*;aa*e!$*iI=>`^J zW#$$BD>`6n z|5h^eY}hNs@Rp$+Y3?B@vbDkKxlugk24sA-DFZ~65)Y7+0Ruz;qyZ`YS=&=jyas@p z3{z#`#4qA`E&)5|g-Ig{#(Hex2e_}R+t|8f@xpum``8mLHlu5?%jv3jxEu|){Nd)N zJ#`lO4FFKb;in zXIT)RFjK&WlxP!D*~uelRp6o9Q2+=5A%wqDs2N2QAagP|28j1BjmbQPFj*o4j~`>9 zI0{FIvuvywoC9&JV!YhghoQFKZ?~pc0+pCR%MTrg^jYsfKrSuNwl6T!8`4 zJw|4Q>Gfc5D4vcnRUU^h*_{%J!n6_DdltA=7Z#U@edqwGm@+}ePM#&eFI^(P5{PMS-m({WUp_iOdZNbqIcIA54ULg>uUBmILkm6BGi-E63Z;CYi2I8opf zAjoHKgC|l8`auUis|l`ERo$Va})ajfn#Y2ySAl@jR!r`zCFv`oXzA4w; z@axRx&1-8r+S-xSm~>bSwh+!FGN5L_%SRn`)Za@=O4j}Ix~n_)=eU&r?tBaE=@!Uk zHrvzRx!cEE@bqiu@=dFTFL~z4?>DVkds2C)Abvxk4{RHk9%H?+WA4?N}@F?`rz zQznhQ%Ro~mHJZt)P5VR%%P&|%;qL>IDeDFcJ@WKmKIE^b)aA+hK?8|Bgc#t+mHd$hP138&X;X#1S3SiqYM-YbWrw!K15`Yyf|k=3PS}LBqM=O z(u1E)yl2gp%V3ZUVN2BUqK?XI+akq|O}M8aI6VVM8#=KcLtiX%s%3FegGB&Dl@#yw zUIRdmIZ`_Oczkb(1cElX?Y6t+&v*Pqnwz(7CriGRr7;)|17nB^%*9Uf!sYN8fYaTP>f^cn8vEj z==4Cr=)oWcN*O7vhCs8}QP0PPRkQ=E+f*9E46-;jCsO{_;=%<3jyn7|6?102H}|B& zHSs}ylfT^em~Y~Q@kdXXIP!9j#W0yxDcj96UA?Eftz32n($?<>O)C-r{qc`~lxwcJ z2F}Nrya;Lla-jRk6c!d5^iUT;j~*`T{<{&TgeVCGwgdeWwYDsn-HjCCf`uTHzH?4n@BbZ z3@F8$PIw8YO;RwQDTO`ofAkj_;y#aQ@9e!zRte4Fzm&?XAYoxiU5vkzN z!61_=57Qa`D2h0x(z*6@Vz`YD3yG?;uYKCw9 z!n5RgOexe@!^?#GLicF!d^qO-UL630C=!bj7p0CSsq!=c^uqNSY#udTD3yvA86vhO zLSJO{$ht9$k05UkGeBhMk_IO}L53?^vq@yE#cuY4V&UZF&nEtI!^e~W}R)k25~Ng_s0 zhY@shBqRkUvp1DYj9;(RMa13G&{xVi6dgAQK0e{%=)E5H5iZv|m4C4lB6>4mpX0F&XWRjUxS z$x)Z0(x|xa_841-ihLNk$FlVzE0;Va|NQIcWyElR_!t-?04F2dhk@`hHvR<4w-8#b$NN}~KkG#HX_C%$zbM{(lhbum_>(7t_Xd6U~9mC zvsNCoKVGyqwOp|%SvcG=Rn?u&(Ezfn!DgXei=y@>l+g%5UxV@%rdF{f34mCujj4Y+ zz|nO=6hi6OwP!+KY?C;ECIO_8p}Yu+lVHJTMHn}UbKbOmjoft8%?JfXA-u;JO7chb zD*$xXc>~1>YStYScnBe;xq!@tMo--alL5fCE5=mezuX70V9G zm{J4NNZm|f^~)MQD7A>XOlFArVEk55E<+BPD~7?K^05MS%$Op+Dbr=s#;sE3v`a7$ zM@S=pRUKpZ80^!Da+tl%@ULGt`lJ86)HLn1V_ASFzo7-b|BX}I?|%6CmjD{AQzi_( z3ghaqSQJuOJ1085iMmh0xq^WMqj@O+f9IWdA;o2*=O$#Pz;$0qxB`RRzM>v%1z%|xEAdPG!P$rI|keJ-1IIJe{ zWge4yfvM0^v$L1e_5GYgHGe*JI@y<6ps-e0d4-Jh`DE4l)l%C~2e1GD0+xl$3YiDG zOwSlGTn@*zW@4z=9txwPeW!GGc1p+}0QdpRt8{4DO#vWewV_>10AyZ|M+&guAvm=m zOE2RFBaF79(3z8dy8=2)XoI4P_6`I**%xb*!IMzaAo0|*&d};7{;ez82zJOQrJ$dJ zG!F06rhpnPges*C8E!2AVSdy_3y8yt_ske)l1Ou>*m=7G94ZGLU&No<(KYdN_qbke zceJ;?$UY_kfauI^LHjx|4n`67CsRql`q#hSBhBsY;&v9Jpe;jg+HOvpUoy{&Mr zO<_fEN=0pscyd_F(q*3?I%LEe9B5-dxj+5lG`{=Q6I$*2TMQ$4 zVo7KzSl%I<#he{PyJp3HaqfHKBtZSknP;9U(E7*?H{2lK`ObG_!h{J*<3rHG)izn7 z?%86W{$5V3$t+Om7AB<09RK;PfzLa01Gf-@QzM`1qS(QGGwn?B}6J)%|@BmmDaGw|sBZy%p zoqF+%5&q$IDJV&BsZO6P8BZa!qdwi;XDy4nO|!**1@4~WN3 z^GSf76#SN0&{ToeZTE`5Yf>itJ$@-g;Cf2`?)6;uOO;KBEe^dJ!3?UyA@qs_kU+aE z{CK7MTLf&JDv6Q>(E4rD`COF&nr-7)B38U`;leM~)YL4-TMfH+hWStDTVVgUzySq- zz}L8=^^K!^;q)+v%V{J3X6rF8BP1{qMqHtZ^luUCAM*BPf>e)5V{pLxH|9tSde?W=h96hG| zhnNRSnE~`^-r?W~h3VJNiNuNY=i6_;T_6HgK!}s^$tRyIcinYYmocJdw1+G&W((o> zo?$V{X(`X=RI0Fg7em4@h(-j^kS~MGswDa@C*{MAm?rJ5oiJB~E(MH;gI+tzKw1YG zdh6%P{%BW4zZuODIh$>yLSg_xGf>%V%j&&xu^`7+Mb@Qw1;qFv5bTPKc4+WdqQGuqZ*es~tsEQwU)r?OL^4 z49}>AVH+F@4m~&=v?&yqGwi23R%dSz=jTL(nORK9*BaJ9BS^AiV0GyAV@2qYr%FMy z7T(cHV<@S%!XISalE8u#SZi2?ha!L?!=}9mnzZASI5X1C8Ag;AbGz8gO?WjT$)ZV$ zy?8GLbHfCH5&V%T4rhC5{0e~~&xJs@+dX53rMjm&sX|5A?qyLD2r6a`M~y+)O(w_Y z@<@`GTTYi4`t!db)S{5)i4~Fv|t_pZELOkZwF`#98dtr zmP|YAH*KC+X|R?Tz*-{>T|^W8M=DEY@Z70VIB|^3EG?DjT?Gyib;{m7BTwXJ<_;d)+IS_6?V)lWDIFOo_Devc=d#_iQwTZ9W_$MeETdh53R!ZLfRLDcNCN_R)%H>6^(ef2aTJ6fg6JgsxM324_kYy zZ2euGFe}-DftQZ8$#lvZ3HhB6iE{MFKiIu=*;7wDe8Z#vee))hZK9Nwc~8MOTg^FC z8*o|XfZ0G=DCfZ2R2Fa+qG%4qN|Fjd;PiP_8aoBOG5nk{V}?9%|KH@XM;?`Do_SUp z;CVy#!U`5g`Qa$@L^1`Mrb8VGVHQ=wLV*Of5xpBY;jm^`Qi@y-sVQ?I`)iDxcKmpQ z%g`jg0(kWVkxc}KGZUigGY$ZdpfbKxMNG0e%;U+L@8!_w%o-!`(Le)FGK;`q1&n-j z^*7e)qW4f5`XD1pHzaV){uYFJBF#vZM@Y&&5EUMnE>Hg}Lc+AvajNColTN<(m;+ z>%8Tr>-UU$JBR+8-zDDyd#VKvC;)_U(2&d|3^7A8-2^WUTLHW*=uThg6!Q=;K5=-n zShY#TgknT$pri8{vm2BPYy?%XV@PjBrpxqkN-S;Lh(R5qUMtdhTjGIaFcWKclMl~t zJ8Xe7zIfRBfBgHkn+}^h#ZX!9Jry%n1x8tIwjx8pFiAE8=)h`5&P-a#ge+uHK?zP> zDrABQfK$CT-HVJKHA=3y`U*Mv$YbQGC!c}d1WY2tx6Ei#VpFsU!&YR5(wH0(tW~qL z${tc#M@(Obac8gz#()8tJ7pMNBj5~;Y^TUpu#y3ZbkO_8g7k5s2hcA{ILlVe(k`5( z6#bH&{WQF#ZQUls?!SW`-9|Zn8cLyVjijAT3J54o z6x4NQs&N!}(E5YcsO0cyNl@x-6))Vp3HD8VWnjAjGfEykf&IC9QVW4>uD%MNb-zbE zEjnVW+GIK+(wH2S?)g-xPo1SI6SDngLTY>3Kz)|R_~=_2=gn+8Pep+QAR;#x_@pTo zl+Dd(N4rOCZde@P!dwUsrZC*mgP3`Sd0LDs2jUGL0 z^r+Fn`iA;vZ@B6AE8xS{`17lOk*~4%sk@YaqrbMm0R@0!P&68>)_4@I&Mugd1tXnW z3>(xgmf-MiD}=>91~)E_!2~5i+tU8tsOkV&V0i*jWp)^AU9noCEp6fsM%8ol)gE%{5qSW#iyr&+(~n&L=o7EsGab;(?W5#5pYFISD~t=M>`AC^0E=v~55vVG<(+4)0G}SR(uk1mN76%Q zJsZNPN?>e*bJ?rWG)%pOz!YoPV^jD}2gKVUUmTY;*B}S*Rnb4mD8*x9GO%qk`XcUM zP7kRTs_VY7og3r^w)e((ZO)NokRB zbLSv~r39HY5O!jfz4hZovbA=tSR9CVL)2L}{|&Ks1fdC{6Np(NsdzkMlGeDx3N)49 za%q7xPCa7XGta+$Q)a>_M|D-v=ZqPXM^%}|h}@ezI+tVa-+i(y7RP)>YA~;n?deXa zs5vtGMv|FJ3><)MEUYS4jF1B%FeiLfe5e9Xq)%a{-gGC-A@9UFLBy@XG-US}o?`?2J%NOqO zoU%2~ln{CQjd(cIW&f%D&5(iXvq1jVy<_MMQT7Bs1yy?Sic;adZQZHsi}`)MUQRD> zlFc))oJJ1}OrT=5;7tI`B$yy8fY8jDC346dfNx!kwaa^y4a@biA6QM8b{ibjXCeKq z*ir78T~gs1TRLL!55`QJ_@9RdmfyYjlTTJ$f7R9b(AX~9IlsgHYJmd^0ExvG@eLTb zx^eTC)=0dwjK=YW@bv*DE1y7z(TbH?6!GkKr%lF=9x2B1QZYF!V1N>`^rh$Jg(n`B z(h|3LP?fqU5SAgJkI5LN5dLb9M}wI{&*qfd(Xqd7#)0^~C!a8P<+IPfb-m9!!{>He z4u^@ZTMI`Vs9U#lCkWF2sn1m101!h5Os5_G6OwPhe4pQ z6#b8m!zn8~YqArb9)wX55&%Xk>TfBrF2Y?V@nAl~{#m|qPqrjMhe?%h=mqTbHtP}q zqya*@%AgReG0O{$V`0pTSAwg837`rksX`994J*7_Jq23>IE0f1X^gbWYpXw$qbfa8 zfpCzLjw_K*-^iuDwHw)(PZPh|?ccsXbHQYTz?mgFw*5J~=MI2a&#>L6kGkz_KUHx? z|5IP!HUAS5=QMW0P_^8zQ=pH&feQf2SD3je>e^)fjtSJ&#)Np z8iR^y$jH-$Fc&DSEGhox=TACeUQJEa_mU?0=%&k+U^>6$TcF>yKyD*de>rWNGvFCG zaQW&F-rrDWu@01A$d&YWiWg~}g}x$jM#9no50M0bkke*YX}+p}B39~NJj270&g{jb z=tGd+3^PPKQe@C_OncZl_3mRDhw$u5EH;Q9OLkUIA3KY;V3|Z^vW@U zsVw#)w1vZHN($|8-4nsIZ)Ii`_o6`AMR>4x8jP47yMSdjrO{XrhED%0#M_n`vGU6NnS6axLK#S+Qz0PIZcl0G=YDZ_r zV|4Ra>e{Ur)IpXDBd6YixsXr2wTOPjy`RyikE-jCy6@jxsI04l@e6p199iW({;l=F zzK}BAsU_{39ZI0iJHs#Fu{357fm>~dc)SQPLg%cp6aL~dTfPmE^pcye$Y+b?{!HZe z+*d7dKmnjRryrkr|Nh6D3>D?Chnhme>>1-IFH(|OR4~%qEFK8?Si7dwf{dB=4ur&N zpkd%>l}0htxH#b^Yr*e!yj2Pp=u{BSx-PFotoG)p(fV;wRrN>nzVVIrebx8}%lG`> zxu>W8eD|XZp@F%2;>1zE_PA|xpqp^(4#~P<;I@(p{qxFIASXLl@WIg5znFG_=qPB; zP*cV%o@^Eo{b>vYJv0W4;3vFQpoA6_jFJHAg|))prX5*LOI~?dnvq53|KMXVj=XkF znVSLHQtrJ-j z_NkKDRiY8jQPs%gi?JzYux0ryfD~|DFg(l=kl^E5d!TKhhe|VD9NAj|FuH-|D`eSP zxGK95s$-RSel7RuB}}E!bbSF`y)~+KU7y-|(cP%8;=NL!tjD&avM9a1Pi;}p_t~bj z((dipy_~iI!fT16je3GZwh5090X_t26fR*O0Cp76&7DW%h4#FcSzxc@B9-chevIcM z4Q8DgCKj-rqb5(adnSz^I&tXeKO8fy{@mcD7p=MD`kQmXihH5Q=MR~0f!h~#dKw6w^4D*;=2JD z1vWKFrnX+{SlxW>dg(;P^JEC(U8Ej|;TzNm<|l??r%TtamPSqto_fLD})gzs$d3vad;fP1{Qg$?C(79 zD1bqXH6?P`IM9Q6e(xHq)s>Ky|WQ?%=oKR2Prp_U2P?^hdRmwbpG7r=Z(JFw#|6}{&r}7-q3>|6gnA@g{pm(yr zN|Dois6u4iAt#{TsD0p@GVY|~j8&(dJnWEZGcRAfV%ab7osGGDZ+@?Q3+$T~$n{Cs zH)oUMx4ibR=iIf={C8Gl)yk`k!NANayVdT(L=y>gDgks1V5k;gsA|d-F_x8yfAv~P z;Z=;4iH8#ojKD0|CvJ5_YcqzmwRYD(2OWLP-_QKbC0lYlg+A|g*7q(oJ#gpsxy=lH zK6?N8z+Yh%wv%b4)O$Q4>BmssOfD*-A_)RQyPpEuy9Ty{GufV zbMl3e3=L#N!06O(*(~)dSIGLsizTprqnJ7Z@S6fN1YasEK!TOkytywxR1!jFng%Xg zzg9|MIyV+xHx?X^0D@+7S=%{fjYHuuK>%L_Lly|5VY|D>TSketkpUJrAl#-u{o952 zvR|2D(`z|(}BmXk4|u!1nZ$ncx0gme|8TR?Wjq6 z+i$zuKoX8di`h53GUV0!+PP&j>(%c`6BIpg@6Lq)Bk*y?AcAHm%V|?Nk2&qhy=<^h zh(#0P^!j8|dz&Q73gxJaf7D!d(!9vW%NL#V@<$)Meen-2pja%wz!0I}7M z{K|>JcW(d3;)Tua|4123_O?`PRC_e+D=+ZC3Mhh!D4XHEb2-UL59XPAcNHj?yYq;+!wd3*Gzhvv0XA z$=fH4A9D$6WDO$lQK24M7Iak(|Lotfw?0X|80&DMycxFk|5R1o5i+BG zm`=>B(ZmJ1Vt+8fCzVf-K|EMZn`!B=$2SDCfae^j0hY^_Fr_5K$zJ8RYZIsDTa%rn z)uf!A`dNF+qBA4A1}ZF%VNqbppump;KxhL$uD!#Nf>SZTq8;D|h5lIUkJ(Z2UQ*R_ zxsF}$!#j|hHilr#9kZgyDMCLx04Ci5Os-&N+kV?;9(HSCt^KnX!vQ6QlcC{sE+I48 zDMZnbVP=QrwCVbuT@PJs6C3NwVIj)?UWSnG7BG49A9=$BiC)!San8Sb(f9 zr?1ZMly8B3&;q;e+56xmKI>P^|LtAXtKNHaZmfReH;XgzDYj7O0H}W?8IFnr+MY0z zQ_QFnkF2l=!WmW!xHe2&s1V)JYLKn<7UPpOhtGR(;M{qu=brJo+(y68db0mxpZohi z9v(P*=G31I8a(8SItc3T04M|IC!LOK5?^>t1?n1Q)l<*Phj`ijVJSjZmzAsx{Z%;m zV8Ub#D+6ji*)V&#km^q3Lxyk?06+;F>RH)MvT*$xu_GFlGP~yq@A}4 z-r;w}p)icql^(V8lV@0s0Enzq3=J9u07`aafNIJr!2rSJ6k$UoFX6a*lL5+Icf+>x zM$KU8a%EEba1#{@%AI*(<{DYLPu)lTzIMYp47UbtsEbJbQcjy50^@adhh6;e_8^K3 zpdI!FKIkuF@B{0_a}so;7`4wOXWeprt83D@#G-`@&u?mOf93cSkL}3m>+?J1TVOA@ zzyZks>1{IalK-oH@6Lx_T=)Kh`ljUzzfxegA7M2ZYV0P5$8NNkY+PAmkcvZ~nz5KO zkz}$nZL_ww#v&Wz1-_TY&6)E^@zm)XX63=p9`9z~`1)5jKlsmw|6;PD)WN_3U&9xa zZC}W;`vBTMa5zmh(_m~uE$#BbzyB*s=D#lS`erG@1YE>Y4jm|=0s!r1nSoeD;MWY% zE(`!|fJ^`}Aqee*08Ydf3CuQO0GN2fBmn>`0|1RNVG#>3jTt~3?s4iT*&qTS&U@6Y z(p8utmMDbQ0a8(WVN0WkXPRM?-sZIJzz~?KqyyQNC0N;`+yYBIfXT|Bb`>k3^CwfF zm5PZOamv}6njxYhJ%+P`xiib07!m4Y0y;H5WD)o|0vj zwe2MIsqLVJJ$r(kZJ}(PsnQC_#9`{WC4qv!5VEHnR_O%L3Zl={ZryAfId+6?@W7g{ zG}kpQ!~ydFsIOfjzyDrofdd)wv+v4NE^+8E9-i$)1diu!N#z z&P>LN@?V*-)fi~D*y=(S^IB_B!J9=xr@lFO;>0b-p7(=2YI^wkf1g9aYh&gpWUJ|S z>xC!YG#r2GVS8(&+$)`8PaX7|U;FCnzy9;--yMPyYr}_>eG6l0H5m^&;3_5oStCjl z1ZU)I`qQ5J>d$l>x*&I~SuZa=_HS9aWQlS$c2<=N&Qj_yA=@g8rGp`>tTrcs-wAeD zbBb$r0vWPg+?24HHN+713LrG*@c5*?uv7y=_&dWtia^Qs46V}mwyB1in7tJ=O(XS+ z;HA5!gx{^ZKX?D#jIf3uAjrUw8Sh)xy;uvmXrVA=hNphle0+E*nkHy_y-Lrp5clc& zB=t}H0777)a9A1;wk{nxLYmQ9308-P`Jw`cs(@>@t$Ku?db@_+&S8hwt%SlmKeonr zk*xy^$}v-`@0~HX@TAtj)M06LyLP4NqN}wD;&)c4SG(`vTDQHgl^)q{!)MG~06-U) z04Oy>S`el61NL;{{RFZiAxn@9>dJ*NyTp)z){qFh2d5$_dx33+tIT%T^DjN#a>B7+ z@b|r4@}J1Jz@BY^+?E-8_B;35(cXXL<HbgLC`G6K@QfI%&*zhYu?G zHiq^Jv=s(!PQcmpX%hiZZyqfBxIMtiWIB)`h?fZ=D}yTFiF1UxVMY}o&7MzoQLG*w zPKZ636uwVoGt=Zz3gd$pUXsef0vRxEhAQ2%!;n)q_N6~Pm|f_-7xttJG!e{kKF+pD zE=-DQP5nakHbp!8o0?@Y^j1@kKMq-%ML-(}@z9oAKast{0A=rOj>q0>TI{{t<-6{W z0#Dob=dSzF^}Yshw{>vs4`gyP2y-bHaK6~rRgqUN67y!`tM_2cYN(J#qm|=!H&L|&L}L0mDRT* z>B1Z{m@Nr|!JI-GwIQB1wX{XNulxP6kS!A%S?u$br_9dQrl|SFjeg_nKfmaauSOuMq8*n2ViqlHazqK+h;2#N=%a_44M6FUnCTe@;Bb z06?5zxv{V0n3a@o;yus;%G=qgafFL^$AYZfdMwl`=bCHM>g|nN7xkB^@^;qn*~6zujs^OwVSux?yG;jl`1v zjzFX}7>U<6c0?L4y!qa}BZ}JtO_e{yf!PB6VAk2Q4L9@i^O%E6hNta6FT7+p?f7H+ zGIiyAGVlK9?}e#I-CR$+_H19K@o-Zn1{nh=dX5hiiDIxu?N&8*SYgn})M$J%gx|V9h0y!732|s7|`) z;-wg%{H9t!@e4g}J=;4D<}C~cE{S$a!&N(_Oc%8e&}}-u?VA?JAhbraP939e$9nNL zWD^3^%|U8mI^H5l{}$SmkeTZMuuX)+7Ps9|HP~YHwuhRA8!|0z z(9}f@a8t)0ZDtFsF4GX3vB<6&IgmC(s7w<6pf$5;aLtfM=09@ZyYs$$PGH|$eg0ei z7h2$;13=wx+TH>`A3u6ug(*^hoFU$Rsyi8;=}6%*R)r$BZMxr0ijyHscS$3&3_>zg zS4t~+{v8$`bb>1rQ|T~xXiP#%^oMwddmnV$`R9HqbK773xyI?R-(azf^OhESj>NF( zqJwNM0?vKWAav;AEn#vG^*|i@Eyjh zOZUYU_P_67Yjj_sa1nvnG5nac2f@x_HeeJ?Xn*#UJbc`j_fXSl>5x-t*Z6NeimU9P znDz&;e@BCCUAaJ_9qT0;0PsNpRyzx9f=(x476s*?ouak`BZ&p6(GE+e zq%a6%0GMpH0*PRS8&0Gn(Tph^H#znSTeUaZ;jB{ooA=HZH{?&o2(zjo^;i`J$;OyoaJ4tq@7WfRiri(TsRdw31@Zu> zuT2;aR7kA-OSV+U1XsrIW&{|9_h0naFy+Bi41ihV@R5c>@ho&h>uOF_G z;f`xTISHnEvp?`yN%#kupflq2npt9_$MO41RB%uA2?Z!2kmKaWc2lYU_-SVSuPhkk zcd@xBhqs_wy%mkc-{}~wz+&zgbWfdj{y5o=sRMV-wvtH<2wNo)jY`L)apn6GYuzj^ zXy&|NI32vl2x}SBNV!M&(ufyaNC1^kHQojnZH$$nR9LfpP<8ciM8rZfNQTgcP@O)Ju_ssJKL+6GPSp8R8Gck`94|oP% zAW6ucX&A@)NHmP&`iNELGgz%MtfFN^Xb!(AILMl8H0|Ol}(A_ zjeR(k1A5mBzI{gcum5~@;U_CLUpIctkjs2_^Q>?U`a7>2U(l+ z|A&|?4}-#NcnY2gR`?#v!vFv=!z&e$_NFbUe-{$3#UoDaZvrUFB%-S37#?Y5lR$+4 zFfE+CVQT=khb2(#Dv(7!3F9Qj%QRmM732A*R*rwM7@@;MN__@QyD=Fah{wj+|JyBp zh6hRu>>a1I1Lf-bc%BGzX?zZ7SxjSeD8ZP3Od%`kL0N;6w1wdQ8;18qRAP>VM4f4g zpjb0sL6<>-FbWH}N+j$VED`Tusf|0^S2agJ-rSk~@PR*GbI`Lt-JAP6UpuK|$?`RC zE?u$Z)?hfkm?bjwG8ua5j6U9;J+r$~=Jxi&AG+S}{`-#ir|YvC;#vBF{zqgx$oO#S zi$knfwoKaFTDqNgPesh0xupJZ6aywc6TqJl3x+fdB6*KLeE%H@wRdi-{h4(|V0_TT zw#&A+f%Fky2X%nBKis9B=b+hDs8?6#SHd53YP^sFpP^n0=AUL5o>~e@kwj2}k|th> zA_$g1q1KdvtPWD;!R5i04KMsV!>mfCfRgzH=us*m2F+|3Rh{^Z8+Z#vh|O*QCj`=f zo-8JV8IHC^f%azCe;$6Q4@OD-v0d}$)L&X4&j9t|PR#z?@#zm9x&wAUniiAX4*x?0 zTha6>Jfm2%;6a9V!Rp1z#ijilg9WBN^}86n62l-M7%+n7SR`WVTxGHqHuOA=yZ-i! z`TxFk@X2X20_w zi}$4A`K0Q|Q!@(_Ml*C)^{oxk+TJYgLQZzdN-{gKvf4WCKTB%xMZYMfT<(w3xEz5hjvgt&@co1W1-06 zzKTV%0tA~84o;#BN=S{KboW-7mHt%P z;oAA0K~exZCOkt){>S#h zivWoKDW5&{%=L^Jo>;AuA>KSCt8oj|Jd{ z`XH8oVQCd49q`WL-!P7iXOe(Cu_!a}#c46QoDK&K)ndj>#Ggten)lGKkUvbm1=RSQ z-}=x37=q&%p73-dMFC;zIPxAsW&j|o2ZOc+f3QddgND^w2o}f#FCd6}sS8q?AdN-f z!_HW@&5pIN(=|CBNiD=FEY(lLluo%|g;5RIv>NXn)BuRreD;}N*m~bT{&ip4Y>$o^ zHT+zJM}{#Q%LK+mgF&pCP6mODOIB+|osbexYMaWmBNx}L+PsqJ^a6Y+!0qhx;|}Tz z$!?QqFXavNLuivP!5Ic}4EAoP8Ix|kVqSj|zGS$50-QjdoA zIVFZy$pl)PY@dU-|F4z29sz-Ne{oMWo-f;1we!>9EIDFM{G9RudT~t*ob!(Jedf4< zwVNav3xW{Bb7(?{ z3mLBTuZ$N|@MgCVj~_hYj9JEF#w8{5(gBFRj>&5$Z8 zbtb0|s&EbuWSpNgCQW~hm=ay%aHl=kR1)%8HBJ^XAMwrKuz8 zvYr3S8!?n_y!}sCrZZ+ob4%R%oW<(S{{D2dx;mm#khXue|t*Y~5UoBBeoz zp{7&{6g^)JD7UZ;0PVUHP{X3RT;jW7UEub*rNHNjMI&L;Nhh6TKI4optK%_)jSfO8 zs+CP0Gbl4TR1e2WH-Y<3dY{lP9s;!wF{t)m&k1kW=X4%U#xoxY4>*!v4Q*jSV4K;r;ZWKOlCaX}hwZjo~6K0RmS6 z=1Z_&8WVj6%x?nVm+!NmC|f8+0%>fB1~mFhf5>LBW`PgwD%k%CNfQ{yOg4t}!O|2I zNo>njaV2r@@O<*@P0;X|Q4Kx?L3Gg6hR_(+j3^dt1?%U)=Tb(G1j8t_8*P@tL^}YG z0oox?pu^z%@0w)sLmdWpgH57VU&q!_h9c)NZN`!(Hzea*esSZKc_YOA{{S2`XOIU# zeY}+^lQodCI5sCz?)GRV?#@`8h-%?M$8sl;LB<3^U`}W}5{RA0FwrW*LkfIJ@gw_( zaq|oYN>11&Oa=vo?xKu2F)A64ng4Rv^=eUh+jT$k;|E{=Q`@We_wnMMrf%~ zL#r-86AjL&D&QPYNyRfONYmA*_sAe9HZ2WJObl0PU)Hv3-dP*<{9FHMBc6p>nLWYikJ(J5I`C{Ot&;M+AXo!SNKT0HzHA8( z6M+vthCEQkAZ=$esMA+o%JxGW8PgqD++;zq!5oMrWL+?2tiS%I%b0!^yY|Q5 zTT<5%Y4Dq(O~1JMYQC4>@-48(S|AUA`q-3li`ySh6}}Zs$EV{lFUPvpYR2l$g2_)& z+N=<+Dt*KAhpEqMEW^NnU*d7RhhHoZ2_~qd#=jFNdDGPDUx^{KqmQR@Q0{)#7msUr z^w~H5=|DZ8VKpV+2hgch18)|8Xyu185`~zU%#S9PG)M%_vycAwKk~=l|6W!uU9M&k zS8;*NI`k0n6qbnF?Glg2BX$S8g>ev%8YY+jCi(H zc#j!QbQE{M-l51PLykKNW~j_+0-LExg12q`5AW>c?lPM(BmVYwS+i=DV((&LW6kjK zK`=`_tgJe3fcDW0&5sEc;|&OPG5D7P(?ts1g5T{{guf6D%W&a<;8{3jY>mWH9lyTm z+Ag4T#cg*|ABj~=e#^JOo^OFX0P16#Cd{5d%oh6q0Ouo2gB8YPq{b2oBdldnLmK1! zu?S+`2#2u@0)#+PQdA-Z1%+zhi^jv4gq@hMGt%A}k2kgi8k$?%pH5~n|1;sPT{Za` zCmr7K&;Pt~cai&$>H?qb1S~cODraT2WexNIeiEn>&XCgwAAcm*UjG|;^2sMbvn6HV z=n?X{&z&q&rcIOb>Kb@1xfDpEJ2$h-xb{|Bp&Tb2f*Fdk`CY2U8yst$c6&i%ZLQ^l zciu+HFcvxCe2DTFV1Ss#mfsHI7EmMzI;3LzM4C5kkvAWCOk(xT;zD@VLahrCH~FQ$n!C{Aq&57OzCIl5fH2`#g;$(w*{*uz(f$$X&|7>*l2J@8!Yo1IQ!fnBLYanXMkAnyCqto9mEf0YDct?)@+{x+r z&pg!tQ`<*SsjhL%d;et#kgt&GBZ^5_=nLSo$fRro+Y(t^1ND2oIR%y_RCO4U3 zg4ZdH9h+6``1o8y8hpfzwxWNv!;eBDqLp)gA}PO zY2~VoNd_S?GCp=$%roVPBaaXt0B0P`Tnqzj6hFDe5GLbh zM~D(>CVgOOShN*t6#y;*DCOR4HA_otvx~nwQMgmhWjM(^_{s4GAIQ6@npFt7@>z!d zCmsrjIT(~Ogu-jCWVgM|dg zH5&>ClS$>K%nQ{jg+g}~m{veYDWzy?m@NxLCj*AHxfqPakTgIIoY5t*M5etV5L?zB zQl&2PTfPPMNek?`T;qLmy8Yuz=AC?sKWZyj7IT*UJ!vmolQLS9QKW4}V@ThH+b@&7 zkj4oz0wFGB`82h(Nvt!3lLs>ZZBQf}mFCutz{V{N@2%UgoXzk63G7+VMY!NnxnGz}I_12WIQ_@aFKoO5K&nl*CDmrqwL(CKG^tX;fv{l>p-ZEW568Bd|tkKJ?Evo@rMC(ix$3EQc4_vCl%%VP~1 zR8zn@YsmzBxG_H%5f5|NY_f34Cvw(V-x5Do->05-nw)*^InY;Fr89`}Q{`GSLV>Is zux(jf0zsyR_c>r|NF~QBQ7PHl=NJk@h8ljyDi&-IH9sXKCF(#7s}TfsPsPvi8}hpy zKrH}?j{?8cqcM!d@+M5@N<#<3zZj79sbOFWgQ*8Ii3_YF`>zhT*?*L=JpQNqJF5Pz zz14p=gb!LI z3LhYhj|Qa6Lp&Wbn4C?mvDS@0yXE)c{(JHHGdtK?AP;~(;~hQmgric=JoidR-0XhU z>M|5%Qn6WB1yy2#EMRp@12aBU%CF}5(Sa3Wdo148)X`bHX2ZspHgB$f#vh0;yzkEI zw=+TfjHjbMcF`|xbR!xwoFnbu9(Z#7x6bFB%Ds0lu8j-?K-u^EHSp=WH@H^n=(wst7}y22wW6|8wrt*n=M1f)3_S_z2>g(pi7=N% z(YIh?>AS^5In>5WQy^}0!VuPlanc)1W~{YQYhBX;(gpITd62X~9sqrY8~ON4?--1k zgd?3yxFZHz7-pxh7>%}NkUSoaB9Dh!O;VytI$()tgdV85p*7LGVbg{{H2zV0DDp;g zyMJ*c9Ivxmy<9Txv2F06lFe|_M=$KE{an_t?a-?7I|b|(kA|FP$7 z!-ou>Fm>{%-(##D>2TUgC}6gtmReI|lU#cFWwLDHVmb1}!-~T|f#94+Ws`6pAAo%;9u&F+h0`v@d!`fA|hf z;H%kmQtP9=*m)SIW$6>e7^-k7JcIBjzOuXH@A!B2GSsKY`sk!2KiJQO3CbLi=g8** ztZPN}0IvCD%a(OucF2I4#LiTAv_cZB7;6I4CEpD9b6W30J%n&!R2j#jU2RZ^Ha)T-v$RKjqtX8AhS>SNjcuT4r>9n!gVsf@zee1)k>gt-C?!Nf~ ziiAG%cJ5DpV=pcBonp7fW+YRwl+$5c=MNCfebXP=b`GpETFS6vC+RSbh}R0io{TuHp>8YKXsh} zQurndaC-i(ftPKI^zHV8nq4o=!7MNWDpYZUjv*?f-1HFGerT@zSMIA8=mv=SD^y`f zcjxelz6rN?mV02iH_iN#5@2KmW`R!}Y!LiWHPFD{@k1~2$G22v5Y++oU-s2SeU|UZ z#_4z=WL#P37mI+(ni|w}E0nsHW@)Ue7eAgw<&X^@Br-tc$gnu1eT1q7Czhy~omPp% zH9wkm!ZE~RfLba)AAOcvlK;$SZh?G<%dzR6eDQsY+i9N+>gQ`FV`et!-qLhBVNDp# zX_FcLr>R6ZlZd!esYpRA8W0EEp`*~Cw8JRO;q_Stj~PEWkv5hfh%?7zabzNqM9`42 zMocCLLg*dO`3fHV>&;hu^D~K|uleJ{hN`lH;e`dsIiXN%VK|xyxjn^hD^sNopzXn@ zUn!eDb>bN{WhFmMCSzkMUbZORPDa9!pj><1HFEz0f0LmjM#`^#eGRl1s8Iv}qof|>wFpU8Ur&QEpsMbv-Sc1PMgi#1}f&)}Yt<&Q(i&JdUtnLlo1 z!tB=y_6N{CKA=uj8T#oRW80Z`^(hvs!ao=Wv(f=sX9*epVaP}ITvZ$ef6JC`p`8N$ z?NIM#Ng@8C1kqWyarN*1*iu=65_uYC`P1zh&wz69hW62gqPEqAl~PzW7|&%~0v!Qq zZEBP#GUN6Gcc_T3d2vuNVjZd z8f%i@-THgE^>=@gp~FYYW!GOTW*4mVL9d(W!kwK=x{8Q)4Wpb?^qNgQCYA0(ZMWZ5 z@oSwW%D&I>o@%Z*%odgFHwJeOf#pV%!}TPHq*cM_P?6Rh=?BH&>O3{Bu{fQHN* zOvu)I{wDSTWs-z1P>LuTdKeq(W+ecgtf-{!sIC&G`Y4Q zW0L)wp5*!9TxF9;q!w`IBK1k zIMCGGE?e3I5?X}sI*if@CK^gxJ5=yoF)_ChHAs$5oMT$ zE~&~Bh}lHZG9w&Q3kn^_4;obY22SVg98Rfgm)~6X+hVUhd4VArI?8B@m)q^uwAJR^ z5{{bR`RbWp=jeO!>#vptwy!6tK3rOU*PXQhZaOg##mU@QzmKWv>puKg7XRl7aRCP$do@{$)Bo!`*VJ~PK!%Pfde@EYc zd(j$s{k?_K9EBOI!wujU6GOONywncYd2G)u~h&-WxJ_$e?fk;sWV6(}kyxU(FUQ#*`~KyDM++uR8F%E&>Cw%($S2XesFJh zvFr-_9g4Q%Czd;6gUH7c2h)RT+kpbeZkV5%fS^?(lqzn@j4%p?0g{|%=w0X&20tA1 zmjatZEbt67W6pPCd`JOQcZNG9$taLo9PPbp?bDH|VPu^%W^k#;K*2#fgm! zCtED!%<@~l1@>JFj$2!bKrMbTdMBFr%YEO9YJ z+@!OPAb~U>M6*O_U^KHp6cVM54D7HMz1+?yI6zN<3xEK}NJ2lTsqy*Y{fvV6_004_ zR-2X~$(SA+O0$26CL;35#~(;r!)9?Iom~mPVG2nEs1}A0i8D+cYU-3G#K{Z@nMG0< zE6spU_#4MPHBORQLv__+Us)gd+P%~n@`wGOX@NWd$^`%!48sv#0W-6Gh?>u?VjwUu zumply>EXdoV3MXum>Jn$;bCJy# zef>q>X~?A)+8hqA&ElWPBf`Pqa0YtrYv5uOfI?jV(0~$P}{4!GcVhGuLB@j z{nG)O-aq($u2{K^8a+n+BrBw@f*OhZc0jg(Cb#Dt4v9}Wor+6eT z9q9K4pF?Sk%qZHc;!gd-Q%a?^se73U2T}RGP37wY-iQk!Ex$h?p|&QeDnSwgo$z7$ zng(EG-n$WChbBj907bCV62f~)WXpm20Wjk4V3Ft_$uJx0Xdv0aR9sY)2S5kplXKvn zbRGch8UPi8xf!a)#U2~SC=089W29r9DJ!y&2!zN0k(J2c;U-hTk{HTM2Cc2w1a+Kk z&pJaa@HxgY!UIZ5i)L3|iy*3L;^} zqYjrrC@TpEWVpgZw2x1N>7myTUvvbb_RomVM*()yG0jrJ%HyicFo=ZFUxE<*Cj!t1 z!Z>h$6F+(zX~u;Nf5FXu%SPF>aV;c3aTIg5iv=O9D6Xm4>l7FMy=HSA*e!(kxWFS* z1eVkQu*fGR%Iry0d`|!kCSZcd%mypV%Ve|)@eHp)IG*D8ncwm)urFI64}f;j+L+9$ zF}aqr@_28lsToygL7RX%=_N5>=#w9RCqQayYQi%Q@hm4utQ5OyGibwSwlo7oy@;Df zBEexph7SBjFx2@W{=Du%2H?#^U>-g*_;b1*tQ>+G|nptM-p zVRlGX4SE43k=R#EiwcJTz;ffnr5vDf4qNN$)O({wjZ{CQ(0~#6=sKW%`4H#7Yxi4# zx*bM(fUmXZoLE-9M_mh_SrRiSZ4-KA$ zNd_h`v;;=sAoNN!Hv}Mb+RQS1Fw7ABM%mOHz(S5=Vg^~SEc}d6A;6Dvf`)ESRnfrp zQAd@Igz!w`J)1dRURuxu^TdA3TJKgv=0DtTTVU@AC;IIr=YC>Ozw#jgA`3m)bN^I! zPu)WV4|@<{8x1INT<9Lx*9J*uS3m%mt)iTAqkhhqs;aUxF1+SeZ*SlGvtQp{dFT;` zUFLAR5A_rlm8{*g^}~frR=#}at+(_`@N@S0S6c3U_=)M?`qr0!KWJd(afwv2h}l#o zlnWtvGb>Mnrp6+C;$ClnQ`bGIsd}*rMRpVUP_%3C*wn3Af(;yH)RAsE`crq zkRRv7jV6eCO)zSJ^4kI00$IjKMY&bATp|~ye51Y`0D1(BhWLskfSP`Ovt2su7703R z;s+ZTL+CpRAQJ>TLQ4jQh*VuKzR+8f6!rt)MDczY{U?NlP7t4O#o+_^Y#hf)f;on% z1L|f`6x2`Jkf4PG5E!l$ve`@kXj7CJ@vRhAlqd`DxGwiISHUJ zti$dQ2Ue6;z)LbHxBvOia{C|tD3hj5k@J3Zp%lYPpamIqCQKL(j|+n-{gb$U+&@&g z9`>T1tFb|oX!(2vT6mL#dAG1!J_Ods2c|#muE%1b@v)%~wj=Zv3rOLMX`TmJme4ZQ%}R*${36Yr?Ei8wCGN zA-s=I4TqyL;!9Zp^!{%UwLAmxA%FqL1*dgXx#z-#+McXhDn%hUfenS?0E20;LtG%3 ztpI_L0;r?_97l7=CN#v+0D$PT#Nm-iBdK6#5~i)8a3GLpfcF1w|DQU8JOJAH?dG$q zangO<^a+qL@W#eQumlkOk?y82pzCNFRm>MdY$~e>115ZCOjfIT$cPa`zyIq$K77@c z7kpKLn@exJYsloOlP@VRFP+!k*-=_vQPJ}5JMY}TcEhHR9=`pi{<3cDYN>CZf0gg_ z(@#F~@I$8E3MPC|DjhQ=lW}B>xDjfw$_vlED7Cep!3yleM5#0 z9SYV56@oA+SfDZB?;(_)V3^E;fpvEaoc8H<;(m447)9XQ!t*v`6>NfTLC^jo6O;!& zeQMeMvvZd4p}8}nRk-0`JkT#IrcaW?{_k8#_~A5;$2EqqjTy!MC~jspn2p+<&0D4E zBdk+m@CssvSoW&aVenr&Y#r={r80Ep9I0>uM46c>g`Ws5nZYP|A_*Dfa7yWj5n}Li zy~Rhl&qK~$Y0ETNSF-~GZBK!O{H;nHZe|TH2*PO^%FsTUGU%95ldn}mfq;?$00k)x zBw1dK6N5gAzz0yu45LI|UjpE%t)&U{IzEAcp>E6Oem92gl}61UwBNKq9suq9RB_HEW)J;HF>pyU^E)r=8UPM zE=7&5(nLIIa6&U-fexY*9y+)E{!enl^*13qE(|Ih;&D6Di!qq8aN?tnJ}TvK#VxDU z3=hNb+ex5(tnl2S7f&X-f0=UnIT6&3KuHb<3>)=rZ;G+|&4_WZo(RzjL_l%;jHnzf zxIW`CEf_XL4yztW6DZ9Bhx@UUg8flHk%KmKa2uW3Pq4pJ#dHw+U&Ar z<9d~?$%Pu39|s()jaZa)fFWyZ4XA}44NAEfhyxs2?Ah!{>`wq>M!T5^fUt-OB15*h zsab|qm!%OlOEfh%?%xOdVC~!a>)K%p?+Q2g#iRqiP zyXTmgeQdYY0@4Jt)cvrA6f1|Tf`>Zx!RnG(UtBp67}>B2Or@e~;J~ zD6)NJ-qCaCV#Vcjxa?E0P%28nP0?nzZ&7Vbs=HjjWt$+XOmb(Xj>C_W`I&#>6 zvtx;*&jzN1I-dya0K9O%eG^XEx7~WHqRO2P)J%&8#qIP+D}>Gjf{Q;`D9=9itel1P zcZbV?EE^1(Xk0q(M#H4xF&XWpwPIK0c&@{ljhtPJ)6kCk1qE#JW(XRf=_j9}*dN`D zF_-KwT`aFNO8UQf5BF9kTYu;ux<3-HQ9VCkO8B$iY%Fh~V2f7vgcH9C+5m}|99+@B zAQ2%Ts|$J^3ZIeE$qXiD1cvZn2a{15T;ve}xaWh;h))X7c>)hKK~0R|Z`6$A^~X7u zm>kN?lYL5wg0V2*cmPi<R&kWPnsiD(L>_%qf;F%eVbc~D$m9M66y7$Y)j4x0@UDpMNc!Crc* z`$KEy&!IoIKpp_?O$R7s+;nw=PrjLdX8!-(c!eY|ODGajo6ya(zo{D+^ zq^7vMh6&LKmXQV+s2OdnN3~(`qNR6j*;vO+v3|AP^ym9Wo_WTp*H@PooPcI=I!TeM zc$E?UIRy$%kOA`h+i{IBM#J@TrfF^MP#JO@{8La)ezNcrl^K&py08Upt-uJzPX|`Y zd&?q)r>W=fFvP)Vr8;w(x-p?eO?({7(GM_ykDo9>Wqxro*HhqiQ^uZPM)xh^{@nr7 z-t;f(PaT80DS}0^XMC}MEH^!5xc_UZX+=v3L$u`fZb2ye@}MtRp#@U!<9-M66In}Q zQyJg5r2`85~!# zy21W%@5n^coH<0Y3X8Kv4IQ#9h)m6>SKJ=lKZu_jI&oj|V492`Tmr5S*Qfv;*l#!V zeab$9fD$2ohQy3GP69E;mW~doLnfsI0&tA0X*>rRu)F<$_v#NVpFf9v)dKxvsj;uF zwh!O)pXc6YN)P>iDmW%GbVU$Eu`UZ@vRp$PzcYhNO_tb6{p1A61tYU1u)<>qhwu5Z z(%}F=27#;(C(ZV@_NNvtT6E>oWy}7A#m@$e5edf3Z4gy*;$?Xa8iv6o6^7Gfo@m2w z3O6(~zPEYHmM8!AyUVfM>_6L|?)k^~v%hir9aW_TCt^Z_+98Vu)eo-xH2_Kdj7lk| z{|8V76@A}MFi8YY0cawU^2g%l@w3qnJiFM$JAl{o(#9 z!7`{XJMLKsdZpE?*Gec1#s-?8B<~;I?*Nb)P%}u%%geG)yEdh;Ptz<^(>-chM`c}s z=RANS!dwI)i9^3kwF6DF`@?nT&*6Wk1@a8gj_)-laG}jF0gy^NB6!h5V681J2*nu1 zj=?;|)Mz}*Y68zb(;BhO&uBLCt)1;sR8p*jv$0SFs~vlt#cExQno=8=E?ssnTvgXh znl%1rg~i2V0{%8Tsb>cY3nsC0j771E!s^Nb{R?XEELppD-97d7^;`N&H~q#BFSea} z@-gF2IO?!FT~_m`cqHtwGMlBx#sQFZ(%yROt?lY&88HE9;79!*5yyTL6YJBIl4oS& zUu1mvYZA8p21tJ1dG{UA{1z!ihLAqk>x|Ei7#;q-=RO3}pM3I(3?DvRyeO)w|DBUZ z_x=0Jecqk(P##&hF3l*^C|kB{5rRKGgs5M;&IIkwckS)ZvX40MSR0k^C3-FWzR*V) z6k{ScU&>C1nV?uWDbJg2_^0dibg;GE`R)jH?d^-|<@kIi31`yDm>6mH80tV7kBHbj zHUJ{jM+3{_^f~3oS(Bw5{#^}C?}5>cBC`#lu&h)-2e`UEj(xrzrEPfcnGKrVl<`3J zwI8lD|CRr%EszI5JHGQi{AoB|MQRsn7eLV}p$1uMqO>N~Hl~y7zDfJ|$)ui(AXYyY<>x~1f}bpEOV)4Q z+VwJc$S@h4b%Ev}uNyP!1O1Lz3>^q?;wOM203stYXU?21K*b4!V4+`WWOnmDs4yan z1|t*(@xIKTKVR0aUE2lx^fuHj1$Oi6_Ugy8byE3|YjbusG_Ev6)c0ijF#(WK)p>z- zj3kei7S=^`bYY!-@g(=sWMsX;VKW=fha%6Tht^2P!etU{sZ$|6Uy)D7jv6Y%Dt!|6 z*=5CsI$614tGdr*$rTHPmwsRV3s;avDIFr!|UKzRso11zWL`g!+*R9*| z?>m3<3x9v;&=>ylnyRmz@%b-K9y{g|YsOH5v|gJTSuv`;dYm9v_zZpB3h^^n{~Q!K z(Nho7Tj*$S>3nw9Zu*3zzMCKjKlv-cO$;gc^eyUaX_JoT7MVSB2C~42j}adQFw!g2 z=i*SUSFWEud$u&dPw16bUXjI%7b`sx0Rkrq{+^4dx~1qTBAv+Y{>T$A0!GmcR-3~!tu`U#-0SQO~N`}qGjsGvCh9_QeY1wi^Sy7&*i z(F;SAOS|sPeKZgc1`Fp#hwM3%$4O~Xk*r?7NkXBp)Ibl`m zEdZ`=3aa~K5fCxN+XCjCKl$v#^|AZuZ`T}q^SbiK&bNTx0(k(`OJ8{Mg%9lTJ{^ld zOgRPtXcQRv_Z;c;1;_Dddi!*7X%}kLf%Ll2He+WD|@FQ%*!H{kCx4->O zUVr^{wJOvnJzfh_Y87ZwOJwz1mI2zgOJjo73|6+(IM8>9gEzfic>j3sc~6%wTaFcM zy9^mT1kBC0Fkkl&PH)*<-9^57;Z%mcPC4Zi#RBPCAhuO^K(N2>^`82ET`10Ludc3^ zva&M8dhvRDW6<{0_wJSB02wD4|AP3J0_F^8!w$Dql6bZY9Vq_E)evG{44fuVAFGjl z3Vu;WI!4ndkVz4EI8<-f13&#J^wkB>)MAen?itEUNx9D}B@?_dar8)~O|;{B>~zA$ z^O3>66~!jhcXDW+Grw%haQJSSgZ6mcRXsn?E7bIw-mbLx{J1+{nKlIzhV8x zM=cgx0and!YJ{4b!9DM%?b|;-wy>f+UQ<|H?xya%8$uN#Agt@&B(%# z*+@RqdpWL0Xk3U1kKl*iKG$A*tr{2==%MLjSD1^o0#r?G13ddf56Q{Qj)9re(Fx_jQ#1Z>CyLN+l(7GPR5KGqr$3ssyGjP zu%*Q744HBX#&t97*WD68zusIJwEgr5bbT%1I<0#c`eMp_VNIosKjB!3H?>Ht!>_p> z6F@5hB`wu%pG-S!4r-?|n~}mHf+MY}I&;CgZ#;JY!9qMY05?3(+~^6-|93HT1>mY{ zm|Q4Nqe_4v>y-?T6@TLxr-NHA{5W0`{OI-a_>9qXfMI^Fd~p9UTx!LH^IN_J_Eihy z0Z{KPO-tVnWGRL8v}QFq(YqvD_82i7X#pmV3<4%;5PCfzqvQO;ZqLmW?CkuuI^vPfE zbN#uk`g(3U*zGP7-=EoDeLer4`>O&E0R4mn<&!4KsY4MCWtJne1+$@}Ect{dTU(?@{aM`yxJZX@O|0&$Z>(k_n3dE@E#kkh)PbhXbnhkMrY-Hai_~DDs zzYc)XkPRRS1dB!^-jA5T5a|WOcK6ogKc8=boLe9dfO=^vJkL=K&I_@6+lYaC6h`Z(26#AsB2n91i^(JGe^TkP4 zON0ffV6=hx?ROP4&%0<|VO51=U{!^CculoqSVfgM}ie=#FL0(19+#=1b+J#qkjXstq%T4RK8^D=`c;N?mC(5hzI~QtourXD)INmJ-iE z40?qa)O||pqXzD5NWfqK%cHWQyeix4OJDkuDjUJ})4YHBx7)o{6|Buj5UiaI8lQrK z0+qV|$6Icdm5Ub2b=Ti4haNT;S|Qv^szY~k)5Ab)7ZnyjJL%$Q)JNLQqxYYW>+F`w z?#o&N^u;%+|6Gao_?l5POqI3CAQYs+w`4srfdkkO)ld_oK&>Gg7bfEahh%Ql2C=4= zcBKBhX*q7%Irh+RZ+Fsq(!j?)gM{02hmTR+#?$Qy1w#sM&(?(1ha8olSKB zKpjS18`pnZB!6ZHTMOg?&<^e>hNor>*3V%w9FO12@$foTDlbAJZkJQ4kO8r3`BK?{ zRNnF9$4g^ftvvjv@U-xl~4p}@660UsqM?q#Fir#D!n zA6M6j>ZK{V4x!H<-RF{R3sbe*j*6!8qunLe>fZpa+F-6_@3}o5*|2Go+;`7k<(0=D zm+$}RC-TD|eh=&({D2VtqJG1~2w-;8kC#2f4z5+R!Rz{r3Lj7u3(>eTyQDTsE4hN0 z>3-}Pd-kihGd#qhshMMY!{IaMFY123a{kcX*pau4_YL?|2h0fw|IMgX2kD>#*8?TqD%HiS*XjY*e8f)xM+ zv&8BcjZ?)bL+`|-EgC>($8YGIkSY$5cYR}vTypi*vUtg2405R7g?K+103&GcbXEkt z?Rqt30c~I{m_XIe68|Lb@*S7hsWRq!_t&%;k0%RDBEqIw# z$*JvS)nNA&B~QJQ?pHz~c7o>#@KID&EDPRxQ|`L!PN_%Q{`Y?NQ@QTf*NO>)c8rd{ zxK2*uT*-3+h(q6@)r(`~12NQiRGit;395ad6^f!gnNP^dXa<%YSM@c(II&(_*GgNf zF~{SuX7)j|W$h#M>romS*54u<)a@dP6B2kYf%Zz^54?3r0>o_F_6Pn@5j*_9Y~==} zc?MWjK41#Z5D@6s#kya}cv8HN_W%4lStAySATlC2(!*a_XQ6&^+xhI6lzs_)oQpp` zML1=Z(T_e3Rv2R$LV5VxR_{T5<`da3E)v!;$ogn|^fh{G};6Mg_jXIEc#wY-XArWVvU zqAyU2-~bvZGxRfQOa>Hw<`PS1C#>JHW=w(p&;xl;#sU)@>fp2oWW$CAx%h&g%Bf%ayhO03>4Zm&#|x4hTF62l zQs$f1$c!UuFuBhbm#-1^ftDgKK7k_B4d!&T5ZOkRRr~OM%d^j2Ho2la_T`eo*b(Vi z=a5(?XwJ~;9!oJJCUg^g9}{qIOC>PlsPPCBJC?PDMO7H6%JFX$LCn}OIN|~^xS4@Q zP|6UW1#^S2(f#+{BM(0C59EcR_$mG~0JU%oWsPkkz>>E9)y)#|S(XY2$FVX@h?mk* z&^!suh(ii0q}YH=tTdPw)?4E$yBh#fZPtC_#AIsB+7)#D34c$)uAeE(y2eITvBtq{ zxQDv0Q&U4Tj0hLW+9k`SdG$tF249{@(oTwbS?*o(lZMdkS?o|5<8 zd|M8mcZ|$C@r171uLCum4mfxMU_0pHI_;r#3hjPkPBbBs;*J9h;>G+(BY?DCpo_OS zzwM(IP^$=LHfV)?^xMP*NIDKa3#?Zn8dm_Q1;vb`EZO7KG;})7)Ad2ycXHN#)qIlp z%qCN5doU6yPR647`Dh>A+WgmOEs*bUIrN0@eevk}JO2C9ljTJvr$wVdj}hUMCPdkD)! zj4@kSg0W(U)8-m*>BYabTz|tQdp+Xu{PUNOA5;_kj?2+}R4TP;w9yuX1e;%r!6*YZ zBMxK7gcG=@2nLQGWIzGZ#RABJ@`=4*D3}TYAf~(nJg`DLnHRztYCqEmA0g%c$)}!H zRC&^7Mips*1n2=&+K8%ntzuEyY^UDC|6Fc~fJ#qcK;>AQV##;B;uRiwVb#YPQMco#&zPJFLe%N7h z@$I+BWazqlFn;41cari4%3)R?LtO?mffafVo-adU5im)1tYj_F13h&2-{j&;E|N$p zCQII1B=U_9I+)v~zg(LgebT~*4*i~em@l2B%Bb)y z7J6Vj;}{6qg9v{qeoF&PnkcXv9jhMuZnQ1_$!s_^41j7*XAEU-A65zZE#CtBum$n} zD96T5MUz{a>Y6?*D=V&Wc?v8U#Ih$%o+iV}Q4Rw_m{rR@kvHFaMOp&@JWaJSWKg*j z7uaQBRVl(hA$SIvRd!fWY^$WB4HB^-(@Ki zV~m;FQISkD&8V^ZLij1=27rOCCv6v(+oLL;$B_c=50QCsh}Sn#OpbvVsS2Rc;lKv( zBSva4BF4amSlmWw^@pU$>yTqkIv!yz=o?g~k5PGjuxfM)+8Tyfx!Diwys|% z^_x)^gV|1`$iV_ReFZXM+ElpehUK}(|Eq<$vcN};sk~p?7MR=ao`%M{R-QT*f}#gR z6~GD3gCK`LIcN_bHBv^69U~T2zDDcMb;7=%^RDnx+0P%X6^`i_d z0d!)}I7D$jU7-5NC}mLak{?`*OtBa&{s8Vk5x?;*^2S?lpz?JPZD*AbwNGfT+Aa06 zxv2?yD2r@BcHG*{n-l=jH-fz^ztrztVsdSA zEufMPsPn_YXjIm{{f%IK_{f={gKR`~_xcX>b7&ybU2g8#4|G3~ZXlo58C>j8=dUuy z5QeWTDlOa-=bb-Bz6Ek^fjj`pp{4GA=p|Dm5;ARWZP`-ja!1;NVM_tn0TO6IxE+7I z;2nAAgV)63g6|J%9Wh+<>{BnuSI_t&$|jH|HiH$KCH}gP#A$6tD5zDLD%uQIR83D8 zn=KgvAl3rgE8D*x`IV`%y!FsRZ~F;}$fl7tYbV6YCJDsd044*#>5LMuYnZqT5QK6? zz--h@uweztL8Rf;F%;tGX?U+3Rwn2!S-pb_NpXdU0g%rBE)Pn6q~mhIk1mjN&N&-; z8Z6zZc>qH~q{OjL)kVWbK{PYMTAG@r0a;>nSayO5g2#~f0KSHd9IggM14qX!_|boM zML>FjogIWm82-v|rHb$s4GonS2{b!Y-E?Oa^m5czgtR!x(&C>LPFT&G%nw$mS}H#- zAq77t2S88=JHqV==gJyT_?{R5BkMw8%5VX6`J+LxyY12%?3BTUgRlp~2oQWDEXR-^ z+s=>hW6wE)$D#n0EAWGU)2~qvfuT)dFeTo28b32I;6l5yu3kR&wa>ePl4xmBNc3o3 z0mNR6|ImlyF=Q+P@N8`lN*#QE3>GY;xKhyXW%ilO%6_)JkK-}p(FzY(hTm2FyLiYG z^EzrCd-=7GzjVxzdv!6m&l@uTh5fb#@&ITTx8~~W|61JH(rkO~xo1WvVo~>q!GjH@ z$PkLMrWcJ7v5Kh~QYK4QS&;=15<9$O0&qm_2qa|L;zl`k-l15{+6A9QmJ_}#h-wyw z4eHKPZ=IfFp{) zQ(2%BLugDed4N-r6coPkR5s9+_52|bru0~BJ3Z#dbJD-R7nZAQ!qNBZ*#^0MM!)X9 zd{1wm>FpeLc0T&e?*e;d0#QebIZ_F<71Iz2oK+}>cZYP4-k-W|?7`bUeYw`|_ilIk zg8rSj6--j#6OM{)_=!}19v9*6Owyz|+vAo5F)k!H4xkUy6jCXM^hceyD7*0K-VK>Q z$(n&EhzRpUJHS-X0U%TgmX&5nc>wh3Q8E9{UTA?l0NP2To_EnT6|0sn8u`jgFHWB` zckZyCUwPS)<>h6bHVB=~U?(C7YlNbH88&i|Ol%k{Z@;?$f>(%QO&*y%dAb}vcb`%H$QsyM&5HjmFgBW-9OYcwT_l5vhN zB`8KYMy%es7=~tHxT)5J76|7=BPpqCZiR*=gIZ@5;wc!7fo~wv{ZY`-1yF;*j_I$Y zz`JH5YJHILrp(>c$d~0)^iiDn!qILD5_Q7^|ZmRz#245SvzmQb%Jsop~-- zcYy4J>$Rbe*^v833JkzngV@_(>;ZBi2mEQ0=^-@@^&3%-_FNN)GOK|L1Ug`&TL)3+ zkeQuk03(K*w;|LWHvyl?1ejq1`ndtDD|CG}6e4y56y^K`9`FsEzkvPT0{PCigIjRk zC07nvw`%zji{5+xbLaiw2aYeFamI||!lIIJObV? zL0PtR6$)j3P6pLXfF5MMELpNnLdXJ|IcFRfPGYG%HWm&Qq?$XHJGV3kqgy^%+TwOj zq8HF!*`9go`~lvg)m}0Cs!`z30U1yWjn*BSRwIit_&Xz4YO$WBPk3cJVj;4B z+mT_RY80JF;xV!$K-c>_JGO=?G;_mTHlD7o^4t{@!igY7^*1#@3CSR&$omV!BoxHe z$ciGTMH*MEkR%+#6Kmrnmj}G^5a4oA$(aU=<=Q%_5t&NO;9cF%=ae6v+vflO^Ms z*}l}hU-b1_4yp3>{jImC`UWy%ikdjZoWa@p8vWOINYkry%XRkjg?;-U?2s=11}G? zK(yd*4itn{*;=~-64+(C3@Cm?XKcC+{@W##mwLWW7%Hbn|F|!I|-CIDQFbsUg&3Lzyinw0A#1hC;D&3;*^L7Qh@Sm*$N-6L`kv0E={pI zJvaJf_1uzxJ#pA;P(5dYL8u`awuY^S;psqUA@&6V&Tsh^*!wMz2S7dA_@rmQH`06d`MfwMZUVKT}z#pG? zUIw|LU1P-FEecJ$2ikdgdypr=V_9#t8Ph~DILk9(5EfSmS zqy+vbg^-e#xUf$kDurwx^eJWqmO<=Wfn{}h7?V7X83mXMFewiI(MWP<39@G)#di+BR%``uKG`}_ih(`ZdZL_yL;T@`T9)fLq`f2QYzX9 zFBpcYK5K@iQLJv+0wA<0Tq|2TJERU^Dd4fg9^}&uP>)Af^))~#+K#4P`SX{N8(V)R7aww8R0Ph8SyWH^`J}hYh>%C-K-Vx8DBwxt`e#-}}X7 zO?Te-n>~^dX0_FgbvRq*WioB3>6Jt^2D8}gC}0XV=43R4fs8Y4lZ0Yhq^@N%1~sn? zD4YRyVhq@g0x?7|ba6udc%dx+?^EI^A!K40C#W&h#DVcjM+hp*&ygO!&k}9(g*l23g06OGhq-;aAKl& zVUoAuCxKCl=5UyOyE9`uJ0E_=Od%?DX<$sEU?#10C$wl53AME&lM0g-v`J>%pCkZ9 z*lH7VQL$oHcnz5lR^aqvf;ih$?2&{O=ZNz*cSgkpR2!w%qs)u!wiM(!fD2&v;_!S5 zqx1nkUxeo%1;3|YCN9pZGQ|Rz+mIxH3`s|!TcR$_94}ViCdGh;G5)=(NSZ7VJ0o*2 z*$LNgoShXnfc4dktQ&`O?F5x(K}{)(!>4&mFtNZ2SO?US76y(p`wt0=!Ebe*N^Y%K>J8XSX^i$l)OO+4p=u7 z`p^aziLhh?+SZ2m?f5;>0Z%O0Ux4>TK!_zBw(nLux<@hHK&_WumS6_9V=~vfWp5s%S)1#f=Dc=IUwm=>LWt-)M zQ@%28^~w+CTzS#YPXGLg$4{csS6gi}!dD;4(q$`@XU^9826^s<7v=OXe;I~>F#l?5 zld_^x=>Qwl(bg)fSFM(Mc)ZLz;&3nzNeGRLW$vsQ^6D!u$&kT=q^YGrK78Zfa`@qg zq0~ei0$5PU(TwrmSuS&vbz0Tt{!a9u4JxTZ?=u&GE;s)nPrS=@}{AX81&waD^29}>&v zR&mn|5EBZfS0xsP3qA(AZJAXRNvah#|KqB>D^`)))%3Pf{M09N+Ns;8x`kJAI*V=* zmYwa-2|aGdgo&nzK>uS#PJETwB}H(^4&sARG_wn6&1%Gi9}6OcgSto9zZ2y>+Hnm5 zFdUpz+Ce=;Fbk$IL15Ydi-xd+sxXV1VXCgut~ZA6yZ|P(sKwUqEtHz$=gF8u4iz_6 z>5KmLgfzbMo_Ls{1Hc(ZG6^7s?-_mRkEE#p48p6=%F^dvk`i<` z3mQ%_ikK4P=xeR;PZ~6Hk{o*asR+4PWz)+q$@<5hmXfeRO3?=!Sz={$g^WArOJV>s zyXuv9%47O(0Q&bQHt>;P5Fj{-{G_=oskV0CLv8_mTLa z)!X#U^Wp=5v<<3}8Q(ff((tN^!O|ib!E*|h$cd6AZ~gfW@h@2_MW};kz(h}m?O@*M zz-Ohcu+xBAI3q`nlqD#suyNxi8C)|E1p#AHR9FZ=G*4cA`Bj9)ra;@UQkojKNKMTU zm?O4JNjYk5r3|i`ni^+QORJ*;tIR#Im6etcC@D6L^>_H)E=M7NjT^P4P>LaiK`b3Y zR4W4Tvr(EFTBNjkoD?7{EM+Xh00qG_(G6qyU=tQS@`%)8(09SfSd}Hs0ty9m@H;K- zx#=Mc?eAoOG$?eiZmek(!|FAL>x^JBBzvQAJK1_;l&f&%462B2#86Kf<2t>K2y0YQ zG5?Oql&n(%3Q&GBj^i=4SUKe8IKjs8IX`Y!5^M{@5EH*}l9WuG1caKAnGU-=xcDPf z>P9FDlNZe$ZCPY$HcP@~TcB^=XqhRhK6HzS1C>uWbM3#LR zletreiR0+$VrglTrn}v;aBaI33@(+ahfI~haC^>dUM-s)?}(udb=j2I436aJeog3~ zCKvie+Jm)&aNEdd1)OZ{$q$!5v3zX3ToumrMstU4r5)=cZ9aD+i)gSX) zz6JJ83*-S1+W9v(-)H~FgMam|TffOLZu~emCktPRPsX4e!UmKqsI9LSvME~Qv~hAK+slZ7A4%o#J}tpy9jb?hBj-I)SX{E(?9cD+_`Nk8XsI$AYRYW3jMWX7 zp$NiR9qsTa3i^?X--r?regLAR3>rR5oSrIp_&6~PQMsam5STHNviart@(CC)niQoG z8o+$owdwGtJ0Q}>Jr07HUbI2*VBNCv8OT8u2x3N{MO8P~j1)Nw5Wr+OfnZ=aBP2tk zNM_O?AcU}7QiT+#O(By&5X9DE_>0UC|3#HdE*p!ohFRat?UT|$gQa-T5RJV9`(zu6 zqP>$QNFzXw70eP#c61;GKZ!6`oZ-8QLYerrFUaaU?v`PnKSq-9YG@0$NCaT51G*0z z&fDrBE5*E>l)U=hd|CMFJMz6B{y-{G3^-HnlR*kOX!E|BOkK;Lk_bqi1aYPK_1B;M>VAhNfn;jzCl5&8WaT7>0 zg1HKbXY5ERJ#40|fAn=3sq?Upf&484VO0N$S_Xy7b^( zIrX=j{6Bs^c>shCaM?xYM9=@p)gRvd*ZbC1S6A0epE$9w6#${Ksz#1E_BeU^*{5X# zfJzlsnJ1wtxB+zV3(q|#vuDo|kJB#g&Gk}UULp-8MY3}FQe=S*lliZ_DyN@znkt>; zbUJ0k$Pv=mubZtw)T{NTGQi1`B#ZWB>)!?P3 z@U*;z)iz5x&=omKEeomc9zzpXglfya-$hzzdGH7Enh258Z=4+;bk_^1^&W*4o7eQp4VSvGc*FxlJ^ zl%u|XoESWX^4@~i#D{FX(PJmclzB(V^KZWojZ9Fg;9wr4jt9qvCLST!PAQ*0QBM5c z+2R;njrw$PaXU-#w{~cJ5H>Tp#T8D98vr1a2+N@}=E$@mg@kdIcz=2>35)^F6R>Id8K^Ih1Qi#I?Bt zA_iz~1>de%O04U);(1sk-iAi;U?Oh@AdDX5ks4+BlLa{J zzkHmP`vd#3(gkh%$(WU@g26tldasr_W&{jPrI^%(VGV_i<1si!1DIPd*03^TyW_P zMUD0ACS88%e69ds#D-Xd@;xMx zMBv2iF91kW_qs|uxKOq-r~f8^lY()hrQ(p;VDl2Px?!_80g6UB(RZhg6VHgj()!^i z03ty^00b)lRx&8Xv3ApX89QvGOg;J-3E>#aKKNMX9(JTypjC2WqIYAq^F#pbFnML5 zW3s^bGuYZLaXc3eusM?f5|+QRSjv$4W((ou!w_^^0cBti8Eo?-GZMPiBv$??jZy%Q zI~#;!R#FQ@U-%irb5;+Qh_t5z=N&=Ftpi~}Z^>Y(o-;%B*o_-EN^47t44X7wh9lu% z^voI3_~s`llNF&LAGYZ3F&C+C>JUq5PI_fqm2hc>t7cXoR9#1`iwY?3=I8 zA9mBtzbm}*ipvLvF<{!^;yZEjBpE+(0+lTiM26S;^=oCyEh#Ym)aglGG^=)$~ zk|;$+na}I-Q9s+~?f)*k+P!w&+M$pB{qA$x+Uie=heCs(Po!kW1}R0CcOU~?N?phi z4Iv35()#bZ_c0kzHD11d-j5(u)U=0bgdi|-$w-9pHi0=<^YUv_1r`ezscM)}AfCkP z+YJpy9N9liMOJW!gCzA*n7mo$;o#@bVjWn8&!r_?SBtDFWX(Z~<1*m{8~_@qhi0EV zOop8Gd2zW4W%HtCvU1^KfENJ8(SyWMSs>|1hn#WFH&mz1pnXlqwwpL>uDtx_5~Z1P zA^c}SxD18YWbu>FOEI3mX=i=`020L(aK4o8rV;LILaKE`!$v`=T^W1AJTPx@sa?57 zCP4QTczdCI{NE=qc{}lZV6_0|A`NHnRU4Me+wZ(9ZUCQ=X@}IuVg2Alh|iC}C&F?T zgc1+tO%tSR{gN()e74QFZWEq=8^W0}^jQxw3N2vXEMzdzM-s{vp176mk4!>ShkW!;f`Wq2GzK2b_aT36d#?rZ z04UqGfBoatnX}KkYQ^|TQ}3Ps#vA4V_y6sP?|=XM!y<6@jYY!Nw9zl;@98wTUobp zgM8!cvs1?&dy+K}h%|veh!yw>BWu@g3HS9@fAw1z4*B@~cMn;%Y{?HJfx0R9JnyLq z3s}GqZH3yU*HHinlruA(_$G>XhLN#z(~Wo!RJid9lZ5hTVp2aL~1VRy82sI+)$oRsDz3xG(CYvj+t41oYtz>cJ7+=m9GhmRRr zlu?+-PZ>Q?BFOkEgI`J+!a7@dC$OIz0GYMYju<#bn!^b=Z5K*8!ipB4R82QTFCF|_ zS=8{qPaDYf>>p~b0bun)<5B^g8LHAkq`VHc1`Y6>vPgAhx%i3(N)6aa+rOVcR#S)A z0fNW?A+1jgo9dZ0~ zNHJ1ahU;4Y$s*}M7|rQ4!5U(Xl%gEW&{I#7>Unde`iSXTr$D;=+poVOwXKa(21CdU z($NuD1?6It5&)&6rl8iGxkTi#f60fdmOxixlo7}%Gf*=G=kZ`uD|QFaDJ>93MWqaw zJWYlUsF7tboh$<2%7Cdg00fo6i|dX1{w~!6E2QGs*=QNorb4JM7M2i1+KZQdB(CX0 zq-OY78TQpvWf(n+koxbiq@?rRm!*A69RMm=BIuxuXuBzMXUL>kQ;_y=fG!ET6`aeK zH{M5y800}h_+rl1xa#XOd!X#ERkc%27^==6A7^G9TER?`5pDr9Y_dSNg|Ma50|7qT zngVs!2+L5;0|0C>c1VFQg#BV^tId>5#M?Wp=8_CtIUp-Ep)+L62@F+da7d&)1~B|I_O}o;YRt{f|8QXyeRTv%flK?3f`wc&3EG z{Fqr)9242}DU+qGrBUj)ZjqVOCm^E=gRr8C8R~%E1OKS3D3=Ooh&nri;)T{=39`v< zxamd=8h*L>$}3{Sh7Pv`LJ49{Fi@Nw`KjA zcnhcnRZdxsS>X;!Z7HNx1a=k(sYtzIvS{F#g4V%`mF^a-&M&|G3aJ@7SjJ2mgH;^S z6Vlx=IoL*zlo?++UEaI(4^UJXB?uh}Q=p3>7_}e+hYrx(6!Vh>LDUB@iqO|I`2GJl zwY$;w~!#sDMBkp;Rl_ zIPho+{Ah04>`5A{w#yA)%{1*P#e*cgWQ%0hx61OTpOPg2Bbz&$OFB2=kSs^quY1H`G z^3EcOHq@(BX9IdIW-v+l@mXI{Nx*}R6@&8OaT?_a+@Z0MkZb}%}ucbQDU%nk;Coa|nB{y7;s zs0s`}21!m?bWK*(kTD=2yfl2|7>J0Y^3J>O%T2fZP8KfuNWT94@3sH%M;F@t{s0CK z2a!(DNyDII{;EOy}+2XAOgd(6@_8yb_~+yLyaVA zf-pyTpdWd&ZY@GnCYj?w+BS7BrUC#&wQr5;Q}DR9T4Z!_DxnaVYPc0_b}D00wKIVQDFZQ6h9VRbWp% z4MB0>v#qJ17%F@c2IJ#`pxFmUM;A0U%>_1D2d!HXjO?7qC`@943xgUM04AUm281r$ z^R14AKu(MhoJ~wl^eO0BnGcvoUoXM;Qx^oQ7ij{B_93BQ3u@}6z%n|)?%1IkH$iO7 zkTcV(S8ZA+!-fn)ND#mfMvX-fNjH_+WL2_LssYxFaMw;EUB8xXhj49i6j@|kPT;|8 zaT%lzSzru5PUtktkc2}bsN6$Vj}lyY2`8LV6|77}Mk zq13g*8w(7S1O7%y2%S3r znq`>Sx{g$#>b`8@d^S_P|M3l4j=l{v13QRdi}MROlcE4(Y zJOk9r?fBCzmnXk};V(a2w_)wD>#o24*()x+^o+`iVoM5CKH&sw5+&jhWKm6*Rum6a zRnV38MN9pr1UQY!an0)0^2C$R$UXNzD9x=c)GuTjmN8_l9uTXideP)j;vsL zVs!L{?%}x?pOY(pb%k7g-EYJOaO4NDbE2}R5#hfhzju~A*H$k}K3M{Rw-0Ic&_lrB z&+BAeusIEWJuAy5tgi04Az#3-Dkix8HI zBP>}2eTxzL9$%45)&oeSJvIp=M70K?q$+2Dw4z)`z+;73V;Y}}!OIF6TsTMydNp^U zU4eqe6z(syMKOT=Qmo1S$mXJ`%zIk6+rn;Mt5`DMh-y1p;Ujv|CWJz?3v9Db#YafZg%o`cnQd!f60M2>C^j-Nv#V zl&!~6ytY1xa1JJa_HSAo9LyVQ(@{&szMRN1Kx~N^7E5ig(`ZAV3Bt-Fh;}xYK>8nJ zh#WdEfNC`g;f5vi21z5p7MBoeSITD(Z06U4r??PLybqX4`H(o(7@H- z_`x-XzyIZ`osOy}eCfNMcV2t-^Zxe6lTbm~ZH5U9St+JMGiah>^(qENN=AuEv`x*F z4CeEeYhRJZ=alIo_Tx_@0MXz7{!ej~7RoQLxDM&VVPyixFo1Q~Aes5CGo^3|D#SD5 zgT}ym9+M3wt}M>l=6y&8=-^(&5pKd$KwW1k;H(8ENiGP^Ip-HTp+IM9Iy5O3up5o* zt0aMa#-L1u85RVnA_nlp%q0S+2>uP>FeY^EKH+|#WPo`JWO2|v>HVKK<@U4Q~_uuOPu1zMCOV^uLSnW!RRUWZ!v^q5Lgrg zG%^KXjf^1^uE{Ec0hWDfml(jHSmAFK@z!dX!D|Tp+=lB)NANs>Ng@N1L>rjNN}-=;@V6*5M^?0Dib$_# zEsXv*zVFr<8Cm_Euow3)1r~_I2UdY78^XG5CW!B?;&&Iy{HI@#O)zjZz<7WX0k$Oq z>|#d$8#J)oZZ^XM2%y|#P8lp#1j&pk_;RNq&&nWq47|C?6iLK04KBAUFM!^yJC^@& zUt1s#fOgVi_uqA6-D%&rVE%ufdT#RUS#zt7KlZ4qIE2G7(7<+!52^47ANk6p0V`m} zz&jx*E-kB&W@rWOzUN+f^^FBER4kQHG>8j$2Y&|$Q1_p00f2+FdbI+e#WgiF3=#2tih9@{ z06?0P9?z8mWY9U(kWq}Ro8sX+{LPILd-Y{G`g13W3mKGZU&UbZyKpYlR`Ggy{%kjX zC+`KT@$z^GziREu&tQLqxPavGZ(bw&F20x7pw5M-&(A60igu3tKIK-N{a*eKW&@%k z^~`MB4sr!Sb{J95x@sR>JFg$f9BL!=2=M%DZM&>^{$(-SV0=h7XDqTZiB@qq5Gpi< zEa9{l-6#=`m{23b?#EyQcxkZ0{lVa1&9-IJM&w<1@>JFpY zmJEk{RE5m4g|@SrpB{|Cr{>RY07!jUyLc9b?5@6!AOvvng+G%4|EQ1&$Sg8qqEY}0 z8wHD~dZ>QqI|um`(6=X%cQ3oCDHse0U2oelQCN{JBUVi2pW61|BZ%)(8$vbOrroFQ zwr=OR-TSCg@TrY>qh}%-|J`*A;&pw?zS(ns2#A#xbk8k&imF!;bYwU7rx-$10cdg( z2DBMoX=sgDR|M@cXWkd%3jhRzeQ~D^5Nt7^#)w)h#o}Qcg_8b~V$W)u z&6Ec~`Hr_wTi{?{r|))S-us8Enr6(EXjC`qAP<*(=C;z?satz>u|ay9b^qIE{-XQ`KT`|nERg!J_79b2$}C7c{~U$P zSQIe?Plhf97S4b$Wh&{CVT0$$#L*`s>^mIf+c1zk17n#)NhBV1xY8-B$!IiZ-~p6K zrIO7hB_$j1!8`!^OgAb2q0ia^c>t6{%QZGOHGKEG-~F}A?cVyk+ipvdF?S%Nr>?O< zDh3RYcR%<@etzlY5J%c%8ft;XLHW~FIi@W7jTy5UVg+aeIeH*Ip7Q(s84FgKw1Q{a zJT_SC*u8k|lny|c4U!?4wCsA#o5NN0z6-z^OzE)d#^CnaYp=;=mtCfe$_UgL=Hp6S ztKp-8xBQlGfqV=6Z?*u#;~WB*g{K%g{sTHltnc7)>6G%4iSUP-DFZ8yPY$U%(LSj1 zC}(-`gnGNBJZUyNkw~ciV>!M0p+kqZ-29tsa}&M&Z#H-Ss`o+*)D7oc#x5m zLU2RCLx9FX?ZF2hlv{4O1zBGa1$_8k3Yrb>li%_!kZ*zi%@$Bx5XJ+_!niPEOe;en zKdcvG5XPHP4#nrG#9mGarpKFI_Q|%Qf(c%)XGlppV=0O!5=9W0gaXSo;?_^6RQ)YmVQnk$5OCX4Y8Nm*&xeM(4?svbFF=NKaDW{y$6%Nzv z>$R0%&rJtP`|pnBzd7Fm`4-r#Euf4*8N;XUTq((s*~m0VERG;j_n{7*w*W&GHS|fW z*^CZ{8RahQcHh{H5eA6Hgxv}w5F8~QiNqjVLR}!Eou|rU?ZhQ5HlX0+&M$IYfoWz*coJLV8Gy`}1!ENBo@tse71AXN~mz$ujYG zGD_T^`||s>@F1(x!x4G!NS*rLF!@j9TcDQ~VE@$tzus5bcXh3t-v7A|p9ekz`tLlP z9#YO$Lq8zem?eQSScB;tG&Ml#{7oo50>7yYvSc&npv;^zn&sZG(WML)31+wwm<+{% zXq%&?tRfYTWGs6 z+%tai1n72<@qogQG5DC6SPu#`u*K#GB+~Hr4aegXCroZ0KW^eHA20gg%T}ASAsPyL zQ;DP{5b%dNK+-QGyHQn02j87CKp140@4^&}y&{0_*%UfZ;roq^jq;P9{6zlsuYbv~ zVZ#(?(YKx;h2V%A!Hf=QI1ywm@4J8w_tC#Y2VN9mbK~*z8lxOKXMUIc(gNK&vF0r;lkn*<9K9_t7PC4ssg|~Sm(A+Vp!jLT z?e=u0Q<{TWe#^JOzHEU!0Lr1Y4Mu~>Y-3qPBr>EDSk$DWr4{843%pJ!cIHG4Pm2r~ z-AH-mfDgt`n6$>}_B4I-%g02o_|+fZ+giJ&JeiD@1=`vwGl{6VrM0z913;Q@hBgRh z3|^0?0H?EQC#OV!M4ixn(QbYG@yBw>C6^%E?G9DpULVYZ!JyRD)yan+ekjj9_na(R zv`DRhb!Nv4h71Fp0#3&sd#uczJ6DkPp)OPD2+#&PqeLLY&oWCczvWw?zqUYk=7#5} zhl6#n!=E$>ZE0zdHEY($JMX-so{vqNHg$!OyECTU7Zm;Hnz`Q6^@d#V0He^(iGYWu zmay|E^ns8@>U$E4K)cf?ag-!UVKMmL`>)H<#~v;)YDMOU=BHqHc_J`_^_d}cbT}Qc zbjsvF(uH@$%9XR0uUP&Zc53fG*PcI<|AiLF1E3u4GK%!U`of+Jh5b>MYbq!%bj2~a zg`*L8xxp1SN%J*>-(<1X4I4S?-ob;0z5C0H&fdyHUwichTQ0r)=0{hrU0G9CTRSe; zUOTFzy&WMiP{njdg`pb;OJtO$4Zebc3VdYoE&$N3Tu*YiqQ39K!J46|#~yo3h7TVu zS6+FgY}~j}{`=qm$_p>NAaA_!hGKhmw(;|K-g&2#mzT?Fr=2FJo_eYRLpsPqpOwcSe_YnDUysbFZ4wvucSd$uLgYZ* z_-tyRZH-h{43(nN3a~#$u{cVxID$ibz%MJ8Ezjm^5Jd4=Haon%nT7V31l zoTXrezyQG-9_0mK9@`!WhSs*Xb-Z!w)#o>LKkW6_UDR~;w}0yOxjbuw?J_Zi!O&v0 z#?zUw)xzrWsWj^1q%&@pt85piqmLx}Bm~y|Ty$VUAawWLcdM1{;>C;Q0~AQ*rl-m4 znjXHV>w5I#aAF|HX=rGWKmYm9D#MIGi29;gvu3H?R5=1QLG1HO%%LabcgeTFwieLY zOTMFruvV;CAvfQAvjRVDZEdQ*cV~yVFBb~z<6IEvnjrnC%%%bx+)tXhCj-QXdRzS6 z1mD&S>aVF zHnSydHJf|0;_N5wkUx!swFUA3D91bd(#!J~noQ{nO{UDOw-(Glrlin&*z8#|D(y~h zw4kKA6^_tMcmH=!@S`8zY+CW|7W zp`hG<|NZj9i!aEfms~36o_j8QL!4c@p`J~l>w@y&XxE)2zsKHf0hsrJ0nw5HxG`zq z;fEiVOW~W<($cJ+1J)Cx%Q~NnZkCwJ=P`j56GH0BES1lSUcRRPfS(<=9}+^838Dp# z@FaZ+{x~)`Q79Ub>=g&8Dv`YuYs8^{1a^`}yrfL;iB?iKqU1 z$lzgHc67M3*%~s~98m}e%}YM|sPf8R{9;w*z!8qgGv=C0s|K|e7CVEfw0TVqr?X!G z5JxnwT>0hH{OF*DDX@(BtBhC-j20A*%wTX&p+Yx?JT#YO<$8JD%&Jx51=1_Qzm0a zsXB*+Wrb9O51E4-OIE)LMQSrDFGQhM6<+9_je|Nh)X`9v7^oQ#%*s+%d zmOmKakn51P*Vfhz+EV*u#%L?7J*K|(^%3KyybXciDy!ACsE?LPItw)7_Ae%&)^_IU&3zcAkd?Au^&$YO3-zdl=yy{8?ehAK$cNGA> z0x@7o*su8<5YeFGmHHiqr+60Zcs9U@67=wYbXCVb{2STPgG&CU%7$qrGq}aE;Av~? zz<3c*HE@|$?{wOWArwFQ!w)|il<#Z#7TA|9kOx40Z*4Aa82DFd4QerHG$!!oE;(xm zpF^w-hNs?wLBWB+GLFf8`2)Ydy!P>XuHAVGm3E$79cA-T`+nlP^NAfHFRo+}zgOw5 zN&5aEvjuu?DS#8K%kIExPfKS{LKi^rqSX&Vwg@WBbLGn|0q0nT4)j#uXP$XRmEX_} z6?FqeYL9dvv^&?5|7^YmcBchYIT(a`uD$kJdFY{slx~fEQ`O32rZ`@9us&83LqmAn z84_l=2%(@v9Hp=DZ=csAZl_DFqzD4inFKP$B5E-K!t~(UR24)rUn&qNp=d~&S{fB2 zBw&rwnUtr!qxFzC-&}Cwy?^@~^6K+jz6JJ83*cL~ zPLV@r&k;A``WAp6?R`^wW?i&wY$u((vDL9{r(@gh*tR-W$7aX2ZQHi_#yR=+x!=EF zue)`-<{HnKqpC&~kJ`}Z6p|()L(;LHr%<;0k>#qEcBHDKPERH%E4k{esvMDt_k*)x z@-{%gwrLALqHF?CcxYy&_rf!- zxY+~WEmVmyF7qt9VSGQ{vo8!uUKC+aQ9ADs67zp>x+Ee{h{?t=Tu{4}@Q3n#p9Q?n zK)+mDEs#FRpmc-{+veBNXoGNUV;Rcq|6C^2)yumN8wFZj65jQ^svtda2$&}ZKRpl! zUQHk9eKNi6dfuf`p`%HmQ^C8dg+~$BM37miVw+>n& zH4GyMhir*cSA@#qf#>-cXwXhhpq}`OBjVF9f81$xRK*j6LzM|75K&>kxrFopNcKhR zyqbz5u2_=f1Ez~g4uPx6bh+(m=o9GAHH2Gk+dki+(jkF;zGp&a?10|5xwn}f z^xf>c1jm;T+2%1ZfzQ&3Y%cf<1tp`;TryGver)0XdqbhUM9@`}&ti6mK)R1T^)9mJ zP4Du|`^oyn)ALIxhWLHTiCwy8^R(-PiRkfMf ztDMJOXhM0PllT&N-~!(SA>}q0&o^-ga^|(Yv%kS7om0=aYSc&;$G*julrJ}mlaKD)pAA;%)?@hbjh-}iUK;b{muY(ijf`1_feIGyux%RaK!y|QvaU2P_Jk@aZR8o0>X6KeIQ z29DmypiVGhO~!$i?(U)=YA-@u_vhOR2np!i#GRoDdJ0O_WYcX%wB$X&M{YwOi|Y5P z?6+zkUW3FT!bN=6VjHxOFj6g9CfJQ zD+E3difn&QvITv*P5;9+cIq1i(RQ(I5oR6NJ4{(bKeEz0%`w4GyVMPNNy8@jWV)QD z+y=H+$dDM|pn|{~b4`M^P%PqdX_TXWESBPb??;*ZOPz*T&DEg&`7JAQ+>hoc^YtUr z6gXMD#3tB24Gonf0u98@g-Rr6bXuzDj>6-*D;6Yr;Fq>e#l_8t%@*+cRE&Lp(_E66 z_-{v>=Kr_pr7yedUpwVxQ|8sc~N5h5m0hJl$Tc zT>IQ_7ay0`+S{)dAn`5Mox2FXivXg0geYW2i1lQt@c6&Xuyv2R+H2kFcw`AA2<-h< zMDcYS7FFH6ja(^Oksb!T4`~tys7I`btHY!z-=o_o5*Vdr(W`W^@YX_Q(g1zNGAYgd zi5P>x0C1>?@g71rB3d&~Kba4rstK5&A%FMFMn7RLoNb|hu10(qm>$T-ONA#&$|n*7W#%>`f6P5DlD7Vz0#EMz&>Ha zAP3`~8sb<9a5u><{WBAgZEE7(P!)*s^5GrSzq_Q;B0F@*VI)Q*TTE4EvYl7V{Rz)x z+jF13uA3cuXjo+?8Ty`GB*g>kr4Z9V?5o<2j?A@EELmM9$LDbK{@szM3c(r+*_Sp# zdi|#@SUXzp45e8sMVJdW2o+3?Z|nWr8Rvo;3y6{+v!wMaEB`_#rL~rnATD0m{#^qx z4_D2TON4$)r{c<2)R-Ys`W!!15`wN7nZHHu>8En?21A4U#dc6>_>PqEz0AH!*S+tu zQ?5k@c`CH1-*k&(JnqO3_6aRb$Ykx6_>a8y8#>LazJ(T9`cYaG90>Z80?9niiT4hA z?^s-;J57D`7cRHJb7e~;48Z%|PH6pnNtv}wWJG$XbbKTNOEWt68@`k~Hh+ij7BAc+ z)=GyGE(aL+)Evv%CG$iJRqYXnROP1e-$}X8$$@Me(9imQ5}))V8(fz>JiS>wX&8Bs z=UK*{%RWpw%j(X&)YWZS-bz**UnYHu1zIdNwn+twY1H-RNhx)u+rZM&S zdl5rJ5u?_Kn;~ zPcA9~oE{(}uaH2?`qf)Nu6=iWCl?DC5igDaJ$pg`O#{h;V0M_$k}>g$#9qt&SvwXYe~l0 zOeZ}fIzm!tIN7!jS72U+xYj!{!TZbb6%r!0$3xg|GF0YvGAWNWUX&@s=82t1U6-6R z#$+|eJxtqSP^PYm|-iGoxe!O@{fU)7v zvcaDxy!F-$zI?N5&EKfY+u% zQG`EZ;>Qj_B0@DV2|QE^->5KKuP-gdOfYo4n(ZeO{3>;nVfaS@6)=;7JR0WCNrHkE z);k1k%1=&zG*EKSbFg#x=@=+voYR4wIiJ_xGiOb*^AwpUHb??}Sq%BudtfA&J@Ld~ zxk$@L+MbWJ9`47*Xg0Mqd+mN)M48eo+BcwfGKh~5DeGl-_Z8;GoEx={yZa$ z-10m==nL$Va$%D#VQ}}kWlQk|{s!($uOkiIs}H~Lk>n!ecJX;x-q=q-+ZC-vUsSOo z8yk3`!5pl|+D0vku@DQ@D_Gckhb7^V{LrMLAt+YBnwCGlyIT=@T`Mc7W zc7^f*2VYRyA|f(7K=X5R1Sw_74(w@GCBIOkN*_WYp#_ckPElM83rZ7C*=yikOvGz_^ZbFtQuJ)7&R z-IjrLb8tcS5?KENnWv2&XipgNI*OHY?T=d@D@sN?lpM_x!i~Kx)rE!Mm&J$QalUC- zoEP*FJ~m z#3)g%L?;)k$zy(~a{79uwwx945UbJXxp**VEWPD>ISKiAoWsY3zS-dWo99e4jN*Sv z!>PW72TE!Ecl=;{GUxyjY>3G3gw7x47qAl~EN5v@i1bx%o?tX+wM~W(%zzuTn#7Qa z@YP-Nw;!jqQZG{iE-hP$+X4bj&TQe_z4296dIH>D)r6YbE!JtRe^~=xp$b~DbPq)$ zpp69$i6|ST=3x5lYzsI0$Mz}f#O$X9tHwVbU8~X3KWgONIMee6$hI0F4+nB~1rv{Z zAv;2mSDFS6Z8X;8%@5Wy?!4W#u&o_)6tsy+5;PovT znH;~|(DVeOBlaY~xDKwEFz#&kmiKUZH(6h=XFTm)m*)O3#?o_ky@(I{dfM8r&(^_! z6Jv1j(h)gg&~ZMUnI+{PM68r2lxi#zHn@hNLUZ2M3SZ1R{TICEe^KS^gBCh{UM~A2 zcM++e%tQ(um&);W8~UxR)e}wGEF`xM7b4Dp>%a-Z1Ep1qp!-UB-F)>6i#507Gb|&e zddVuS)S-gHYJ_t7bxj&H)ydO>jagP%DWr~w$^}P@M)W*o`pRgz#9jlhVZt2HN~b0j z)?+2*==Ndzk(1-KPVe&N3_P`M=bIAQ56kbjbV(=e^65zk0+7g0+phd z?<&T7(-1`muLhMtz*P-1jM|%LZ9~dY<^J*V4mg!LEkau0a}6AZHm$--xUA_Zqz+&o zW5WnB4BQ56<=^%%hm$j)jA4G~OuLmDO6AiLv3`F3Ius0~T2)Gy^ z7$*oL>Tr zH2bP=$*KO8b;T$y=;8nRWN+%rjt>X3*g%p-Q^+{My;($`8Xuz zCw_BnSDvq}m&hg1Am!k9W`3ZIrTD^}wo<2C^|Vbob(wt0$7x2@qQ4bs?g`(FDZi3 zb71zBx!=Y7X05VAzZ*+`h|3(S<3LDknjm08#+aj%vJktlDbSq!g#wd2eH*q3`xAwo zUBvrrBW6fVKU>)IvPhu04go4X9u{pnfnTY;BG)(fYBr+h#)p-U3Ti_Ec{!THDeVbdO)8_bcS-tx^ z*t!}dUZL@MyD%3|`o(8AnvhOyo*8eO<5ja!*%lLo0(uBKc9ZwM)3!Y}%J;^GH0ztw z4N5lRyveC0XDe4U8GvK(4ED#D&YV3kBQAfcyrHC;Owcint!C`~xu;GJXRfQM(Z`T;r z&%=j#y);8kKl#T^V8wU<&hVf5Dm1fc2SC8Dl&)yqi3Gq+nUn*kr=Yt$theRE3N>rN%!pY^lE<7we&bX*d#SyCXSQ_@5>JSk6M4M>t0kyp(a;0 zIU?pS3x-GKW-q}&B!88wUby|(Lcoo@Of$iyIg-@?mlOaRRtsySj0ZstLpuQ5->dm$ zHn|q=UYTbHcm(aMfw(6LDhkjc`k-V@6_V45m5ZvsDLd$UX42PAn;l^UR5=b$;pdl@ zjy?c-!H}3p;c}1)huxYr(z(3dxYh6&!1CFsdvT(2DGG<}wp-z=12lc((4i<{{OB7% zDVb+?@Or0-Bze{xqH`&v)0kKuOzAOI`CqyZyex~CGA^)`F$v z>7td+++6UGhnec}CnxyoJFh)}8xl{H_JK-Nb>6oX1e3WnyW3x$5^Yd%SFWwO zo=dwJDKC6!`d~k0QlhsR>n={TGe0jpYDCLsl!Ne2<`8g%7|}enZD2BBeji=}-UYaY zT|;GP3gCm(#Y@2F*SScCqMG;?D2}Fl#A14T4cI@YxFrp)bNAWLDVWC3@42a$_;7Xx zZfa01%G~2O`>bvWzv&c&8K9UKA2BO1y&6p;ac(Pdvdpc~_d?(hL7 zW|<}-^|v<%Qbj-zcrO7BQ_{45nh;>cnToE5)L`s4%W!HTgX67Kg;^+yK!qDHr5EfK~uEAa{a24B<;Ezn&pZCWAQ#c9=+NHR2UpCjUJ&a?>`yU${+g`ul zmv)mM9NwO87!*oXE01mkfu1Lh%oYd&KaTv3Z`a+Jz#)&=ep*OKr>{FYo2O77F~oEd zybY)_L?-eU)s88LFrzvNbHQXll~@zCjD-mmlO^-W`7a#4Wa9ZORC>SqPx2y|3U6qF zWvh1Y>w*?}9R7)gq+LhLoz&339{lzCxh>xw49{BNBAFs>8#m3iZ!_cFERDW*2Q2!4 zq>K;W7-i%|Ylg46{FmvEm1Aq{`u9mL?B}J|x$!a|r0( z&#2@>WrE0@1^*6xhMx?ZwPrxvl)Qm62yPNZ7{1AtBRK zIG*~~l=kW|?>suo?+Ep!p{;?nv^z&(B#iYd{ zdNnh9t|zI|k}mg&G;Jv{1MV5?hn7N;eDK}P2B%D7cmH6Ec+%y{vmVDTD53pMM+9d} zDG}3B&+V-KG)U<8cHz!>cUZB6^c*atgPE)zxHpdEG?-ptaiF66N{ob%_{fx#fb}-1sFU7+-*L8&Z;aNw8kDqRFGIs9_2KEJ% z@7F?Du)*H=;p4@+!!_wwVZtYES9&FP>>s4RCRI*xu4cJ8Pc2=S=HRdTN?n*xW?xKH zUN1R3k2<-sjcv4~=|uUFn;OTY#^q%25##+q7zQy4TJIiSGUrfG5n1pa)W3d~D-XyR#o-6R5O(ejOlo@dyMj_#ggSM&_E-lt! z*@mPU2+38Sf!^yzSrtn8w}QYhP01rB>dE!gfy4j4`jtx4#r0_9uqK6GqzZQG#sZN{ zF95&(rXcfoXucE3M`U%5drL5L4t-5kDQEz)E;+CRgUdTYBlmJ=4~U#d4h}=r)5+*Z zHm31+f@0#i&)4-Q6lW0&m9gZ^27Lja#?S(;TmnP-J)zT?pSfA zR(6>cH~l%G;18cvds~KUi$Tw}G35`A*U1ESzFF90~FMS^WMUK({iD9Xr_Z5B!J;VY0V|W6a><>~uAo@1=Zgk88TJzBzW?`Bgm@ z&*#BGtROw39A2x@VDs(E({q3HSl3|(XDGnR{;UNW@_P;$duP(H#B$sT;^9bifVRB1 ztxqjQ>_j!<3;(!w0^A*Hx;Z{IkhVrx3bb#gPr9&ya6yAWS)1d^I~Fd1u6d!r{7ul zgisW{BUXnXcc1o#`TkJ!UT+v7O1qUCJcCOp%AJv>Kc~kJ)?kDJP?i&pZtuB)l+ai} zBfO{J9|?jspV;#&$LdmXdG**RHXRpWhvysJBIyGcIIPCg3Y5JMQj`y9&N$%paWwIY z`@TUcXqUJ?{@&1mvCS?JrXSEqqNq-sj@xW7Q-;a!MN`q~%GjlV8Ek%#3IWz&3D;-9 z9d{K!v7N|&Ep!bAe+9L}cbf+Rxt4=KYWfclKl|d6>=|!u3czMwFyofcgqzr>N6|tc zr?K`hnxxbokoS8m{I|A5&O&4Pvc$d=$z%J`3HTf_HhMmp%`a+FI}jn7g-JVP^gO3M z@B-gjMwjQMJ0f~7>OZp2+hH`Gy49dT5cbrU@j8oQi9w&Ydr2O7vJT^bVUOsPd=4rI zX2A_v@DZs&7G4+~|{%-TT-1O{?%p5g2{u^nB;;D<2iw#acg)EysF)WY%0Y<&l z1x5-E0Z=n{q}@QF^@wu#_qO8V76!qEodqiX6SvQ7gs^|a6mbPxN9a0(0{Xz&L&^*9 z=9?*(0WbKxfM@t<$T!-*ll*0j>W$-Dss-Oa5EIaMdGjZWO?B^U`|cO&2z-PxMK+1| z4vi?W#>i!AnJkjOB{dlzdTJ~84uzY6cs2P&zR|hb?Yg!t=C$_?!3M_xy25+(&tZS@ zvvJk`DG8-kz`8 zc~XN?GZuXwwoS6OmBZz_mEj^(HmtKZ@3S~ zWf=9)X-fLiz>ri%7A9#9ln z5Q>{8>EWn=rPShU!oqF(Fz*mEpao zp>I$L{gD4@eQ)+W^raz-)F?VWc({~{RKagyhX3MasL883^}lp{AAjuamVEs@TfJ^BSh3aF`G6chCfzAa?2lYNePk zOvzqHuwPYAFup;%ySKTzYv(&2>lE~FOOlG=HP=Re4=wsOva| ztY~&-6~|jxmM30kgQ_~8@sA@Q@b1;iaVvQrwkVR|_1--qsb z7Q)a0f(f9+%~IfDpotyf^qaz3V0-9ZT|`Vbb$(qX9LBa>W}kK82y}m}R%Yj}AH%kE zWM3HMvJot2BAGLS>zR1s=g{c+m{oPQlKJB)qK+bmX*)GHHF}Nl^*)GN%=1L9okfQF z!v&Z=54iB{29ge!pTa(fG&T7P__rFq!f&jmE>V=5wWdnv5$F5e&@l*nN%9NE!8{gkVbp@ZB zy!mDt&(GbjKYFf{3@M$pH(BfSoWKg~*43_ACJcLUYz%O8ut=hod|jy`8$_@$7y|#V zyiu?;IYKq$XZ$$2ne{r+e4mu?feio(DYNpQ(Gc#;NHZ(Gg7OOS*y@Dwgo8#6P(Znr zt$G;BR}K!<1rF=Mm)CmOjPqs{!`lt#hXzMi2yQjHK-U0 z7OuY=9W)e<1+U~T<#x6>uCcv+wO5Iq+(w>wF)Y zptE`!DzdooxT#4ssj1P93T`l8bCt`uUZ=@E$0K^PQ}deMn|{ZI4%e77y*^GlfYCrN zQsllOwy{+m&+kVm%kcR^AUY})LKtnNqa9tk44b_Ose$&_tN%-4GZ-6>|O-t-0 zoo%mJ|2sJWC;*I@m*!sSuJz{DIA+MU0XAJmS-ij1K9L6WYL#I9_tsnK%~l%aYs z>o)8xiO_lu9SiXBJ57bn(0l*2y>a&er4&l(xjWl#%-;W~6~i#?H+7Ks8BR4|KphD} zn@ND6c7rk?2vVGy-4DBoZ83AwM1(+yVBo*4=`TdDK(cY!F4oZqC9cfE3)e_uUGBd; zfzfAznSH8JC<^LvWnG%JKn?ZV>FXWpkrh^dBzLUsOSV zOt#j-r5_ts4hOt58C;iZc9CM&muT8FWrtop|JXMQ?9Y_oz;@{YO#B#1HXxKIQ`mkO zzzDB_kzLY(QA&9X)BT~*3Pw0zdM~#$4W!RsG#EK)!^a6{$L`2V(UNOPDk2g*mVv$A z=XOp;Y>7luEkGbu2n9 z@8h+`-`jz=^SE@D)pWe$%j{O(X}_fn4b@8LRV!L`Uk2$rF{4$;7I9g;h1ynmPbvw| zZ$o2dh+^Yv2J_qcH47ql-r`fu7rO{`@pmJm{sU3mUpY3M%e$?PpYj!29$}m*6uRBS z!9fKc+~)KXERSG(h2}Ze_-mS)$}WZlzAj(GuCun1J{4ch$4frnhX?)&w0ig``|RUu zFIRZ3NLrBg+=3#a@BWM`K`I{E&%t6*ieQ+dQBl^X$-;grY*T+(z-_JD+~c!)zQ8uaPL3oUM&EhY zGBywaBWjRUofhwhCKn8GLL9sjjSU88Fv_gaDm*|SN{D2^2z*%N02zD{{B+LHGGK(jL~AoMtKvS8CJ2T{~%5sf2G)_M*rS!5g^zuD`NQ-xYbrFCvTk^>J@c5^=dKjVn}wR z?>*)rAaL?J@!>Fi2KWg3KKiG?NAh{wk%-`RC#JAFVU>x;-mflowN~S|Rzw#zQ`$R| z`g7Q%zL9WFm@^iplK1e09sMvAv3Klg)gamja|O87=Mo_mPG6)3i}Zwrvx zWP~y5VuGozX0IJd9^y*zQ(TzR%L?&}BMoCm9JZ3ZIrpS=Uyk>slta(Q@%*zRHt14A zp3l>%U3Y_j`rJX!Z|Icrou6Tt1*}L+hsk|eGO6f;q`p?FH3dsP5fWPw@4!NVc|Ruj zhk)5*7isV|6VgoxaW`7fZ19ii!G+PLK7HO$ON@B<0sC+*Pl+Bu$z+#P4KoM=1t~ow ze~J03x51Tyus_2&W8&fG;0yUF5Dv<0{=5+doA@wtU7Eh-l?VHEJuq_w}2XOlb5DDv& zc_~$Y`Ndz{=1eU@4^~AqzJKOw?+5PxAu~@Fo@P1DUG+nUMl-%^abP>|d zFS0l>SbpB^-%ytL3pEA%e8#KQ@Myp+4-q67|`rNL0Jm$I;BlTSDuZ))qA{pT!^ zRm4-EsF+BT$qc>lNQQ@*8DW_`#vu2)-kcqlH(3V52z_Xy)p0(F?YCMAa5r$lw`BY9 zLvBgyd+cRGyZ5vpHi%jNPzR0%y|mTfArd0vpgnw|h8VG|T5@BJW@Bh(QOqU-^VbEzYI69J5Jw*O^Bra|* z>D+Mb2TEUPjnClOH8i;QOx*@SaY~2%fQ}uVZbWoxKew5cEN4YxyIIhw65W^)MzkdK zX2A8-X)iE#|yRp+w4Pltq1Lj%JT%Gync`SqW!n~D{MW&7zx^Gv4G z49Xnu_h{P^mT9wjFgX}P@m!LLbjyU%pSl8M(hl|oH~2k+ZfPVEcBuShQ=@>0g_|y0l1GEe>rESTNG+w<d$O^1E?I5r>k$yGa^S^y5D&rmbglK|{aALH30;7%c){FDED8hwOCiASr?rISB4RiF1!V ztQ{0wUYrHBdL0Y2+L(j8+Et}@q{u4^4=}pu|AjMbW)DM5;Un`)WZzU(^)HRXpN!W= z)d%~!lx*)fmf=_m*{+(>_kGp!4BRvN5Z6g69lHSA`Je|FO;g$x;_+H&cg6OQAV?Sa z0}zAUnjaW*Gwat+P=R)cgiO1360)3zlNaXKcHDrgdjSVTzxRHIrX{wGhv)Q1+~jW^ z8l^s$c9^goEPfr#%4#e5|^ZxuaEjKe7@(@`ZKf3^IB!#_Vltya&d5A@-~~qV?l4S9Q~YJvmg+o4w(uTULWPZ1guq=suWS{l{>v^WV5VNh^U2*H z(eg3gzR>m~6w&T`N#*mGs%ks?w##(XrjODV>+g_(g~T73Y$1O`odG z^yszBuBPJMWVD|yFCQ%~VotUfzdtKoX9!KOFOU z*5Xj4tSe`ZvK4^0qqQS!sO5<P0&hEOtv@3lpY}fU>Ogws&B$lqSY{0}FwNC#P&_H-+qNo|!cJ)5`dmEPPsgdyo^aEIJJs(l5>EBvJ<28snZfhKWZxhAuRWeNc1-Y-ipu%__I6m$Q z;m6C|tlx2%8^##B41^CYg|!@Tjm`_thnSv;ya^?Sg4&U`qV5!phbp~~CsXDzMaYCj z#PQiMbWiL7LP5D>+#pjq{uu)xBEbCsh~C4I-m~zNr%Em{^6IJ=i`^1vn!4@w*H;6d z%PY|JW;+6vPA_#!?O*Gham>(z1#&Ri{--w0 z9v>bq3f!N3=)3oql$d)hE^-`TVIgDNi%Ln2w$wSGhb1Fqr~VkB7hfO0^So%?`I{(c zl!oRklkhm9Ke9H6b!z+TQ#W1aPNmI0p1Q6(Lq$EYxcqcB<-X`i-iycA%}m?QEXx#o z?2^oM^5@$OHnmKr*}0a5`I&-N3=tS42cBA@S%?P4KMi_t70!(EBJ#kW=+T$IQaXTh z=<-wCPQz0X7m}=dNFl;88nkDkt5{@WZAO~Cv^>(j7VaEq$C)f1%VJo%I`#)EbENl9 zj{CQw7&y}g#9W*Fm23+aH*fWv`2F5e@QEDw%V%*uOL#W6m1}J-<8zZ4?A2SUnwqM* zo$a@I(AU#cJdGt`PMg(LP7s?zGhjJPKTpDsv1c)#?fMmii?xDzs<|sX&NGCYlGm@( zJ$eSDvFO|veU}dGOhnmEeikbIaJ7eO^p6q>0;dEJ2!Bo7Ch zwk@gq4m|w(vAnE&q}S71>B|k}whjG1kUp3Jh*3s>Ha=T73&TxpFVCeYkv_@iD^>N^ z{o8PkjpQ5lMUr{kC1jH?7yTYWAT+qc4nRzT(9?6NGi=}kPiC{1ZF%cpwo!CxrPlcF z&e18iYX|YwXNwkafRQ8gFpKm-b~9^zQ#T5r+b6B7f|4*7?BVpWz z|96Xu8Rj#Zv}$noR@5_fG8;KS#6G3d7X!~`u|dO$a}95!)k)$hnNFLR$L-2_X?Yns zG9p6Q)YLR4o>ZLM(b1{H-AHp}Yn&-ljx5wNdZfkLHP5ybnoV&BRUAsCLacec4|OuU zQJl0EvnBehtgL@=-L)yG4B+$J+Xatf_Vm2C1Q*Z`4r&U5PcW^xIf#W2K-2Z5ObrZV zjL^yUc@1!edS?JgLl0m20>{2Wv7n_x5p32kar)BQ=1*CP+)w=tNFESnjff#I3;L{WdIBVNzChH<`yY8Z7BxV= OA1N_8(JEns!2bseL#4L> literal 0 HcmV?d00001 diff --git a/ios/Podfile.lock b/ios/Podfile.lock index e6a0ac0..0f8d01a 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -3,6 +3,8 @@ PODS: - Flutter - MTBBarcodeScanner - SwiftProtobuf + - device_info (0.0.1): + - Flutter - easy_app_installer (0.0.1): - Flutter - Flutter (1.0.0) @@ -12,6 +14,8 @@ PODS: - FMDB (2.7.5): - FMDB/standard (= 2.7.5) - FMDB/standard (2.7.5) + - location (0.0.1): + - Flutter - modal_progress_hud_nsn (0.0.1): - Flutter - MTBBarcodeScanner (5.0.11) @@ -22,6 +26,8 @@ PODS: - FlutterMacOS - permission_handler_apple (9.0.4): - Flutter + - platform_device_id (0.0.1): + - Flutter - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS @@ -33,13 +39,16 @@ PODS: DEPENDENCIES: - barcode_scan2 (from `.symlinks/plugins/barcode_scan2/ios`) + - device_info (from `.symlinks/plugins/device_info/ios`) - easy_app_installer (from `.symlinks/plugins/easy_app_installer/ios`) - Flutter (from `Flutter`) - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) + - location (from `.symlinks/plugins/location/ios`) - modal_progress_hud_nsn (from `.symlinks/plugins/modal_progress_hud_nsn/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) + - platform_device_id (from `.symlinks/plugins/platform_device_id/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`) @@ -53,12 +62,16 @@ SPEC REPOS: EXTERNAL SOURCES: barcode_scan2: :path: ".symlinks/plugins/barcode_scan2/ios" + device_info: + :path: ".symlinks/plugins/device_info/ios" easy_app_installer: :path: ".symlinks/plugins/easy_app_installer/ios" Flutter: :path: Flutter fluttertoast: :path: ".symlinks/plugins/fluttertoast/ios" + location: + :path: ".symlinks/plugins/location/ios" modal_progress_hud_nsn: :path: ".symlinks/plugins/modal_progress_hud_nsn/ios" package_info_plus: @@ -67,6 +80,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/path_provider_foundation/ios" permission_handler_apple: :path: ".symlinks/plugins/permission_handler_apple/ios" + platform_device_id: + :path: ".symlinks/plugins/platform_device_id/ios" shared_preferences_foundation: :path: ".symlinks/plugins/shared_preferences_foundation/ios" sqflite: @@ -74,16 +89,19 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: barcode_scan2: 0af2bb63c81b4565aab6cd78278e4c0fa136dbb0 + device_info: d7d233b645a32c40dfdc212de5cf646ca482f175 easy_app_installer: 29abe397da7d86721fee853281202f414373f45c Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 fluttertoast: eb263d302cc92e04176c053d2385237e9f43fad0 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a + location: 3a2eed4dd2fab25e7b7baf2a9efefe82b512d740 modal_progress_hud_nsn: f6fb744cd060653d66ed8f325360ef3650eb2fde MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e - path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852 + path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9 permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce - shared_preferences_foundation: 297b3ebca31b34ec92be11acd7fb0ba932c822ca + platform_device_id: 81b3e2993881f87d0c82ef151dc274df4869aef5 + shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472 sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 SwiftProtobuf: b02b5075dcf60c9f5f403000b3b0c202a11b6ae1 Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 097b297..3559a67 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -47,5 +47,14 @@ UIViewControllerBasedStatusBarAppearance + + NSLocationWhenInUseUsageDescription + Need location when in use + NSLocationAlwaysAndWhenInUseUsageDescription + Always and when in use! + NSLocationUsageDescription + Older devices need location. + NSLocationAlwaysUsageDescription + Can I have location always? diff --git a/lib/bloc/sos/sos_bloc.dart b/lib/bloc/sos/sos_bloc.dart index 03e5341..beb2c0f 100644 --- a/lib/bloc/sos/sos_bloc.dart +++ b/lib/bloc/sos/sos_bloc.dart @@ -1,22 +1,106 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:flutter/foundation.dart'; import 'package:location/location.dart'; +import 'package:unit2/model/sos/session.dart'; import 'package:unit2/sevices/sos/sos_service.dart'; +import '../../utils/global.dart'; + part 'sos_event.dart'; part 'sos_state.dart'; class SosBloc extends Bloc { SosBloc() : super(SosInitial()) { LocationData? locationData; - on((event, emit)async { - emit(LoadingState()); + String? mobile1; + String? mobile2; + String? sessionToken; + ////get user location + on((event, emit) async { + emit(const LoadingState(message: "Getting Location")); + mobile1 = await SOS!.get('mobile1'); + mobile2 = await SOS!.get('mobile2'); + sessionToken = await SOS!.get('session_token'); + if (mobile1 != null) { + if (sessionToken != null) { + add(CheckAcknowledgement(sessionToken: sessionToken!)); + } else { + try { + LocationData? newLocationData = + await SosService.instance.getUserLocation(); + locationData = newLocationData; + emit(RequestSosState( + locationData: locationData!, + mobile1: mobile1!, + mobile2: mobile2)); + } catch (e) { + emit(ErrorState(message: e.toString())); + } + } + } else { + try { LocationData? newLocationData = - await SosService.instance.getUserLocation(); - locationData = newLocationData; - // User location Loaded and there is no mobile numbers in cache - emit(UserLocationLoaded(locationData: locationData!)); + await SosService.instance.getUserLocation(); + locationData = newLocationData; + + emit(UserLocationLoaded(locationData: locationData!)); + } catch (e) { + emit(ErrorState(message: e.toString())); + } + } + }); + ////submit mobile + on((event, emit) async { + emit(const LoadingState(message: "")); + mobile1 = event.mobile1; + mobile2 = event.mobile2; + await SOS!.put('mobile1', event.mobile1); + await SOS!.put('mobile2', event.mobile2); + emit(RequestSosState( + locationData: locationData!, + mobile1: event.mobile1, + mobile2: event.mobile2)); + }); + + //// send SOS + on((event, emit) async { + SessionData sessionData; + emit(const LoadingState(message: "Sending emergency response request")); + try { + sessionData = await SosService.instance.requestSos( + location: locationData!, + mobile1: mobile1!, + mobile2: mobile2, + msg: event.msg, + requestedDate: event.requestDate); + await SOS!.put('session_token', sessionData.sessionToken); + emit(SOSReceivedState(sessionToken: sessionData.sessionToken!)); + } catch (e) { + emit(ErrorState(message: e.toString())); + } + }); + //// check acknowledgement + on((event, emit) async { + SessionData sessionData; + try { + SessionData? newSessionData = + await SosService.instance.checkAcknowledgement(event.sessionToken); + if (newSessionData.acknowledgeDate == null) { + debugPrint(newSessionData.sessionToken); + emit(SOSReceivedState(sessionToken: newSessionData.sessionToken!)); + } else { + sessionData = newSessionData; + emit(SoSAcknowledgementConfirm(sessionData: sessionData)); + } + } catch (e) { + emit(ErrorState(message: e.toString())); + } + }); + on((event, emit) async { + await SOS!.delete('session_token'); + add(LoadUserLocation()); }); } } diff --git a/lib/bloc/sos/sos_event.dart b/lib/bloc/sos/sos_event.dart index 0880459..db341cd 100644 --- a/lib/bloc/sos/sos_event.dart +++ b/lib/bloc/sos/sos_event.dart @@ -11,3 +11,33 @@ class LoadUserLocation extends SosEvent { @override List get props => []; } + +class SubmitMobile extends SosEvent { + final String mobile1; + final String? mobile2; + const + SubmitMobile({required this.mobile1,required this.mobile2}); + @override + List get props => []; + +} + +class SendSOS extends SosEvent { + final String msg; + final String requestDate; + const SendSOS({required this.msg, required this.requestDate}); + @override + List get props => [msg, requestDate]; +} + +class CheckAcknowledgement extends SosEvent { + final String sessionToken; + const CheckAcknowledgement({required this.sessionToken}); + @override + List get props => [sessionToken]; +} + +class OnDoneRequest extends SosEvent { + @override + List get props => []; +} \ No newline at end of file diff --git a/lib/bloc/sos/sos_state.dart b/lib/bloc/sos/sos_state.dart index ee325b9..44ca7a8 100644 --- a/lib/bloc/sos/sos_state.dart +++ b/lib/bloc/sos/sos_state.dart @@ -21,7 +21,30 @@ class ErrorState extends SosState{ @override List get props => [message]; } +class RequestSosState extends SosState{ + final LocationData locationData; + final String mobile1; + final String? mobile2; + + const RequestSosState({required this.locationData, required this.mobile1, required this.mobile2}); + @override + List get props => [locationData,mobile1]; +} class LoadingState extends SosState{ - + final String message; + const LoadingState({required this.message}); +} +class SOSReceivedState extends SosState { + final String sessionToken; + const SOSReceivedState({required this.sessionToken}); + @override + List get props => [sessionToken]; +} + +class SoSAcknowledgementConfirm extends SosState{ + final SessionData sessionData; + const SoSAcknowledgementConfirm({required this.sessionData}); + @override + List get props => [sessionData]; } diff --git a/lib/bloc/user/user_bloc.dart b/lib/bloc/user/user_bloc.dart index 5c9c705..3bec84b 100644 --- a/lib/bloc/user/user_bloc.dart +++ b/lib/bloc/user/user_bloc.dart @@ -22,26 +22,31 @@ class UserBloc extends Bloc { String? _apkVersion; bool save = false; UserBloc() : super(UserInitial()) { - // this event is called when opening the app to check if - // there is new app version + //// this event is called when opening the app to check if + //// there is new app version on((event, emit) async { try { emit(SplashScreen()); - VersionInfo versionInfo = await AuthService.instance.getVersionInfo(); - _versionInfo = versionInfo; + if (_versionInfo == null) { + VersionInfo versionInfo = await AuthService.instance.getVersionInfo(); + _versionInfo = versionInfo; + } String apkVersion = await getAppVersion(); _apkVersion = apkVersion; final String? saved = CREDENTIALS?.get('saved'); final String? username = CREDENTIALS?.get('username'); final String? password = CREDENTIALS?.get('password'); debugPrint(username); - debugPrint(password); + debugPrint(password); if (saved != null) { save = true; add(UserLogin(username: username, password: password)); } else { emit(VersionLoaded( - versionInfo: _versionInfo, apkVersion: _apkVersion)); + versionInfo: _versionInfo, + apkVersion: _apkVersion, + username: null, + password: null)); } } catch (e) { emit(UserError( @@ -49,11 +54,15 @@ class UserBloc extends Bloc { )); } }); -//Loading the current version of the app +////Loading the current version of the app on((event, emit) { - emit(VersionLoaded(versionInfo: _versionInfo, apkVersion: _apkVersion)); + emit(VersionLoaded( + versionInfo: _versionInfo, + apkVersion: _apkVersion, + username: event.username, + password: event.password)); }); - +////userlogin on((event, emit) async { try { Map response = await AuthService.instance @@ -61,10 +70,16 @@ class UserBloc extends Bloc { if (response['status'] == true) { UserData userData = UserData.fromJson(response['data']); emit(UserLoggedIn( - userData: userData, success: true, message: response['message'],savedCredentials: save)); + userData: userData, + success: true, + message: response['message'], + savedCredentials: save)); } else { emit(UserLoggedIn( - userData: null, success: false, message: response['message'],savedCredentials: save)); + userData: null, + success: false, + message: response['message'], + savedCredentials: save)); } } on TimeoutException catch (_) { emit(InternetTimeout(message: timeoutError)); @@ -77,7 +92,7 @@ class UserBloc extends Bloc { UserData? userData = await AuthService.instance .qrLogin(uuid: event.uuid, password: event.password); _userData = userData; - emit(UserLoggedIn(userData: _userData,savedCredentials: save)); + emit(UserLoggedIn(userData: _userData, savedCredentials: save)); } on TimeoutException catch (_) { emit(InternetTimeout(message: timeoutError)); } on SocketException catch (_) { @@ -87,7 +102,7 @@ class UserBloc extends Bloc { } }); on((event, emit) { - emit(UserLoggedIn(userData: _userData,savedCredentials: save)); + emit(UserLoggedIn(userData: _userData, savedCredentials: save)); }); on((event, emit) async { ScanResult result = await QRCodeBarCodeScanner.instance.scanner(); diff --git a/lib/bloc/user/user_event.dart b/lib/bloc/user/user_event.dart index cf529fb..a0cbd10 100644 --- a/lib/bloc/user/user_event.dart +++ b/lib/bloc/user/user_event.dart @@ -28,7 +28,11 @@ class GetUuid extends UserEvent { } -class LoadVersion extends UserEvent {} +class LoadVersion extends UserEvent { + final String? username; + final String? password; + LoadVersion({this.password,this.username}); +} class UuidLogin extends UserEvent { final String? uuid; diff --git a/lib/bloc/user/user_state.dart b/lib/bloc/user/user_state.dart index de6688f..ab21f7f 100644 --- a/lib/bloc/user/user_state.dart +++ b/lib/bloc/user/user_state.dart @@ -42,7 +42,9 @@ class UserLoggedIn extends UserState{ class VersionLoaded extends UserState { final VersionInfo? versionInfo; final String? apkVersion; - VersionLoaded({this.versionInfo,this.apkVersion}); + final String? username; + final String? password; + VersionLoaded({this.versionInfo,this.apkVersion, this.password,this.username}); @override List get props => [versionInfo!]; } diff --git a/lib/main.dart b/lib/main.dart index da7df5a..20b079b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -20,7 +20,7 @@ Future main() async { var appDirectory = await path_provider.getApplicationDocumentsDirectory(); await Hive.initFlutter(appDirectory.path); CREDENTIALS = await Hive.openBox('credentials'); - SOSCONTACTS = await Hive.openBox('soscontacts'); + SOS = await Hive.openBox('soscontacts'); SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]) .then((_) { runApp(MyApp()); diff --git a/lib/model/sos/session.dart b/lib/model/sos/session.dart new file mode 100644 index 0000000..2fa398a --- /dev/null +++ b/lib/model/sos/session.dart @@ -0,0 +1,69 @@ +// To parse this JSON data, do +// +// final sessionData = sessionDataFromJson(jsonString); + +import 'dart:convert'; + +SessionData sessionDataFromJson(String str) => SessionData.fromJson(json.decode(str)); + +String sessionDataToJson(SessionData data) => json.encode(data.toJson()); + +class SessionData { + SessionData({ + this.id, + this.receivedDate, + this.responseDate, + this.statusid, + this.acknowledgeDate, + this.acknowledgedBy, + this.remarks, + this.respondentid, + this.respondentMessage, + this.pushedbuttonDate, + this.sosLevel, + this.sessionToken, + }); + + final int? id; + final String? receivedDate; + final String? responseDate; + final int? statusid; + final String? acknowledgeDate; + final String? acknowledgedBy; + final String? remarks; + final int? respondentid; + final String? respondentMessage; + final String? pushedbuttonDate; + final String? sosLevel; + final String? sessionToken; + + factory SessionData.fromJson(Map json) => SessionData( + id: json["id"] = json["id"], + receivedDate: json["received_date"], + responseDate: json["response_date"], + statusid: json["statusid"], + acknowledgeDate: json["acknowledge_date"], + acknowledgedBy: json["acknowledged_by"], + remarks: json["remarks"], + respondentid: json["respondentid"], + respondentMessage: json["respondent_message"], + pushedbuttonDate: json["pushedbutton_date"], + sosLevel: json["sos_level"], + sessionToken: json["session_token"], + ); + + Map toJson() => { + "id": id, + "received_date": receivedDate, + "response_date": responseDate, + "statusid": statusid, + "acknowledge_date": acknowledgeDate, + "acknowledged_by": acknowledgedBy, + "remarks": remarks, + "respondentid": respondentid, + "respondent_message": respondentMessage, + "pushedbutton_date": pushedbuttonDate , + "sos_level": sosLevel, + "session_token": sessionToken, + }; +} diff --git a/lib/screens/sos/add_mobile.dart b/lib/screens/sos/add_mobile.dart index 8abe496..51aa7e9 100644 --- a/lib/screens/sos/add_mobile.dart +++ b/lib/screens/sos/add_mobile.dart @@ -82,7 +82,7 @@ class AddMobile extends StatelessWidget { validator: mobileNumberValidator, maxLength: 11, decoration: - normalTextFieldStyle(mobile1, "+63")), + normalTextFieldStyle(mobile1, "09000000000")), const SizedBox( height: 12, ), @@ -90,7 +90,7 @@ class AddMobile extends StatelessWidget { name: 'mobile2', maxLength: 11, decoration: - normalTextFieldStyle(mobile2, "+63")), + normalTextFieldStyle(mobile2, "0900000000000")), SizedBox( height: isMobile() diff --git a/lib/screens/sos/components/acknnowledge.dart b/lib/screens/sos/components/acknnowledge.dart new file mode 100644 index 0000000..8a771ba --- /dev/null +++ b/lib/screens/sos/components/acknnowledge.dart @@ -0,0 +1,123 @@ +import 'package:animate_do/animate_do.dart'; +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttericon/iconic_icons.dart'; +import 'package:unit2/theme-data.dart/btn-style.dart'; + +import '../../../model/sos/session.dart'; +import '../../../theme-data.dart/colors.dart'; +import '../../../utils/global.dart'; + +class SosAcknowledged extends StatelessWidget { + final Function() onpressed; + final SessionData sessionData; + const SosAcknowledged({super.key, required this.onpressed, required this.sessionData}); + + @override + Widget build(BuildContext context) { + + return Container( + padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 35), + height: screenHeight, + child: Stack( + children: [ + Positioned( + bottom: 0, + child: SizedBox( + width: screenWidth, + height: screenHeight / 2, + child: Opacity( + opacity: .2, + child: Image.asset( + "assets/pngs/emergency.png", + fit: BoxFit.cover, + ), + ), + )), + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox( + height: 18, + ), + SlideInDown( + child: AutoSizeText( + "SOS Acknowledged!", + maxLines: 2, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.displayMedium!.copyWith( + fontSize: 40, + color: success2, + fontWeight: FontWeight.w600), + ), + ), + const SizedBox( + height: 5, + ), + SlideInDown( + child: const Icon( + Iconic.ok_circle, + color: success2, + size: 120, + ), + ), + const SizedBox( + height: 22, + ), + SlideInUp( + child: Container( + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(15))), + child: Column( + children: [ + + + ListTile( + title: AutoSizeText( + sessionData.acknowledgedBy!.toUpperCase(), + maxLines: 2, + style: Theme.of(context).textTheme.headline4!.copyWith( + fontSize: 22, + fontWeight: FontWeight.bold, + color: third), + ), + subtitle: Text( + "Acknowledge by", + style: Theme.of(context).textTheme.labelLarge, + ), + ), + Container( + padding: const EdgeInsets.all(15), + child: Text( + "NOTE: Please ensure that the mobile numbers you provided are still active, and look for an area with stable network. The response team will contact your mobile number.", + textAlign: TextAlign.justify, + style: Theme.of(context).textTheme.caption!.copyWith( + fontSize: 14, + color: Colors.black87, + ), + ), + ), + ], + ), + ), + ), + Expanded(child: Container()), + SlideInUp( + child: SizedBox( + height: 50, + width: double.infinity, + child: ElevatedButton( + style: + mainBtnStyle(second, Colors.transparent, Colors.white54), + onPressed: onpressed, + child: const Text("DONE!")), + ), + ) + ], + ), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/screens/sos/components/add_mobile.dart b/lib/screens/sos/components/add_mobile.dart index 80b16b4..3025e92 100644 --- a/lib/screens/sos/components/add_mobile.dart +++ b/lib/screens/sos/components/add_mobile.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:unit2/bloc/sos/sos_bloc.dart'; import 'package:unit2/screens/sos/components/request_sos.dart'; import 'package:unit2/theme-data.dart/text-styles.dart'; import 'package:unit2/utils/screen_info.dart'; @@ -17,25 +19,16 @@ class AddMobile extends StatelessWidget { final _formKey = GlobalKey(); @override Widget build(BuildContext context) { - return WillPopScope( - onWillPop: () async { - return true; - }, - child: SafeArea( - child: Scaffold( - appBar: AppBar( - title: const Text("Add contact info"), - centerTitle: true, - backgroundColor: primary, - elevation: 0, - ), - resizeToAvoidBottomInset: true, - body: SingleChildScrollView( + + return BlocBuilder( + builder: (context, state) { + if(state is UserLocationLoaded){ + return SingleChildScrollView( child: SizedBox( - height: screenHeight * .90, + height: screenHeight * .95, child: Stack( children: [ - Wave(height: blockSizeVertical * 8), + Positioned( bottom: 0, right: 0, @@ -77,22 +70,25 @@ class AddMobile extends StatelessWidget { key: _formKey, child: Column( children: [ - // Mobile number 1 + //// Mobile number 1 FormBuilderTextField( + autovalidateMode: AutovalidateMode.onUserInteraction, name: 'mobile1', validator: mobileNumberValidator, maxLength: 11, decoration: - normalTextFieldStyle(mobile1, "+63")), + normalTextFieldStyle(mobile1, "09")), const SizedBox( height: 12, ), + //// Mobile number 2 FormBuilderTextField( + autovalidateMode: AutovalidateMode.onUserInteraction, name: 'mobile2', - maxLength: 11, + decoration: - normalTextFieldStyle(mobile2, "+63")), - + normalTextFieldStyle(mobile2, "09")), + SizedBox( height: isMobile() ? blockSizeVertical * 3 @@ -101,22 +97,20 @@ class AddMobile extends StatelessWidget { width: double.infinity, height: screenHeight * .06, child: ElevatedButton( - style: secondaryBtnStyle(second, + style: secondaryBtnStyle(primary, Colors.transparent, Colors.white54), child: const Text( submit, style: TextStyle(color: Colors.white), - ), + ),//// on pressed onPressed: () { if (_formKey.currentState! .saveAndValidate()) { - - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ - return RequestSOS(); - })); - + String mobile1 = _formKey.currentState!.value['mobile1']; + String? mobile2 = _formKey.currentState!.value['mobile2']; + context.read().add(SubmitMobile(mobile1: mobile1, mobile2: mobile2)); } - + // } }, ), @@ -128,9 +122,11 @@ class AddMobile extends StatelessWidget { ], ), ), - ), - ), - ), + ); + } + return Container(); + + }, ); } } diff --git a/lib/screens/sos/components/edit_mobile.dart b/lib/screens/sos/components/edit_mobile.dart new file mode 100644 index 0000000..8270841 --- /dev/null +++ b/lib/screens/sos/components/edit_mobile.dart @@ -0,0 +1,86 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:unit2/main.dart'; +import 'package:unit2/theme-data.dart/btn-style.dart'; +import 'package:unit2/utils/validators.dart'; + +import '../../../theme-data.dart/colors.dart'; +import '../../../theme-data.dart/form-style.dart'; + +class EditMobile extends StatelessWidget { + final String initialValue; + final String title; + final String label; + final Function() onpressed; + final Function(String?) onchanged; + const EditMobile( + {super.key, + required this.initialValue, + required this.label, + required this.title, + required this.onpressed, + required this.onchanged}); + + + @override + + Widget build(BuildContext context) { + final formKey = GlobalKey(); + return Container( + height: 300, + padding: const EdgeInsets.all(26), + child: Stack( + children: [ + Positioned( + top: -16, + right: -10, + child: IconButton( + onPressed: () { + Navigator.of(context, rootNavigator: true).pop(); + }, + icon: const Icon( + Icons.close, + size: 18, + )), + ), + FormBuilder( + key: formKey, + child: Column( + children: [ + Text(title), + const SizedBox( + height: 25, + ), + FormBuilderTextField( + autovalidateMode: AutovalidateMode.onUserInteraction, + name: "mobile", + onChanged: onchanged, + validator: mobileNumberValidator, + keyboardType: TextInputType.number, + initialValue: initialValue, + decoration: normalTextFieldStyle(label, label), + maxLength: 11, + ), + const SizedBox( + height: 25, + ), + SizedBox( + height: 50, + width: 200, + child: ElevatedButton( + onPressed: (){ + if(formKey.currentState!.saveAndValidate()){ + onpressed(); + } + }, + style: mainBtnStyle(success2, Colors.transparent, success), + child: const Text("Submit")), + ) + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/screens/sos/components/request_sos.dart b/lib/screens/sos/components/request_sos.dart index f6e6f6d..990de3e 100644 --- a/lib/screens/sos/components/request_sos.dart +++ b/lib/screens/sos/components/request_sos.dart @@ -1,18 +1,23 @@ import 'package:flutter/material.dart'; import 'package:flutter/src/widgets/container.dart'; import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_svg/svg.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:fluttericon/typicons_icons.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:intl/intl.dart'; +import 'package:unit2/bloc/sos/sos_bloc.dart'; import 'package:unit2/screens/sos/components/mobile.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; +import '../../../sevices/sos/sos_service.dart'; import '../../../theme-data.dart/btn-style.dart'; import '../../../theme-data.dart/form-style.dart'; import '../../../utils/global.dart'; +import 'edit_mobile.dart'; class RequestSOS extends StatefulWidget { const RequestSOS({super.key}); @@ -22,88 +27,142 @@ class RequestSOS extends StatefulWidget { } class _RequestSOSState extends State { + final formKey = GlobalKey(); + DateFormat dteFormat = DateFormat("y-M-d H:m:s"); + + String? mobileNumber1; + String? mobileNumber2; @override Widget build(BuildContext context) { - return SafeArea( - child: Scaffold( - appBar: AppBar( - centerTitle: true, - title: const Text(sOSTitle), - backgroundColor: second, - ), - body: SingleChildScrollView( - child: Container( - height: screenHeight * .84, - padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 10), - child: Column( - children: [ - SizedBox( - height: blockSizeVertical * 2, - ), - SvgPicture.asset( - 'assets/svgs/request_sos.svg', - height: blockSizeVertical * 22, - allowDrawingOutsideViewBox: true, - ), - Mobile( - title: "09661548775", - subtitle: mobile1, - onPressed: () {}), - const Divider(), - Mobile( - title: "09661548775", - subtitle: mobile2, - onPressed: () {}), - const Divider(), - ListTile( - leading: const Icon( - Typicons.location, - color: second, - ), - title: Text("Latitude/Longitude"), - subtitle: Text( - currentLocation, - style: Theme.of(context).textTheme.caption, - ), - ), - FormBuilderTextField( - name: "message", - validator: FormBuilderValidators.compose([ - FormBuilderValidators.required( - errorText: messageRequired) - ]), - autovalidateMode: AutovalidateMode.onUserInteraction, - maxLines: 5, - decoration: normalTextFieldStyle("", sosMessage), - ), - const Expanded( - child: SizedBox(), - ), - SizedBox( - width: double.infinity, - height: screenHeight * .06, - child: ElevatedButton( - style: secondaryBtnStyle( - second, Colors.transparent, Colors.white54), - child: const Text( - submit, - style: TextStyle(color: Colors.white), + return BlocBuilder( + builder: (context, state) { + if(state is RequestSosState){ + return SingleChildScrollView( + child: Container( + height: screenHeight * .82, + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 10), + child: FormBuilder( + key: formKey, + child: Column( + children: [ + SizedBox( + height: blockSizeVertical * 2, ), - onPressed: () { - // if (_formKey.currentState.validate()) { - // _formKey.currentState.save(); - // BlocProvider.of(context) - // .add(UserWebLogin( - // password: password, - // username: username)); - // } - }, - ), + SvgPicture.asset( + 'assets/svgs/request_sos.svg', + height: blockSizeVertical * 22, + allowDrawingOutsideViewBox: true, + ), + Mobile( + title: state.mobile1, + subtitle: mobile1, + //// edit modal + onPressed: () { + showBottomSheet( + context: context, + builder: (context) { + return EditMobile( + title: "Edit Mobile 1", + label: "Mobile number 1", + initialValue: state.mobile1, + onchanged: (value) { + mobileNumber1 = value; + }, + onpressed: () { + Navigator.of(context, + rootNavigator: true) + .pop(); + BlocProvider.of(context) + .add(SubmitMobile( + mobile1: mobileNumber1!, + mobile2: state.mobile2)); + }, + ); + }); + },), + const Divider(), + ////edit mobile 2 + Mobile( + title: state.mobile2??"N/A", + subtitle: mobile2, + onPressed: () { + showBottomSheet( + context: context, + builder: (context) { + return EditMobile( + title: "Edit Mobile 2", + label: "Mobile number 2", + initialValue: state.mobile2??'', + onchanged: (value) { + mobileNumber2 = value; + }, + onpressed: () { + Navigator.of(context, + rootNavigator: true) + .pop(); + BlocProvider.of(context) + .add(SubmitMobile( + mobile1: state.mobile1, + mobile2: mobileNumber2)); + }, + ); + }); + }), + const Divider(), + ListTile( + leading: const Icon( + Typicons.location, + color: second, + ), + title: Text("${state.locationData.latitude}/${state.locationData.longitude}"), + subtitle: Text( + currentLocation, + style: Theme.of(context).textTheme.bodySmall, + ), + ), + FormBuilderTextField( + name: "message", + validator: FormBuilderValidators.compose([ + FormBuilderValidators.required(errorText: messageRequired) + ]), + autovalidateMode: AutovalidateMode.onUserInteraction, + maxLines: 5, + decoration: normalTextFieldStyle("", sosMessage), + ), + const Expanded( + child: SizedBox(), + ), + SizedBox( + width: double.infinity, + height: screenHeight * .06, + child: ElevatedButton( + style: secondaryBtnStyle( + primary, Colors.transparent, Colors.white54), + child: const Text( + submit, + style: TextStyle(color: Colors.white), + ), + //// on pressed + onPressed: () async{ + if( formKey.currentState!.saveAndValidate()){ + String message = formKey.currentState!.value['message']; + DateTime today = DateTime.now(); + String requestedDate = + dteFormat.format(today); + BlocProvider.of(context).add(SendSOS(msg: message, requestDate: requestedDate)); + + } + + }, + ), + ), + ], ), - ], - )), - ), - ), + )), + ); + } + return Container(); + }, ); } } diff --git a/lib/screens/sos/components/sos_received.dart b/lib/screens/sos/components/sos_received.dart index 3d9192d..a8bad17 100644 --- a/lib/screens/sos/components/sos_received.dart +++ b/lib/screens/sos/components/sos_received.dart @@ -9,125 +9,125 @@ import '../../../theme-data.dart/colors.dart'; import '../../../utils/global.dart'; class SOSreceived extends StatelessWidget { - final Function onpressed; - const SOSreceived({required Key key, required this.onpressed}) - : super(key: key); + final Function() onpressed; + const SOSreceived({super.key, required this.onpressed}); @override Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 35), - height: screenHeight, - child: Stack( - children: [ - Positioned( - bottom: 0, - child: SizedBox( - width: screenWidth, - height: screenHeight / 2, - child: Opacity( - opacity: .2, - child: Image.asset( - "assets/emergency.png", - fit: BoxFit.cover, + + return Container( + padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 35), + height: screenHeight, + child: Stack( + children: [ + Positioned( + bottom: 0, + child: SizedBox( + width: screenWidth, + height: screenHeight / 2, + child: Opacity( + opacity: .2, + child: Image.asset( + "assets/pngs/emergency.png", + fit: BoxFit.cover, + ), ), + )), + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + height: blockSizeVertical * 6, ), - )), - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SizedBox( - height: blockSizeVertical * 6, - ), - Bounce( - from: 20, - infinite: true, - delay: const Duration(milliseconds: 800), - child: Stack( - alignment: AlignmentDirectional.topCenter, - children: [ - Container( - margin: const EdgeInsets.only(top: 20), - child: CircleAvatar( - radius: blockSizeVertical * 8, - backgroundColor: second, - child: Container( - margin: const EdgeInsets.only(top: 25), - child: SvgPicture.asset( - 'assets/sos.svg', - height: blockSizeHorizontal * 17, - color: Colors.white, - allowDrawingOutsideViewBox: true, - alignment: Alignment.bottomCenter, + Bounce( + from: 20, + infinite: true, + delay: const Duration(milliseconds: 800), + child: Stack( + alignment: AlignmentDirectional.topCenter, + children: [ + Container( + margin: const EdgeInsets.only(top: 20), + child: CircleAvatar( + radius: blockSizeVertical * 8, + backgroundColor: second, + child: Container( + margin: const EdgeInsets.only(top: 25), + child: SvgPicture.asset( + 'assets/svgs/sos.svg', + height: blockSizeHorizontal * 17, + color: Colors.white, + allowDrawingOutsideViewBox: true, + alignment: Alignment.bottomCenter, + ), ), ), ), - ), - Positioned( - top: blockSizeVertical * 3, - child: const SpinKitPulse( - color: primary, - size: 120, + Positioned( + top: blockSizeVertical * 3, + child: const SpinKitPulse( + color: primary, + size: 120, + ), ), - ), - ], + ], + ), ), - ), - const SizedBox(height: 16), - SlideInUp( - from: 50, - child: AutoSizeText( - sosReceived, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.displayMedium!.copyWith( - fontSize: 40, - color: third, - fontWeight: FontWeight.w500, - ), - ), - ), - const SizedBox( - height: 8, - ), - SlideInUp( - from: 50, - child: Container( - padding: const EdgeInsets.all(15), - decoration: const BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.all(Radius.circular(15))), + const SizedBox(height: 16), + SlideInUp( + from: 50, child: AutoSizeText( - sOSReceivedMessage, + sosReceived, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.caption!.copyWith( - fontSize: 16, - color: Colors.black87, + style: Theme.of(context).textTheme.displayMedium!.copyWith( + fontSize: 40, + color: third, + fontWeight: FontWeight.w500, ), ), ), - ), - const SizedBox( - height: 40, - ), - Expanded(child: Container()), - SlideInUp( - child: SizedBox( - height: 50, - width: 200, - child: TextButton( - style: mainBtnStyle(second, Colors.transparent, second), - onPressed: () {}, - child: const Text(cancelRequest, - style: TextStyle( - color: Colors.white, - )), + const SizedBox( + height: 8, + ), + SlideInUp( + from: 50, + child: Container( + padding: const EdgeInsets.all(15), + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(15))), + child: AutoSizeText( + sOSReceivedMessage, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.caption!.copyWith( + fontSize: 16, + color: Colors.black87, + ), + ), ), ), - ) - ], - ), - ], - ), + const SizedBox( + height: 40, + ), + Expanded(child: Container()), + SlideInUp( + child: SizedBox( + height: 50, + width: 200, + child: TextButton( + style: mainBtnStyle(second, Colors.transparent, second), + onPressed: onpressed, + child: const Text(cancelRequest, + style: TextStyle( + color: Colors.white, + )), + ), + ), + ) + ], + ), + ], + ), ); } } diff --git a/lib/screens/sos/index.dart b/lib/screens/sos/index.dart index 6fe5db8..f3bdb90 100644 --- a/lib/screens/sos/index.dart +++ b/lib/screens/sos/index.dart @@ -1,12 +1,25 @@ +import 'dart:async'; + +import 'package:animate_do/animate_do.dart'; import 'package:flutter/material.dart'; import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter/src/widgets/placeholder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:unit2/bloc/sos/sos_bloc.dart'; +import 'package:unit2/screens/sos/components/acknnowledge.dart'; import 'package:unit2/screens/sos/components/add_mobile.dart'; +import 'package:unit2/screens/sos/components/request_sos.dart'; import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/text_container.dart'; +import 'package:unit2/widgets/error_state.dart'; + +import '../../utils/global.dart'; +import '../../widgets/wave.dart'; +import 'components/request_sos.dart'; +import 'components/sos_received.dart'; class SosScreen extends StatefulWidget { const SosScreen({super.key}); @@ -16,38 +29,97 @@ class SosScreen extends StatefulWidget { } class _SosScreenState extends State { + Timer? timer; + + @override + void dispose() { + timer != null ? timer!.cancel() : timer = null; + super.dispose(); + } + + @override + void initState() { + timer = Timer(Duration.zero, () {}); + super.initState(); + } + @override Widget build(BuildContext context) { return SafeArea( child: Scaffold( + appBar: AppBar(backgroundColor: primary), resizeToAvoidBottomInset: true, - appBar: AppBar( - title: const Text("SOS"), - centerTitle: true, - backgroundColor: primary, - ), body: ProgressHUD( - padding: const EdgeInsets.all(24), + padding: const EdgeInsets.all(32), backgroundColor: Colors.black87, indicatorWidget: const SpinKitFadingCircle(color: Colors.white), - child: BlocConsumer( + child: BlocConsumer( listener: (context, state) { - //// user location loaded - if (state is UserLocationLoaded) { - Navigator.push(context, - MaterialPageRoute(builder: (BuildContext context) { - return AddMobile(); - })); + if (state is ErrorState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); } ////loading state if (state is LoadingState) { final progress = ProgressHUD.of(context); - progress!.showWithText("Please wait..."); + progress!.showWithText(state.message); + } + //// dismiss progress + if (state is ErrorState || state is SOSReceivedState || state is RequestSosState || state is UserLocationLoaded || state is SoSAcknowledgementConfirm) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); } }, builder: (context, state) { - if (state is ErrorState) {} - return Container(); + //// error state + if (state is ErrorState) { + timer!.cancel(); + return SomethingWentWrong( + message: state.message, + onpressed: () { + context.read().add(LoadUserLocation()); + }); + } //// user location loaded + if (state is UserLocationLoaded) { + return AddMobile(); + + //// request sos state + } + if (state is RequestSosState) { + return const RequestSOS(); + } + //// received sos state + if (state is SOSReceivedState) { + timer = Timer.periodic(const Duration(seconds: 10), (timer) { + context.read().add( + CheckAcknowledgement(sessionToken: state.sessionToken)); + }); + return SOSreceived(onpressed: () async { + timer!.cancel(); + await SOS!.delete('session_token'); + Navigator.pop(context); + }); + } + ///// sos acknowledge and confirm + if (state is SoSAcknowledgementConfirm) { + return SosAcknowledged( + onpressed: () async { + timer!.cancel(); + await SOS!.delete('session_token'); + Navigator.pop(context); + }, + sessionData: state.sessionData); + } + return SizedBox( + height: screenHeight, + child: Stack(children: [ + + Positioned( + bottom: 0, + right: 0, + child: WaveReverse(height: blockSizeVertical * 8)) + ]), + ); }, ), ), diff --git a/lib/screens/sos/request_sos.dart b/lib/screens/sos/request_sos.dart deleted file mode 100644 index a98b654..0000000 --- a/lib/screens/sos/request_sos.dart +++ /dev/null @@ -1,109 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/container.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter_form_builder/flutter_form_builder.dart'; -import 'package:flutter_svg/svg.dart'; -import 'package:fluttericon/font_awesome5_icons.dart'; -import 'package:fluttericon/typicons_icons.dart'; -import 'package:form_builder_validators/form_builder_validators.dart'; -import 'package:unit2/screens/sos/components/mobile.dart'; -import 'package:unit2/theme-data.dart/colors.dart'; -import 'package:unit2/utils/text_container.dart'; - -import '../../theme-data.dart/btn-style.dart'; -import '../../theme-data.dart/form-style.dart'; -import '../../utils/global.dart'; - -class RequestSOS extends StatefulWidget { - const RequestSOS({super.key}); - - @override - State createState() => _RequestSOSState(); -} - -class _RequestSOSState extends State { - @override - Widget build(BuildContext context) { - return SafeArea( - child: Scaffold( - appBar: AppBar( - centerTitle: true, - title: const Text(sOSTitle), - backgroundColor: second, - ), - body: SingleChildScrollView( - child: Container( - height: screenHeight * .89, - padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 10), - child: Column( - children: [ - SizedBox( - height: blockSizeVertical * 2, - ), - SvgPicture.asset( - 'assets/svgs/request_sos.svg', - height: blockSizeVertical * 22, - allowDrawingOutsideViewBox: true, - ), - Mobile( - title: "09661548775", - subtitle: mobile1, - onPressed: () {}), - const Divider(), - Mobile( - title: "09661548775", - subtitle: mobile2, - onPressed: () {}), - const Divider(), - ListTile( - leading: const Icon( - Typicons.location, - color: second, - ), - title: Text("Latitude/Longitude"), - subtitle: Text( - currentLocation, - style: Theme.of(context).textTheme.caption, - ), - ), - FormBuilderTextField( - name: "message", - validator: FormBuilderValidators.compose([ - FormBuilderValidators.required( - errorText: messageRequired) - ]), - autovalidateMode: AutovalidateMode.onUserInteraction, - maxLines: 5, - decoration: normalTextFieldStyle("", sosMessage), - ), - const Expanded( - child: SizedBox(), - ), - SizedBox( - width: double.infinity, - height: screenHeight * .06, - child: ElevatedButton( - style: secondaryBtnStyle( - second, Colors.transparent, Colors.white54), - child: const Text( - submit, - style: TextStyle(color: Colors.white), - ), - onPressed: () { - // if (_formKey.currentState.validate()) { - // _formKey.currentState.save(); - // BlocProvider.of(context) - // .add(UserWebLogin( - // password: password, - // username: username)); - // } - }, - ), - ), - ], - )), - ), - ), - ); - } -} diff --git a/lib/screens/sos/sos_received.dart b/lib/screens/sos/sos_received.dart deleted file mode 100644 index c89b81c..0000000 --- a/lib/screens/sos/sos_received.dart +++ /dev/null @@ -1,133 +0,0 @@ -import 'package:animate_do/animate_do.dart'; -import 'package:auto_size_text/auto_size_text.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_spinkit/flutter_spinkit.dart'; -import 'package:flutter_svg/svg.dart'; -import 'package:unit2/theme-data.dart/btn-style.dart'; -import 'package:unit2/utils/text_container.dart'; -import '../../theme-data.dart/colors.dart'; -import '../../utils/global.dart'; - -class SOSreceived extends StatelessWidget { - final Function onpressed; - const SOSreceived({required Key key, required this.onpressed}) - : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 35), - height: screenHeight, - child: Stack( - children: [ - Positioned( - bottom: 0, - child: SizedBox( - width: screenWidth, - height: screenHeight / 2, - child: Opacity( - opacity: .2, - child: Image.asset( - "assets/emergency.png", - fit: BoxFit.cover, - ), - ), - )), - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SizedBox( - height: blockSizeVertical * 6, - ), - Bounce( - from: 20, - infinite: true, - delay: const Duration(milliseconds: 800), - child: Stack( - alignment: AlignmentDirectional.topCenter, - children: [ - Container( - margin: const EdgeInsets.only(top: 20), - child: CircleAvatar( - radius: blockSizeVertical * 8, - backgroundColor: second, - child: Container( - margin: const EdgeInsets.only(top: 25), - child: SvgPicture.asset( - 'assets/sos.svg', - height: blockSizeHorizontal * 17, - color: Colors.white, - allowDrawingOutsideViewBox: true, - alignment: Alignment.bottomCenter, - ), - ), - ), - ), - Positioned( - top: blockSizeVertical * 3, - child: const SpinKitPulse( - color: primary, - size: 120, - ), - ), - ], - ), - ), - const SizedBox(height: 16), - SlideInUp( - from: 50, - child: AutoSizeText( - sosReceived, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.displayMedium!.copyWith( - fontSize: 40, - color: third, - fontWeight: FontWeight.w500, - ), - ), - ), - const SizedBox( - height: 8, - ), - SlideInUp( - from: 50, - child: Container( - padding: const EdgeInsets.all(15), - decoration: const BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.all(Radius.circular(15))), - child: AutoSizeText( - sOSReceivedMessage, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.caption!.copyWith( - fontSize: 16, - color: Colors.black87, - ), - ), - ), - ), - const SizedBox( - height: 40, - ), - Expanded(child: Container()), - SlideInUp( - child: SizedBox( - height: 50, - width: 200, - child: TextButton( - style: mainBtnStyle(second, Colors.transparent, second), - onPressed: () {}, - child: const Text(cancelRequest, - style: TextStyle( - color: Colors.white, - )), - ), - ), - ) - ], - ), - ], - ), - ); - } -} diff --git a/lib/screens/unit2/basic-info/basic-info.dart b/lib/screens/unit2/basic-info/basic-info.dart index 6459508..c16824a 100644 --- a/lib/screens/unit2/basic-info/basic-info.dart +++ b/lib/screens/unit2/basic-info/basic-info.dart @@ -5,6 +5,7 @@ import 'package:flutter_svg/svg.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:intl/intl.dart'; import 'package:qr_flutter/qr_flutter.dart'; +import 'package:signature/signature.dart'; import 'package:unit2/model/login_data/user_info/user_data.dart'; import 'package:unit2/theme-data.dart/btn-style.dart'; import 'package:unit2/utils/global.dart'; @@ -12,6 +13,7 @@ import 'package:unit2/utils/text_container.dart'; import '../../../bloc/user/user_bloc.dart'; import '../../../theme-data.dart/colors.dart'; import '../../../widgets/splash_screen.dart'; +import '../signature/signature_pad.dart'; import './components/cover-image.dart'; class BasicInfo extends StatelessWidget { @@ -159,6 +161,9 @@ class BuildInformation extends StatelessWidget { style: mainBtnStyle(third, Colors.transparent, Colors.white54), onPressed: () { + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + return SignaturePad(); + })); }, icon: const Icon( FontAwesome5.signature, diff --git a/lib/screens/unit2/homepage.dart/components/menu-screen.dart b/lib/screens/unit2/homepage.dart/components/menu-screen.dart index 312d2d0..65b3621 100644 --- a/lib/screens/unit2/homepage.dart/components/menu-screen.dart +++ b/lib/screens/unit2/homepage.dart/components/menu-screen.dart @@ -56,7 +56,7 @@ class _MenuScreenState extends State { getTile(FontAwesome5.user_circle, "Profile", '/profile', context, widget.userData!), const Divider(), - getTile(FontAwesome5.life_ring, "Request SOS", '/request-sos', + getTile(FontAwesome5.life_ring, "Request SOS", '/sos', context, widget.userData!), const Divider(), Expanded( diff --git a/lib/screens/unit2/homepage.dart/components/menu.dart b/lib/screens/unit2/homepage.dart/components/menu.dart index 5ddc5cf..10103f7 100644 --- a/lib/screens/unit2/homepage.dart/components/menu.dart +++ b/lib/screens/unit2/homepage.dart/components/menu.dart @@ -30,6 +30,8 @@ Widget getTile( Navigator.pushNamed(context, route,arguments: profileArguments); }if(title.toLowerCase() == 'basic info'){ Navigator.pushNamed(context, '/basic-info'); + }if(title.toLowerCase() == 'request sos'){ + Navigator.pushNamed(context, '/sos'); } }, diff --git a/lib/screens/unit2/login/login.dart b/lib/screens/unit2/login/login.dart index 4c4ef35..bba33c3 100644 --- a/lib/screens/unit2/login/login.dart +++ b/lib/screens/unit2/login/login.dart @@ -78,7 +78,7 @@ class _UniT2LoginState extends State { final progress = ProgressHUD.of(context); progress!.dismiss(); errorAlert(context, "Error Login", state.message, () { - context.read().add(LoadVersion()); + context.read().add(LoadVersion(username: username,password: password)); Navigator.of(context).pop(); }); } @@ -138,9 +138,10 @@ class _UniT2LoginState extends State { SizedBox( height: blockSizeVertical * 3, ), - // USERNAME + //// USERNAME FormBuilderTextField( name: 'username', + initialValue: state.username, validator: FormBuilderValidators.required( errorText: usernameRequired), @@ -153,9 +154,10 @@ class _UniT2LoginState extends State { SizedBox( height: blockSizeVertical * 1.5, ), - // PASSWORD + //// PASSWORD FormBuilderTextField( name: 'password', + initialValue: state.password, validator: FormBuilderValidators.required( errorText: passwordRequired), onChanged: (value) { @@ -222,7 +224,7 @@ class _UniT2LoginState extends State { ), SizedBox( height: blockSizeVertical * 7, - // Login Button + //// Login Button child: SizedBox( width: MediaQuery.of(context).size.width, @@ -296,12 +298,12 @@ class _UniT2LoginState extends State { ), )), SizedBox( - height: blockSizeVertical * 1, + height: blockSizeVertical * 2, ), const LoginViaQr( text: emergencyReponseLabel), SizedBox( - height: blockSizeVertical * 1, + height: blockSizeVertical * 2, ), // REQUEST SOS SizedBox( diff --git a/lib/sevices/sos/sos_service.dart b/lib/sevices/sos/sos_service.dart index 40eb9c8..0cb0697 100644 --- a/lib/sevices/sos/sos_service.dart +++ b/lib/sevices/sos/sos_service.dart @@ -1,5 +1,14 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:flutter/services.dart'; import 'package:intl/intl.dart'; import 'package:location/location.dart'; +import 'package:platform_device_id/platform_device_id.dart'; +import 'package:http/http.dart' as http; +import 'package:unit2/utils/request.dart'; +import '../../model/sos/session.dart'; +import '../../utils/urls.dart'; class SosService{ static final SosService _instance = SosService(); @@ -10,7 +19,8 @@ class SosService{ LocationData? locationData; bool serviceEnabled = false; PermissionStatus permissionGranted; - serviceEnabled = await location.serviceEnabled(); + + serviceEnabled = await location.serviceEnabled(); if (!serviceEnabled) { serviceEnabled = await location.requestService(); if (!serviceEnabled) { @@ -24,8 +34,92 @@ class SosService{ return null; } } + try{ LocationData newLocationData = await location.getLocation(); locationData = newLocationData; return locationData; + }catch(e){ + throw e.toString(); + } + } + Future initPlatformState() async { + String? deviceId; + try { + deviceId = await PlatformDeviceId.getDeviceId; + } on PlatformException { + deviceId = 'Failed to get deviceId.'; + } + return deviceId!; + } + + +Future requestSos({required LocationData location, required String mobile1, + String? mobile2, required String msg, required String requestedDate}) async { + var body; + String deviceId = await initPlatformState(); + String sessionToken = + deviceId.toString() + todayFormat.format(DateTime.now()); + String path = Url.instance.sosRequest(); + SessionData? sessionData; + + if(mobile2 != null){ + body = { + "mobile_id": deviceId.toString(), + "mobile_gps": + "${location.latitude.toString()},${location.longitude.toString()}", + "mobile1": mobile1, + "mobile2": mobile2, + "respondent_message": msg, + "session_token": sessionToken, + "pushedbutton_date": requestedDate + }; + }else{ + body = { + "mobile_id": deviceId.toString(), + "mobile_gps": + "${location.latitude.toString()},${location.longitude.toString()}", + "mobile1": mobile1, + "respondent_message": msg, + "session_token": sessionToken, + "pushedbutton_date": requestedDate + }; + } + + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + + try { + http.Response response = await Request.instance.postRequest(path: path,body: body,headers: headers); + if (response.statusCode == 201) { + var body = jsonDecode(response.body); + SessionData newSessionData = SessionData.fromJson(body['data']); + sessionData = newSessionData; + } + + }on Error catch (e) { + throw (e.toString()); + } + return sessionData!; + } + + + Future checkAcknowledgement(String sessionToken) async { + String path = Url.instance.sosRequest(); + SessionData? sessionData; + try { + http.Response response = await Request.instance.getRequest(path: path,param:{"session_token":sessionToken} ); + if (response.statusCode == 200) { + var body = jsonDecode(response.body); + SessionData newSessionData = SessionData.fromJson(body['data'][0]); + + sessionData = newSessionData; + + } + } on Error + catch (e) { + throw (e.toString()); + } + return sessionData!; } } \ No newline at end of file diff --git a/lib/utils/app_router.dart b/lib/utils/app_router.dart index 3bd227a..9db2326 100644 --- a/lib/utils/app_router.dart +++ b/lib/utils/app_router.dart @@ -47,7 +47,7 @@ class AppRouter { case '/sos': return MaterialPageRoute(builder: (BuildContext context) { return BlocProvider( - create: (context) => SosBloc()..add(LoadUserLocation()), + create: (_) => SosBloc()..add(LoadUserLocation()), child: const SosScreen(), ); }); diff --git a/lib/utils/global.dart b/lib/utils/global.dart index de816db..65b6eaf 100644 --- a/lib/utils/global.dart +++ b/lib/utils/global.dart @@ -12,4 +12,4 @@ double safeBlockVertical = 0; //// hive boxes Box? CREDENTIALS; -Box? SOSCONTACTS; \ No newline at end of file +Box? SOS; \ No newline at end of file diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index 8849535..0d165e9 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -3,10 +3,10 @@ class Url { static Url get instance => _instance; String host() { - // return '192.168.10.221:3003'; - return 'agusandelnorte.gov.ph'; + // return '192.168.10.183:3000'; + // return 'agusandelnorte.gov.ph'; // return "192.168.10.219:3000"; - // return "devweb.agusandelnorte.gov.ph"; + return "devweb.agusandelnorte.gov.ph"; // return 'devapi.agusandelnorte.gov.ph:3004'; } @@ -22,6 +22,11 @@ class Url { return "/api/system_app/apk_version/latest"; } + ////SOS paths + String sosRequest(){ + return "/api/sos_app/sos_request/"; + } + ////ELIGIBILITIES PATHS String eligibilities(){ return "/api/jobnet_app/eligibilities/"; diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index a124bbe..4b37fe9 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -7,9 +7,13 @@ #include "generated_plugin_registrant.h" #include +#include void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) modal_progress_hud_nsn_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "ModalProgressHudNsnPlugin"); modal_progress_hud_nsn_plugin_register_with_registrar(modal_progress_hud_nsn_registrar); + g_autoptr(FlPluginRegistrar) platform_device_id_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "PlatformDeviceIdLinuxPlugin"); + platform_device_id_linux_plugin_register_with_registrar(platform_device_id_linux_registrar); } diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index f6f1987..f64d1cc 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST modal_progress_hud_nsn + platform_device_id_linux ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 4567379..7a1cdf6 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,16 +5,22 @@ import FlutterMacOS import Foundation +import location import modal_progress_hud_nsn import package_info_plus import path_provider_foundation +import platform_device_id +import platform_device_id_macos import shared_preferences_foundation import sqflite func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + LocationPlugin.register(with: registry.registrar(forPlugin: "LocationPlugin")) ModalProgressHudNsnPlugin.register(with: registry.registrar(forPlugin: "ModalProgressHudNsnPlugin")) FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + PlatformDeviceIdMacosPlugin.register(with: registry.registrar(forPlugin: "PlatformDeviceIdMacosPlugin")) + PlatformDeviceIdMacosPlugin.register(with: registry.registrar(forPlugin: "PlatformDeviceIdMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) } diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 42d76d9..d473e2f 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -3,6 +3,8 @@ PODS: - FMDB (2.7.5): - FMDB/standard (= 2.7.5) - FMDB/standard (2.7.5) + - location (0.0.1): + - FlutterMacOS - modal_progress_hud_nsn (0.0.1): - FlutterMacOS - package_info_plus (0.0.1): @@ -10,6 +12,10 @@ PODS: - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS + - platform_device_id (0.0.1): + - FlutterMacOS + - platform_device_id_macos (0.0.1): + - FlutterMacOS - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS @@ -19,9 +25,12 @@ PODS: DEPENDENCIES: - FlutterMacOS (from `Flutter/ephemeral`) + - location (from `Flutter/ephemeral/.symlinks/plugins/location/macos`) - modal_progress_hud_nsn (from `Flutter/ephemeral/.symlinks/plugins/modal_progress_hud_nsn/macos`) - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/macos`) + - platform_device_id (from `Flutter/ephemeral/.symlinks/plugins/platform_device_id/macos`) + - platform_device_id_macos (from `Flutter/ephemeral/.symlinks/plugins/platform_device_id_macos/macos`) - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos`) - sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`) @@ -32,12 +41,18 @@ SPEC REPOS: EXTERNAL SOURCES: FlutterMacOS: :path: Flutter/ephemeral + location: + :path: Flutter/ephemeral/.symlinks/plugins/location/macos modal_progress_hud_nsn: :path: Flutter/ephemeral/.symlinks/plugins/modal_progress_hud_nsn/macos package_info_plus: :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos path_provider_foundation: :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/macos + platform_device_id: + :path: Flutter/ephemeral/.symlinks/plugins/platform_device_id/macos + platform_device_id_macos: + :path: Flutter/ephemeral/.symlinks/plugins/platform_device_id_macos/macos shared_preferences_foundation: :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos sqflite: @@ -46,10 +61,13 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a + location: 7cdb0665bd6577d382b0a343acdadbcb7f964775 modal_progress_hud_nsn: 8099d46c2cf9de7af8fe0a3f8f5d2aa32cf956c3 package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce - path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852 - shared_preferences_foundation: 297b3ebca31b34ec92be11acd7fb0ba932c822ca + path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9 + platform_device_id: 3e414428f45df149bbbfb623e2c0ca27c545b763 + platform_device_id_macos: f763bb55f088be804d61b96eb4710b8ab6598e94 + shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472 sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7 diff --git a/pubspec.lock b/pubspec.lock index 3bb1c30..3e337c3 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -297,6 +297,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + device_info: + dependency: transitive + description: + name: device_info + sha256: f4a8156cb7b7480d969cb734907d18b333c8f0bc0b1ad0b342cdcecf30d62c48 + url: "https://pub.dev" + source: hosted + version: "2.0.3" + device_info_platform_interface: + dependency: transitive + description: + name: device_info_platform_interface + sha256: b148e0bf9640145d09a4f8dea96614076f889e7f7f8b5ecab1c7e5c2dbc73c1b + url: "https://pub.dev" + source: hosted + version: "2.0.1" device_preview: dependency: "direct main" description: @@ -925,6 +941,54 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.0" + platform_device_id: + dependency: "direct main" + description: + name: platform_device_id + sha256: "7a12ec84de4a823bb10eba2f0e1ad29e2365abba17790489a0d78029904f562e" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + platform_device_id_linux: + dependency: transitive + description: + name: platform_device_id_linux + sha256: "994b1608593e527a629af2d5aeb241c60d308d3434bc78b0f6fcb3c1a02dff43" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + platform_device_id_macos: + dependency: transitive + description: + name: platform_device_id_macos + sha256: "968db2a504c611294b12a031b3734432d6df10553a0d3ae3b33ed21abfdbaba0" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + platform_device_id_platform_interface: + dependency: transitive + description: + name: platform_device_id_platform_interface + sha256: c61607594252aaddacf3e4c4371ab08f2ef85ff427817fa6e48a169429610c46 + url: "https://pub.dev" + source: hosted + version: "1.0.0" + platform_device_id_web: + dependency: transitive + description: + name: platform_device_id_web + sha256: "58e124594e1165db7f108395a780b1d1e1cd403021978e5228cf4289fbe736d5" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + platform_device_id_windows: + dependency: transitive + description: + name: platform_device_id_windows + sha256: dbf8dcf03ad8555320ebae2403a3081b79f137f37661874e161fe2de0a84eeeb + url: "https://pub.dev" + source: hosted + version: "1.0.0" plugin_platform_interface: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 5cd2be5..ab0bffe 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -76,6 +76,8 @@ dependencies: hive: ^2.0.5 hive_flutter: ^1.1.0 mask_text_input_formatter: ^2.4.0 + location: ^4.3.0 + platform_device_id: ^1.0.1 dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index c1aa7e0..458cc68 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -8,10 +8,13 @@ #include #include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { ModalProgressHudNsnPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("ModalProgressHudNsnPlugin")); PermissionHandlerWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); + PlatformDeviceIdWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("PlatformDeviceIdWindowsPlugin")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 2446653..4c970ab 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -5,6 +5,7 @@ list(APPEND FLUTTER_PLUGIN_LIST modal_progress_hud_nsn permission_handler_windows + platform_device_id_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST From cf7902f923d34e7a3e7bdc76441f191bf774654d Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Fri, 14 Apr 2023 09:01:11 +0800 Subject: [PATCH 64/86] commit before checkout to develop --- pubspec.lock | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 3e337c3..c34bf28 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -677,30 +677,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" - location: - dependency: "direct main" - description: - name: location - sha256: "9051959f6f2ccadd887b28b66e9cbbcc25b6838e37cf9e894c421ccc0ebf80b5" - url: "https://pub.dev" - source: hosted - version: "4.4.0" - location_platform_interface: - dependency: transitive - description: - name: location_platform_interface - sha256: "62eeaf1658e92e4459b727f55a3c328eccbac8ba043fa6d262ac5286ad48384c" - url: "https://pub.dev" - source: hosted - version: "2.3.0" - location_web: - dependency: transitive - description: - name: location_web - sha256: "6c08c408a040534c0269c4ff9fe17eebb5a36dea16512fbaf116b9c8bc21545b" - url: "https://pub.dev" - source: hosted - version: "3.1.1" logging: dependency: transitive description: From 7f39bd5cd9fab0765e3c57fda71d7f8bffd30dcc Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Tue, 25 Apr 2023 15:50:36 +0800 Subject: [PATCH 65/86] implemented education profile API --- lib/bloc/docsms/docsms_bloc.dart | 28 + lib/bloc/docsms/docsms_event.dart | 15 + lib/bloc/docsms/docsms_state.dart | 28 + .../profile/education/education_bloc.dart | 108 +- .../profile/education/education_event.dart | 58 +- .../profile/education/education_state.dart | 61 +- .../profile/eligibility/eligibility_bloc.dart | 28 +- .../address/address_bloc.dart | 48 + .../address/address_event.dart | 10 + .../address/address_state.dart | 42 +- .../voluntary_works/voluntary_work_bloc.dart | 42 + .../voluntary_works/voluntary_work_event.dart | 8 + .../voluntary_works/voluntary_work_state.dart | 17 +- .../profile/workHistory/workHistory_bloc.dart | 2 +- lib/model/docsms/document.dart | 207 +++ lib/model/profile/educational_background.dart | 2 +- .../{ => components}/request_receipt.dart | 10 +- lib/screens/docsms/index.dart | 52 + .../basic_information/address/add_modal.dart | 474 +++++++ .../basic_information/address/edit_modal.dart | 474 +++++++ .../basic_information/address_screen.dart | 133 +- .../components/education/add_modal.dart | 510 +++++++ .../components/education/edit_modal.dart | 564 ++++++++ .../profile/components/education_screen.dart | 182 ++- .../components/eligibility/add_modal.dart | 714 +++++----- .../components/voluntary_works_screen.dart | 14 +- .../components/work_history/add_modal.dart | 1183 +++++++---------- .../components/work_history/edit_modal.dart | 293 ++-- .../voluntary_works/add_modal.dart | 597 +++++++++ .../profile/shared/add_for_empty_search.dart | 68 + .../homepage.dart/components/dashboard.dart | 277 ++-- .../unit2/homepage.dart/module-screen.dart | 10 +- lib/sevices/docsms/docsms_service.dart | 36 + lib/sevices/profile/education_services.dart | 201 ++- .../profile/work_history_services.dart | 2 +- lib/utils/app_router.dart | 3 + lib/utils/profile_utilities.dart | 88 +- lib/utils/qr_scanner.dart | 17 + lib/utils/urls.dart | 22 +- pubspec.lock | 32 + pubspec.yaml | 2 +- 41 files changed, 5233 insertions(+), 1429 deletions(-) create mode 100644 lib/bloc/docsms/docsms_bloc.dart create mode 100644 lib/bloc/docsms/docsms_event.dart create mode 100644 lib/bloc/docsms/docsms_state.dart create mode 100644 lib/model/docsms/document.dart rename lib/screens/docsms/{ => components}/request_receipt.dart (96%) create mode 100644 lib/screens/docsms/index.dart create mode 100644 lib/screens/profile/components/basic_information/address/add_modal.dart create mode 100644 lib/screens/profile/components/basic_information/address/edit_modal.dart create mode 100644 lib/screens/profile/components/education/add_modal.dart create mode 100644 lib/screens/profile/components/education/edit_modal.dart create mode 100644 lib/screens/profile/components/work_history/voluntary_works/add_modal.dart create mode 100644 lib/screens/profile/shared/add_for_empty_search.dart create mode 100644 lib/sevices/docsms/docsms_service.dart create mode 100644 lib/utils/qr_scanner.dart diff --git a/lib/bloc/docsms/docsms_bloc.dart b/lib/bloc/docsms/docsms_bloc.dart new file mode 100644 index 0000000..be473b2 --- /dev/null +++ b/lib/bloc/docsms/docsms_bloc.dart @@ -0,0 +1,28 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/sevices/docsms/docsms_service.dart'; + +import '../../model/docsms/document.dart'; + +part 'docsms_event.dart'; +part 'docsms_state.dart'; + +class DocsmsBloc extends Bloc { + DocsmsBloc() : super(DocsmsInitial()) { + Document? document; + on((event, emit)async { + emit(DocSmsLoadingState()); + try { + document = await AutoReceiveDocumentServices.instance.getDocument(event.documentId); + if(document != null){ + emit(DocumentLoaded(document: document!)); + }else{ + emit(const DocSmsErrorState(message: "Invalid Qr code")); + } + +} catch (e) { + emit(DocSmsErrorState(message: e.toString())); + } + }); + } +} diff --git a/lib/bloc/docsms/docsms_event.dart b/lib/bloc/docsms/docsms_event.dart new file mode 100644 index 0000000..190fd98 --- /dev/null +++ b/lib/bloc/docsms/docsms_event.dart @@ -0,0 +1,15 @@ +part of 'docsms_bloc.dart'; + +abstract class DocsmsEvent extends Equatable { + const DocsmsEvent(); + + @override + List get props => []; +} + +class LoadDocument extends DocsmsEvent{ +final String documentId; +const LoadDocument({required this.documentId}); + @override + List get props => []; +} diff --git a/lib/bloc/docsms/docsms_state.dart b/lib/bloc/docsms/docsms_state.dart new file mode 100644 index 0000000..c06433a --- /dev/null +++ b/lib/bloc/docsms/docsms_state.dart @@ -0,0 +1,28 @@ +part of 'docsms_bloc.dart'; + +abstract class DocsmsState extends Equatable { + const DocsmsState(); + + @override + List get props => []; +} + +class DocsmsInitial extends DocsmsState {} + +class DocSmsLoadingState extends DocsmsState{ + +} +class DocumentLoaded extends DocsmsState{ + final Document document; + const DocumentLoaded({required this.document}); + @override + List get props => [document]; + +} + +class DocSmsErrorState extends DocsmsState{ + final String message; + const DocSmsErrorState({required this.message}); + @override + List get props => [message]; +} diff --git a/lib/bloc/profile/education/education_bloc.dart b/lib/bloc/profile/education/education_bloc.dart index bb19420..73f08b3 100644 --- a/lib/bloc/profile/education/education_bloc.dart +++ b/lib/bloc/profile/education/education_bloc.dart @@ -8,10 +8,13 @@ part 'education_state.dart'; class EducationBloc extends Bloc { List educationalBackgrounds = []; + List schools = []; + List programs = []; + List honors = []; EducationBloc() : super(EducationInitial()) { on((event, emit) async { emit(EducationalBackgroundLoadingState()); - try { + try { List educations = await EducationService.instace .getEducationalBackground(event.profileId, event.token); educationalBackgrounds = educations; @@ -21,5 +24,108 @@ class EducationBloc extends Bloc { emit(EducationalBackgroundErrorState(message: e.toString())); } }); + //// SHOW ADD FORM + on((event, emit) async { + emit(EducationalBackgroundLoadingState()); + try { + if (schools.isEmpty) { + List newSchools = await EducationService.instace.getSchools(); + schools = newSchools; + } + if (programs.isEmpty) { + List newPrograms = + await EducationService.instace.getPrograms(); + programs = newPrograms; + } + if (honors.isEmpty) { + List newHonors = await EducationService.instace.getHonors(); + honors = newHonors; + } + emit(AddEducationState( + schools: schools, programs: programs, honors: honors)); + } catch (e) { + emit(EducationalBackgroundErrorState(message: e.toString())); + } + }); + ////Add + on((event, emit) async { + Map status = await EducationService.instace.add( + honors: event.honors, + educationalBackground: event.educationalBackground, + token: event.token, + profileId: event.profileId); + if (status['success']) { + EducationalBackground educationalBackground = + EducationalBackground.fromJson(status['data']); + educationalBackgrounds.add(educationalBackground); + emit(EducationAddedState(response: status)); + } else { + emit(EducationAddedState(response: status)); + } + }); + ////Update + on((event, emit) async { + Map status = await EducationService.instace.edit( + honors: event.honors, + educationalBackground: event.educationalBackground, + token: event.token, + profileId: event.profileId); + if (status['success']) { + EducationalBackground educationalBackground = + EducationalBackground.fromJson(status['data']); + educationalBackgrounds.add(educationalBackground); + emit(EditedEducationState(response: status)); + } else { + emit(EditedEducationState(response: status)); + } + }); + ////LOAD + on((event, emit) { + emit(EducationalBackgroundLoadedState( + educationalBackground: educationalBackgrounds)); + }); + //// SHOW EDIT FORM + on((event, emit) async { + try { + if (schools.isEmpty) { + List newSchools = await EducationService.instace.getSchools(); + schools = newSchools; + } + if (programs.isEmpty) { + List newPrograms = + await EducationService.instace.getPrograms(); + programs = newPrograms; + } + if (honors.isEmpty) { + List newHonors = await EducationService.instace.getHonors(); + honors = newHonors; + } + emit(EditEducationState( + schools: schools, + programs: programs, + honors: honors, + educationalBackground: event.educationalBackground)); + } catch (e) { + emit(EducationalBackgroundErrorState(message: e.toString())); + } + }); + ////delete + on((event, emit) async { + try { + final bool success = await EducationService.instace.delete( + profileId: event.profileId, + token: event.token, + educationalBackground: event.educationalBackground); + if (success) { + educationalBackgrounds.removeWhere( + (element) => element.id == event.educationalBackground.id); + emit(EducationDeletedState(success: success)); + } else { + emit(EducationDeletedState(success: success)); + } + } catch (e) { + emit(EducationalBackgroundErrorState(message: e.toString())); + } + }); } } diff --git a/lib/bloc/profile/education/education_event.dart b/lib/bloc/profile/education/education_event.dart index cfe1db7..59a431b 100644 --- a/lib/bloc/profile/education/education_event.dart +++ b/lib/bloc/profile/education/education_event.dart @@ -6,10 +6,60 @@ abstract class EducationEvent extends Equatable { @override List get props => []; } -class GetEducationalBackground extends EducationEvent{ + +class GetEducationalBackground extends EducationEvent { final int profileId; final String token; - const GetEducationalBackground({required this.profileId, required this.token}); - @override - List get props => [profileId,token]; + const GetEducationalBackground( + {required this.profileId, required this.token}); + @override + List get props => [profileId, token]; +} +////show add form +class ShowAddEducationForm extends EducationEvent {} +////show edit form +class ShowEditEducationForm extends EducationEvent { + final EducationalBackground educationalBackground; + final int profileId; + final String token; + const ShowEditEducationForm({required this.educationalBackground,required this.profileId, required this.token}); + @override + List get props => [educationalBackground]; +} +////load +class LoadEducations extends EducationEvent {} +////add +class AddEducation extends EducationEvent { + final List honors; + final EducationalBackground educationalBackground; + final int profileId; + final String token; + const AddEducation( + {required this.educationalBackground, + required this.profileId, + required this.token, required this.honors}); + @override + List get props => [educationalBackground, profileId, token]; +} +////update education +class UpdateEducation extends EducationEvent{ + final List honors; + final EducationalBackground educationalBackground; + final int profileId; + final String token; + const UpdateEducation( + {required this.educationalBackground, + required this.profileId, + required this.token,required this.honors}); + @override + List get props => [educationalBackground, profileId, token]; +} +////delete +class DeleteEducation extends EducationEvent{ + final int profileId; + final String token; + final EducationalBackground educationalBackground; + const DeleteEducation({required this.educationalBackground, required this.profileId, required this.token}); + @override + List get props => [educationalBackground, profileId, token]; } diff --git a/lib/bloc/profile/education/education_state.dart b/lib/bloc/profile/education/education_state.dart index cc75963..2a0f1ec 100644 --- a/lib/bloc/profile/education/education_state.dart +++ b/lib/bloc/profile/education/education_state.dart @@ -2,28 +2,73 @@ part of 'education_bloc.dart'; abstract class EducationState extends Equatable { const EducationState(); - + @override List get props => []; } class EducationInitial extends EducationState {} - - -class EducationalBackgroundLoadedState extends EducationState{ +class EducationalBackgroundLoadedState extends EducationState { final List educationalBackground; const EducationalBackgroundLoadedState({required this.educationalBackground}); - @override + @override List get props => [educationalBackground]; } -class EducationalBackgroundErrorState extends EducationState{ +class EducationalBackgroundErrorState extends EducationState { final String message; const EducationalBackgroundErrorState({required this.message}); - @override + @override List get props => [message]; } -class EducationalBackgroundLoadingState extends EducationState{ +class EducationalBackgroundLoadingState extends EducationState {} + +////Add +class AddEducationState extends EducationState { + final List schools; + final List programs; + final List honors; + const AddEducationState( + {required this.honors, required this.programs, required this.schools}); + @override + List get props => [schools, programs, honors]; +} + +////Edit +class EditEducationState extends EducationState { + final EducationalBackground educationalBackground; + final List schools; + final List programs; + final List honors; + const EditEducationState({ + required this.educationalBackground, + required this.honors, + required this.programs, + required this.schools, + }); +} + +//// Added State +class EducationAddedState extends EducationState { + final Map response; + const EducationAddedState({required this.response}); + @override + List get props => [response]; +} +//// Edited State +class EditedEducationState extends EducationState { + final Map response; + const EditedEducationState({required this.response}); + @override + List get props => [response]; +} + +////deleted State +class EducationDeletedState extends EducationState { + final bool success; + const EducationDeletedState({required this.success}); + @override + List get props => [success]; } diff --git a/lib/bloc/profile/eligibility/eligibility_bloc.dart b/lib/bloc/profile/eligibility/eligibility_bloc.dart index 28c0627..4f03727 100644 --- a/lib/bloc/profile/eligibility/eligibility_bloc.dart +++ b/lib/bloc/profile/eligibility/eligibility_bloc.dart @@ -14,8 +14,8 @@ part 'eligibility_state.dart'; class EligibilityBloc extends Bloc { EligibilityBloc() : super(EligibilityInitial()) { - List? globalCountries; - List? globalRegions; + List globalCountries = []; + List globalRegions = []; List globalEligibilities = []; List eligibilities = []; //// LOAD ELIGIBILTY @@ -67,11 +67,11 @@ class EligibilityBloc extends Bloc { //// SHOW EDIT FORM on((event, emit) async { try { - if (globalCountries == null) { + if (globalCountries.isEmpty) { List countries = await LocationUtils.instance.getCountries(); globalCountries = countries; } - if (globalRegions == null) { + if (globalRegions.isEmpty) { List regions = await LocationUtils.instance.getRegions(); globalRegions = regions; } @@ -84,13 +84,13 @@ class EligibilityBloc extends Bloc { (Eligibility eligibility) => event.eligibityCert.eligibility!.id == eligibility.id); bool? isOverseas = event.eligibityCert.overseas; - Country currentCountry = globalCountries!.firstWhere( + Country currentCountry = globalCountries.firstWhere( (Country country) => event.eligibityCert.examAddress!.country!.code == country.code); if (event.eligibityCert.examAddress?.cityMunicipality?.province ?.region != null) { - Region currrentRegion = globalRegions!.firstWhere((Region region) => + Region currrentRegion = globalRegions.firstWhere((Region region) => event.eligibityCert.examAddress!.cityMunicipality!.province! .region!.code == region.code); @@ -117,8 +117,8 @@ class EligibilityBloc extends Bloc { cities: cities, isOverseas: isOverseas!, eligibityCert: event.eligibityCert, - countries: globalCountries!, - regions: globalRegions!, + countries: globalCountries, + regions: globalRegions, eligibilities: globalEligibilities)); } else { emit(EditEligibilityState( @@ -131,8 +131,8 @@ class EligibilityBloc extends Bloc { currentEligibility: currentEligibility, isOverseas: isOverseas!, eligibityCert: event.eligibityCert, - countries: globalCountries!, - regions: globalRegions!, + countries: globalCountries, + regions: globalRegions, eligibilities: globalEligibilities)); } } catch (e) { @@ -164,7 +164,7 @@ class EligibilityBloc extends Bloc { //// SHOW ADD FORM on((event, emit) async { emit(EligibilityLoadingState()); - if (globalRegions == null) { + if (globalRegions.isEmpty) { List regions = await LocationUtils.instance.getRegions(); globalRegions = regions; } @@ -173,15 +173,15 @@ class EligibilityBloc extends Bloc { await ProfileUtilities.instance.getEligibilities(); globalEligibilities = eligibilities; } - if (globalCountries == null) { + if (globalCountries.isEmpty) { List countries = await LocationUtils.instance.getCountries(); globalCountries = countries; } emit(AddEligibilityState( eligibilities: globalEligibilities, - regions: globalRegions!, - countries: globalCountries!)); + regions: globalRegions, + countries: globalCountries)); }); //// ADD diff --git a/lib/bloc/profile/primary_information/address/address_bloc.dart b/lib/bloc/profile/primary_information/address/address_bloc.dart index 30195bc..00f09c0 100644 --- a/lib/bloc/profile/primary_information/address/address_bloc.dart +++ b/lib/bloc/profile/primary_information/address/address_bloc.dart @@ -1,13 +1,21 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:unit2/model/location/barangay.dart'; +import '../../../../model/location/city.dart'; +import '../../../../model/location/country.dart'; +import '../../../../model/location/provinces.dart'; +import '../../../../model/location/region.dart'; import '../../../../model/profile/basic_information/adress.dart'; +import '../../../../utils/location_utilities.dart'; part 'address_event.dart'; part 'address_state.dart'; class AddressBloc extends Bloc { AddressBloc() : super(AddressInitial()) { + List globalCountries=[]; + List globalRegions=[]; List addresses = []; on((event, emit) { emit(AddressLoadingState()); @@ -17,6 +25,46 @@ class AddressBloc extends Bloc { }catch(e){ emit(AddressErrorState(message: e.toString())); } + //// show add form + });on((event,emit)async{ + emit(AddressLoadingState()); + try{ + if (globalRegions.isEmpty) { + List regions = await LocationUtils.instance.getRegions(); + globalRegions = regions; + } + if (globalCountries.isEmpty) { + List countries = await LocationUtils.instance.getCountries(); + globalCountries = countries; + } + emit(AddAddressState(countries: globalCountries, regions: globalRegions)); + }catch(e){ + emit(AddressErrorState(message: e.toString())); + } + }); + + //// Show Edit Form + on((event,emit)async{ + + try{ + if (globalRegions.isEmpty) { + List regions = await LocationUtils.instance.getRegions(); + globalRegions = regions; + } + if (globalCountries.isEmpty) { + List countries = await LocationUtils.instance.getCountries(); + globalCountries = countries; + } + emit(EditAddressState(countries: globalCountries, regions: globalRegions,address: event.address)); + }catch(e){ + emit(AddressErrorState(message: e.toString())); + } + }); + + ////call error state + on((event, emit) { + emit(const AddressErrorState( + message: "Something went wrong. Please try again")); }); } } diff --git a/lib/bloc/profile/primary_information/address/address_event.dart b/lib/bloc/profile/primary_information/address/address_event.dart index 1f4ba71..f1d36f4 100644 --- a/lib/bloc/profile/primary_information/address/address_event.dart +++ b/lib/bloc/profile/primary_information/address/address_event.dart @@ -13,3 +13,13 @@ class GetAddress extends AddressEvent{ @override List get props => [addresses]; } +class ShowAddAddressForm extends AddressEvent{ + +} +class ShowEditAddressForm extends AddressEvent{ + final MainAdress address; + const ShowEditAddressForm({required this.address}); +} +class CallErrorState extends AddressEvent{ + +} diff --git a/lib/bloc/profile/primary_information/address/address_state.dart b/lib/bloc/profile/primary_information/address/address_state.dart index fcdeea3..563a5f4 100644 --- a/lib/bloc/profile/primary_information/address/address_state.dart +++ b/lib/bloc/profile/primary_information/address/address_state.dart @@ -2,26 +2,46 @@ part of 'address_bloc.dart'; abstract class AddressState extends Equatable { const AddressState(); - + @override List get props => []; } class AddressInitial extends AddressState {} - -class AddressLoadedState extends AddressState{ - final List addresses; - const AddressLoadedState({required this.addresses}); - @override +//// LOADED STATE +class AddressLoadedState extends AddressState { + final List addresses; + const AddressLoadedState({required this.addresses}); + @override List get props => [addresses]; } - -class AddressErrorState extends AddressState{ +////ERROR STATE +class AddressErrorState extends AddressState { final String message; const AddressErrorState({required this.message}); - @override + @override List get props => [message]; } -class AddressLoadingState extends AddressState{ - +//// LOADING STATE +class AddressLoadingState extends AddressState {} + +////ADD ADDRESS STATE +class AddAddressState extends AddressState { + final List countries; + final List regions; + const AddAddressState( + { + required this.countries, + required this.regions}); + @override + List get props => [countries, regions,]; } + +class EditAddressState extends AddressState{ + final MainAdress address; + final List countries; + final List regions; + const EditAddressState({required this.address, required this.countries, required this.regions}); + @override + List get props => [countries, regions,address]; +} \ No newline at end of file diff --git a/lib/bloc/profile/voluntary_works/voluntary_work_bloc.dart b/lib/bloc/profile/voluntary_works/voluntary_work_bloc.dart index d2bbd22..0480f9c 100644 --- a/lib/bloc/profile/voluntary_works/voluntary_work_bloc.dart +++ b/lib/bloc/profile/voluntary_works/voluntary_work_bloc.dart @@ -1,8 +1,14 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:unit2/sevices/profile/volunatary_services.dart'; +import 'package:unit2/utils/profile_utilities.dart'; +import '../../../model/location/country.dart'; +import '../../../model/location/region.dart'; import '../../../model/profile/voluntary_works.dart'; +import '../../../model/utils/agency.dart'; +import '../../../model/utils/category.dart'; +import '../../../model/utils/position.dart'; part 'voluntary_work_event.dart'; part 'voluntary_work_state.dart'; @@ -10,6 +16,12 @@ part 'voluntary_work_state.dart'; class VoluntaryWorkBloc extends Bloc { VoluntaryWorkBloc() : super(VoluntaryWorkInitial()) { List voluntaryWorks = []; + List globalCountries=[]; + List agencyCategory = []; + List globalRegions=[]; + List agencyPositions = []; + List agencies = []; + ////Get Voluntary Works on((event, emit) async{ emit(VoluntaryWorkLoadingState()); try{ @@ -20,5 +32,35 @@ class VoluntaryWorkBloc extends Bloc { emit(VoluntaryWorkErrorState(message: e.toString())); } }); + //// Show Add form Event + on((event,emit)async{ + try{ + emit(VoluntaryWorkLoadingState()); + if (agencyPositions.isEmpty) { + List positions = + await ProfileUtilities.instance.getAgencyPosition(); + agencyPositions = positions; + } + + /////AGENCIES------------------------------------------ + if (agencies.isEmpty) { + List newAgencies = + await ProfileUtilities.instance.getAgecies(); + agencies = newAgencies; + } + + /////Category Agency------------------------------------------ + if (agencyCategory.isEmpty) { + List categoryAgencies = + await ProfileUtilities.instance.agencyCategory(); + agencyCategory = categoryAgencies; + } + emit(AddVoluntaryWorkState(agencies: agencies,positions: agencyPositions,agencyCategory: agencyCategory,regions: globalRegions,countries: globalCountries)); + }catch(e){ + emit(VoluntaryWorkErrorState(message: e.toString())); + } + });on((event,emit){ + emit(VoluntaryWorkErrorState(message: event.message)); + }); } } diff --git a/lib/bloc/profile/voluntary_works/voluntary_work_event.dart b/lib/bloc/profile/voluntary_works/voluntary_work_event.dart index 0e62010..52aba14 100644 --- a/lib/bloc/profile/voluntary_works/voluntary_work_event.dart +++ b/lib/bloc/profile/voluntary_works/voluntary_work_event.dart @@ -14,3 +14,11 @@ class GetVoluntarWorks extends VoluntaryWorkEvent{ @override List get props => [profileId,token]; } +class ShowAddVoluntaryWorks extends VoluntaryWorkEvent{ + +} + +class ShowErrorState extends VoluntaryWorkEvent{ + final String message; + const ShowErrorState({required this.message}); +} diff --git a/lib/bloc/profile/voluntary_works/voluntary_work_state.dart b/lib/bloc/profile/voluntary_works/voluntary_work_state.dart index 8c2c912..3024e12 100644 --- a/lib/bloc/profile/voluntary_works/voluntary_work_state.dart +++ b/lib/bloc/profile/voluntary_works/voluntary_work_state.dart @@ -8,20 +8,33 @@ abstract class VoluntaryWorkState extends Equatable { } class VoluntaryWorkInitial extends VoluntaryWorkState {} +////Loaded State class VoluntaryWorkLoadedState extends VoluntaryWorkState{ final List voluntaryWorks; const VoluntaryWorkLoadedState({required this.voluntaryWorks}); @override List get props => [voluntaryWorks]; } - +////Error State class VoluntaryWorkErrorState extends VoluntaryWorkState{ final String message; const VoluntaryWorkErrorState({required this.message}); @override List get props => [message]; } - +////Loading State class VoluntaryWorkLoadingState extends VoluntaryWorkState{ } + + +class AddVoluntaryWorkState extends VoluntaryWorkState{ + final List positions; + final List agencies; + final List agencyCategory; + final List countries; + final List regions; + const AddVoluntaryWorkState({required this.agencies,required this.countries, required this.positions, required this.regions,required this.agencyCategory}); + @override + List get props => [positions,agencies,countries,regions]; +} \ No newline at end of file diff --git a/lib/bloc/profile/workHistory/workHistory_bloc.dart b/lib/bloc/profile/workHistory/workHistory_bloc.dart index d19eca5..a3c9f4b 100644 --- a/lib/bloc/profile/workHistory/workHistory_bloc.dart +++ b/lib/bloc/profile/workHistory/workHistory_bloc.dart @@ -26,7 +26,7 @@ class WorkHistoryBloc extends Bloc { try { if (workExperiences.isEmpty) { List works = await WorkHistoryService.instance - .getWorkExperiences(event.profileId!, event.token!); + .getWorkExperiences(event.profileId, event.token); workExperiences = works; } diff --git a/lib/model/docsms/document.dart b/lib/model/docsms/document.dart new file mode 100644 index 0000000..354ca18 --- /dev/null +++ b/lib/model/docsms/document.dart @@ -0,0 +1,207 @@ +// To parse this JSON data, do +// +// final document = documentFromJson(jsonString); + +class Document { + Document({ + required this.id, + required this.sk, + required this.dsk, + required this.slug, + required this.files, + required this.title, + required this.agency, + required this.office, + required this.subject, + required this.fileType, + required this.isPublic, + required this.createdAt, + required this.createdBy, + required this.validatedAt, + required this.validatedBy, + required this.documentType, + required this.documentRoutes, + required this.alternateMobile, + required this.attachedDocument, + required this.enableNotification, + required this.createdByPersonId, + }); + + final int? id; + final String? sk; + final String? dsk; + final String? slug; + final List? files; + final String? title; + final String? agency; + final String? office; + final String? subject; + final FileType? fileType; + final bool? isPublic; + final String? createdAt; + final String? createdBy; + final String? validatedAt; + final String? validatedBy; + final DocumentType? documentType; + final dynamic documentRoutes; + final AlternateMobile? alternateMobile; + final dynamic attachedDocument; + final bool? enableNotification; + final int? createdByPersonId; + + factory Document.fromJson(Map json) => Document( + id: json["id"], + sk: json["sk"], + dsk: json["dsk"], + slug: json["slug"], + files: List.from(json["files"].map((x) => FileElement.fromJson(x))), + title: json["title"], + agency: json["agency"], + office: json["office"], + subject: json["subject"], + fileType: FileType.fromJson(json["file_type"]), + isPublic: json["is_public"], + createdAt: json["created_at"], + createdBy: json["created_by"], + validatedAt: json["validated_at"], + validatedBy: json["validated_by"], + documentType: DocumentType.fromJson(json["document_type"]), + documentRoutes: json["document_routes"], + alternateMobile: AlternateMobile.fromJson(json["alternate_mobile"]), + attachedDocument: json["attached_document"], + enableNotification: json["enable_notification"], + createdByPersonId: json["created_by_person_id"], + ); + + Map toJson() => { + "id": id, + "sk": sk, + "dsk": dsk, + "slug": slug, + "files": List.from(files!.map((x) => x.toJson())), + "title": title, + "agency": agency, + "office": office, + "subject": subject, + "file_type": fileType?.toJson(), + "is_public": isPublic, + "created_at": createdAt, + "created_by": createdBy, + "validated_at": validatedAt, + "validated_by": validatedBy, + "document_type": documentType?.toJson(), + "document_routes": documentRoutes, + "alternate_mobile": alternateMobile?.toJson(), + "attached_document": attachedDocument, + "enable_notification": enableNotification, + "created_by_person_id": createdByPersonId, + }; +} + +class AlternateMobile { + AlternateMobile({ + required this.id, + required this.mobileNo, + required this.documentId, + }); + + final dynamic id; + final dynamic mobileNo; + final dynamic documentId; + + factory AlternateMobile.fromJson(Map json) => AlternateMobile( + id: json["id"], + mobileNo: json["mobile_no"], + documentId: json["document_id"], + ); + + Map toJson() => { + "id": id, + "mobile_no": mobileNo, + "document_id": documentId, + }; +} + +class DocumentType { + DocumentType({ + required this.id, + required this.name, + required this.slug, + required this.categoryId, + required this.priorityNumber, + }); + + final int id; + final String name; + final dynamic slug; + final int categoryId; + final dynamic priorityNumber; + + factory DocumentType.fromJson(Map json) => DocumentType( + id: json["id"], + name: json["name"], + slug: json["slug"], + categoryId: json["category_id"], + priorityNumber: json["priority_number"], + ); + + Map toJson() => { + "id": id, + "name": name, + "slug": slug, + "category_id": categoryId, + "priority_number": priorityNumber, + }; +} + +class FileType { + FileType({ + required this.id, + required this.name, + required this.isActive, + required this.extensions, + }); + + final int id; + final String name; + final bool isActive; + final List extensions; + + factory FileType.fromJson(Map json) => FileType( + id: json["id"], + name: json["name"], + isActive: json["is_active"], + extensions: List.from(json["extensions"].map((x) => x)), + ); + + Map toJson() => { + "id": id, + "name": name, + "is_active": isActive, + "extensions": List.from(extensions.map((x) => x)), + }; +} + +class FileElement { + FileElement({ + required this.path, + required this.extnsn, + required this.dateTime, + }); + + final String path; + final String extnsn; + final String dateTime; + + factory FileElement.fromJson(Map json) => FileElement( + path: json["path"], + extnsn: json["extnsn"], + dateTime: json["date_time"], + ); + + Map toJson() => { + "path": path, + "extnsn": extnsn, + "date_time": dateTime, + }; +} diff --git a/lib/model/profile/educational_background.dart b/lib/model/profile/educational_background.dart index 83c2086..4deaee4 100644 --- a/lib/model/profile/educational_background.dart +++ b/lib/model/profile/educational_background.dart @@ -26,7 +26,7 @@ class EducationalBackground { final String? periodTo; final dynamic attachments; final String? periodFrom; - final dynamic unitsEarned; + final int? unitsEarned; final String? yearGraduated; factory EducationalBackground.fromJson(Map json) => EducationalBackground( diff --git a/lib/screens/docsms/request_receipt.dart b/lib/screens/docsms/components/request_receipt.dart similarity index 96% rename from lib/screens/docsms/request_receipt.dart rename to lib/screens/docsms/components/request_receipt.dart index b72897a..d2e2491 100644 --- a/lib/screens/docsms/request_receipt.dart +++ b/lib/screens/docsms/components/request_receipt.dart @@ -9,10 +9,10 @@ import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/costum_divider.dart'; import 'package:unit2/widgets/text_icon.dart'; -import '../../test_data.dart'; -import '../../theme-data.dart/btn-style.dart'; -import '../../theme-data.dart/form-style.dart'; -import '../../utils/global.dart'; +import '../../../test_data.dart'; +import '../../../theme-data.dart/btn-style.dart'; +import '../../../theme-data.dart/form-style.dart'; +import '../../../utils/global.dart'; class RequetAutoReceipt extends StatefulWidget { RequetAutoReceipt({super.key}); @@ -124,7 +124,7 @@ class _RequetAutoReceiptState extends State { height: screenHeight * .06, child: ElevatedButton( style: secondaryBtnStyle( - second, Colors.transparent, Colors.white54), + primary, Colors.transparent, Colors.white54), child: const Text( requestAutoReceipt, style: TextStyle(color: Colors.white), diff --git a/lib/screens/docsms/index.dart b/lib/screens/docsms/index.dart new file mode 100644 index 0000000..d1730f0 --- /dev/null +++ b/lib/screens/docsms/index.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:unit2/bloc/docsms/docsms_bloc.dart'; +import 'package:unit2/screens/docsms/components/request_receipt.dart'; +import 'package:unit2/widgets/error_state.dart'; + +class AutoReceiveDocument extends StatefulWidget { + const AutoReceiveDocument({super.key}); + + @override + State createState() => _AutoReceiveDocumentState(); +} + +class _AutoReceiveDocumentState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle( + color: Colors.white, + + ), + child: BlocConsumer( + listener: (context, state) { + if (state is DocSmsLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is DocSmsErrorState || state is DocumentLoaded) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + }, + builder: (context, state) { + if (state is DocumentLoaded) { + return RequetAutoReceipt(); + }if(state is DocSmsErrorState){ + return SomethingWentWrong(message: state.message.toString(), onpressed: (){}); + } + return Container(); + }, + ), + ), + ); + } +} diff --git a/lib/screens/profile/components/basic_information/address/add_modal.dart b/lib/screens/profile/components/basic_information/address/add_modal.dart new file mode 100644 index 0000000..c790d4d --- /dev/null +++ b/lib/screens/profile/components/basic_information/address/add_modal.dart @@ -0,0 +1,474 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; +import 'package:unit2/bloc/profile/primary_information/address/address_bloc.dart'; +import 'package:unit2/model/utils/category.dart'; +import 'package:unit2/utils/global.dart'; + +import '../../../../../model/location/barangay.dart'; +import '../../../../../model/location/city.dart'; +import '../../../../../model/location/country.dart'; +import '../../../../../model/location/provinces.dart'; +import '../../../../../model/location/region.dart'; +import '../../../../../theme-data.dart/btn-style.dart'; +import '../../../../../theme-data.dart/colors.dart'; +import '../../../../../theme-data.dart/form-style.dart'; +import '../../../../../utils/location_utilities.dart'; +import '../../../../../utils/text_container.dart'; + +class AddAddressScreen extends StatefulWidget { + final int profileId; + final String token; + const AddAddressScreen( + {super.key, required this.profileId, required this.token}); + + @override + State createState() => _AddAddressScreenState(); +} + +class _AddAddressScreenState extends State { + ////boolean + bool hasLotandBlock = false; + bool overseas = false; + bool provinceCall = false; + bool cityCall = false; + bool barangayCall = false; + ////selected + Region? selectedRegion; + Province? selectedProvince; + CityMunicipality? selectedMunicipality; + Barangay? selectedBarangay; + Country? selectedCountry; + ////Lists + final List areaClass = [ + const Area(value: "Rural Area", group: 0), + const Area(value: "Sitio", group: 1), + const Area(value: "Village", group: 1), + const Area(value: "Urban Area", group: 0), + const Area(value: "Town", group: 1), + const Area(value: "City", group: 1), + const Area(value: "Metropolis", group: 1), + const Area(value: "Megacity", group: 1), + ]; + final List category = ["Permanent", "Residential", "Birthplace"]; + List? provinces; + List? citymuns; + List? barangays; + final formKey = GlobalKey(); + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is AddAddressState) { + return SingleChildScrollView( + child: SizedBox( + height: screenHeight * .90, + child: FormBuilder( + key: formKey, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, horizontal: 18), + child: Column( + children: [ + //// category + FormBuilderDropdown( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: + normalTextFieldStyle("Category*", "Category"), + name: "category", + onChanged: (String? category) {}, + items: category.map>( + (String category) { + return DropdownMenuItem( + value: category, child: Text(category)); + }).toList()), + const SizedBox( + height: 12, + ), + ////Area Class + FormBuilderDropdown( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: normalTextFieldStyle( + "Area class *", "Area class"), + name: "area_class", + onChanged: (Area? area) {}, + items: areaClass + .map>((Area area) { + return area.group == 0 + ? DropdownMenuItem( + enabled: false, + value: area, + child: Text(area.value.toUpperCase(), + style: const TextStyle( + color: Colors.black38))) + : DropdownMenuItem( + value: area, + enabled: true, + child: Text( + " ${area.value.toUpperCase()}")); + }).toList()), + const SizedBox( + height: 12, + ), + ////stateful builder + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + ////with block & Lot Switch + FormBuilderSwitch( + initialValue: hasLotandBlock, + activeColor: second, + onChanged: (value) { + setState(() { + hasLotandBlock = value!; + }); + }, + decoration: normalTextFieldStyle( + "With Lot and Block?", 'Graduated?'), + name: 'graudated', + title: Text(hasLotandBlock ? "YES" : "NO"), + ), + SizedBox( + height: hasLotandBlock ? 12 : 0, + ), + SizedBox( + width: screenWidth, + child: hasLotandBlock + ? Row( + children: [ + ////block # + Flexible( + flex: 1, + child: FormBuilderTextField( + name: "block_number", + decoration: + normalTextFieldStyle( + "Block #*", "Block #"), + )), + const SizedBox( + width: 8, + ), + //// lot # + Flexible( + flex: 1, + child: FormBuilderTextField( + name: "lot_number", + decoration: + normalTextFieldStyle( + "Lot #*", "Lot #"), + )), + ], + ) + : Container(), + ), + ], + ); + }), + const SizedBox( + height: 12, + ), + //// Address Line + FormBuilderTextField( + name: "address_line", + decoration: normalTextFieldStyle( + "Address Line *", "Address Line"), + ), + const SizedBox( + height: 12, + ), + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: normalTextFieldStyle( + "Overseas Address?", ''), + name: 'overseas', + title: Text(overseas ? "YES" : "NO"), + ), + SizedBox( + height: overseas == true ? 8 : 0, + ), + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + FormBuilderDropdown( + autovalidateMode: AutovalidateMode + .onUserInteraction, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + onChanged: (Region? region) async { + if (selectedRegion != region) { + setState(() { + provinceCall = true; + }); + selectedRegion = region; + getProvinces(); + } + }, + initialValue: null, + decoration: normalTextFieldStyle( + "Region*", "Region"), + name: 'region', + items: state.regions + .map>( + (Region region) { + return DropdownMenuItem( + value: region, + child: Text( + region.description!)); + }).toList(), + ), + const SizedBox( + height: 8, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + value: selectedProvince, + onChanged: + (Province? province) { + if (selectedProvince != + province) { + setState(() { + cityCall = true; + }); + selectedProvince = + province; + getCities(); + } + }, + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province province) { + return DropdownMenuItem( + value: province, + child: FittedBox( + child: Text(province + .description!), + )); + }).toList(), + decoration: + normalTextFieldStyle( + "Province*", + "Province")), + ), + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: DropdownButtonFormField< + CityMunicipality>( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + isExpanded: true, + onChanged: + (CityMunicipality? city) { + if (selectedMunicipality != + city) { + setState(() { + barangayCall = true; + }); + selectedMunicipality = city; + getBarangays(); + } + }, + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), + ), + ), + ), + //// BARANGAY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: barangayCall, + child: DropdownButtonFormField< + Barangay>( + isExpanded: true, + onChanged: (Barangay? baragay) { + selectedBarangay = baragay; + }, + decoration: + normalTextFieldStyle( + "Barangay*", + "Barangay"), + value: selectedBarangay, + items: barangays == null + ? [] + : barangays!.map< + DropdownMenuItem< + Barangay>>( + (Barangay barangay) { + return DropdownMenuItem( + value: barangay, + child: Text(barangay + .description!)); + }).toList(), + ), + ), + ), + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( + height: 60, + child: FormBuilderDropdown( + initialValue: null, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + items: state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox( + child: + Text(country.name!))); + }).toList(), + name: 'country', + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) {}, + ), + ), + ), + + ], + ); + }), + ////sumit button + const Expanded(child: SizedBox()), + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () {}, + child: const Text(submit)), + ), + const SizedBox( + height: 20, + ), + ], + ), + + )), + ), + ); + } + return Placeholder(); + }, + ); + } + + Future getProvinces() async { + try { + List newProvinces = await LocationUtils.instance + .getProvinces(regionCode: selectedRegion!.code.toString()); + setState(() { + provinces = newProvinces; + selectedProvince = provinces![0]; + provinceCall = false; + cityCall = true; + getCities(); + }); + } catch (e) { + context.read().add(CallErrorState()); + } + } + + Future getCities() async { + try { + List newCities = await LocationUtils.instance + .getCities(code: selectedProvince!.code.toString()); + citymuns = newCities; + setState(() { + selectedMunicipality = newCities[0]; + cityCall = false; + barangayCall = true; + getBarangays(); + }); + } catch (e) { + context.read().add(CallErrorState()); + } + } + + Future getBarangays() async { + try { + List newBarangays = await LocationUtils.instance + .getBarangay(code: selectedMunicipality!.code.toString()); + barangays = newBarangays; + setState(() { + selectedBarangay = newBarangays[0]; + barangayCall = false; + }); + } catch (e) { + context.read().add(CallErrorState()); + } + } +} + +class Area { + final int group; + final String value; + const Area({required this.group, required this.value}); +} diff --git a/lib/screens/profile/components/basic_information/address/edit_modal.dart b/lib/screens/profile/components/basic_information/address/edit_modal.dart new file mode 100644 index 0000000..5760b17 --- /dev/null +++ b/lib/screens/profile/components/basic_information/address/edit_modal.dart @@ -0,0 +1,474 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; +import 'package:unit2/bloc/profile/primary_information/address/address_bloc.dart'; +import 'package:unit2/model/utils/category.dart'; +import 'package:unit2/utils/global.dart'; + +import '../../../../../model/location/barangay.dart'; +import '../../../../../model/location/city.dart'; +import '../../../../../model/location/country.dart'; +import '../../../../../model/location/provinces.dart'; +import '../../../../../model/location/region.dart'; +import '../../../../../theme-data.dart/btn-style.dart'; +import '../../../../../theme-data.dart/colors.dart'; +import '../../../../../theme-data.dart/form-style.dart'; +import '../../../../../utils/location_utilities.dart'; +import '../../../../../utils/text_container.dart'; + +class EditAddressScreen extends StatefulWidget { + final int profileId; + final String token; + const EditAddressScreen( + {super.key, required this.profileId, required this.token}); + + @override + State createState() => _EditAddressScreenState(); +} + +class _EditAddressScreenState extends State { + ////boolean + bool hasLotandBlock = false; + bool overseas = false; + bool provinceCall = false; + bool cityCall = false; + bool barangayCall = false; + ////selected + Region? selectedRegion; + Province? selectedProvince; + CityMunicipality? selectedMunicipality; + Barangay? selectedBarangay; + Country? selectedCountry; + ////Lists + final List areaClass = [ + const Area(value: "Rural Area", group: 0), + const Area(value: "Sitio", group: 1), + const Area(value: "Village", group: 1), + const Area(value: "Urban Area", group: 0), + const Area(value: "Town", group: 1), + const Area(value: "City", group: 1), + const Area(value: "Metropolis", group: 1), + const Area(value: "Megacity", group: 1), + ]; + final List category = ["Permanent", "Residential", "Birthplace"]; + List? provinces; + List? citymuns; + List? barangays; + final formKey = GlobalKey(); + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is EditAddressState) { + return SingleChildScrollView( + child: SizedBox( + height: screenHeight * .90, + child: FormBuilder( + key: formKey, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, horizontal: 18), + child: Column( + children: [ + //// category + FormBuilderDropdown( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: + normalTextFieldStyle("Category*", "Category"), + name: "category", + onChanged: (String? category) {}, + items: category.map>( + (String category) { + return DropdownMenuItem( + value: category, child: Text(category)); + }).toList()), + const SizedBox( + height: 12, + ), + ////Area Class + FormBuilderDropdown( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: normalTextFieldStyle( + "Area class *", "Area class"), + name: "area_class", + onChanged: (Area? area) {}, + items: areaClass + .map>((Area area) { + return area.group == 0 + ? DropdownMenuItem( + enabled: false, + value: area, + child: Text(area.value.toUpperCase(), + style: const TextStyle( + color: Colors.black38))) + : DropdownMenuItem( + value: area, + enabled: true, + child: Text( + " ${area.value.toUpperCase()}")); + }).toList()), + const SizedBox( + height: 12, + ), + ////stateful builder + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + ////with block & Lot Switch + FormBuilderSwitch( + initialValue: hasLotandBlock, + activeColor: second, + onChanged: (value) { + setState(() { + hasLotandBlock = value!; + }); + }, + decoration: normalTextFieldStyle( + "With Lot and Block?", 'Graduated?'), + name: 'graudated', + title: Text(hasLotandBlock ? "YES" : "NO"), + ), + SizedBox( + height: hasLotandBlock ? 12 : 0, + ), + SizedBox( + width: screenWidth, + child: hasLotandBlock + ? Row( + children: [ + ////block # + Flexible( + flex: 1, + child: FormBuilderTextField( + name: "block_number", + decoration: + normalTextFieldStyle( + "Block #*", "Block #"), + )), + const SizedBox( + width: 8, + ), + //// lot # + Flexible( + flex: 1, + child: FormBuilderTextField( + name: "lot_number", + decoration: + normalTextFieldStyle( + "Lot #*", "Lot #"), + )), + ], + ) + : Container(), + ), + ], + ); + }), + const SizedBox( + height: 12, + ), + //// Address Line + FormBuilderTextField( + name: "address_line", + decoration: normalTextFieldStyle( + "Address Line *", "Address Line"), + ), + const SizedBox( + height: 12, + ), + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: normalTextFieldStyle( + "Overseas Address?", ''), + name: 'overseas', + title: Text(overseas ? "YES" : "NO"), + ), + SizedBox( + height: overseas == true ? 8 : 0, + ), + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + FormBuilderDropdown( + autovalidateMode: AutovalidateMode + .onUserInteraction, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + onChanged: (Region? region) async { + if (selectedRegion != region) { + setState(() { + provinceCall = true; + }); + selectedRegion = region; + getProvinces(); + } + }, + initialValue: null, + decoration: normalTextFieldStyle( + "Region*", "Region"), + name: 'region', + items: state.regions + .map>( + (Region region) { + return DropdownMenuItem( + value: region, + child: Text( + region.description!)); + }).toList(), + ), + const SizedBox( + height: 8, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + value: selectedProvince, + onChanged: + (Province? province) { + if (selectedProvince != + province) { + setState(() { + cityCall = true; + }); + selectedProvince = + province; + getCities(); + } + }, + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province province) { + return DropdownMenuItem( + value: province, + child: FittedBox( + child: Text(province + .description!), + )); + }).toList(), + decoration: + normalTextFieldStyle( + "Province*", + "Province")), + ), + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: DropdownButtonFormField< + CityMunicipality>( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + isExpanded: true, + onChanged: + (CityMunicipality? city) { + if (selectedMunicipality != + city) { + setState(() { + barangayCall = true; + }); + selectedMunicipality = city; + getBarangays(); + } + }, + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), + ), + ), + ), + //// BARANGAY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: barangayCall, + child: DropdownButtonFormField< + Barangay>( + isExpanded: true, + onChanged: (Barangay? baragay) { + selectedBarangay = baragay; + }, + decoration: + normalTextFieldStyle( + "Barangay*", + "Barangay"), + value: selectedBarangay, + items: barangays == null + ? [] + : barangays!.map< + DropdownMenuItem< + Barangay>>( + (Barangay barangay) { + return DropdownMenuItem( + value: barangay, + child: Text(barangay + .description!)); + }).toList(), + ), + ), + ), + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( + height: 60, + child: FormBuilderDropdown( + initialValue: null, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + items: state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox( + child: + Text(country.name!))); + }).toList(), + name: 'country', + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) {}, + ), + ), + ), + + ], + ); + }), + ////sumit button + const Expanded(child: SizedBox()), + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () {}, + child: const Text(submit)), + ), + const SizedBox( + height: 20, + ), + ], + ), + + )), + ), + ); + } + return Placeholder(); + }, + ); + } + + Future getProvinces() async { + try { + List newProvinces = await LocationUtils.instance + .getProvinces(regionCode: selectedRegion!.code.toString()); + setState(() { + provinces = newProvinces; + selectedProvince = provinces![0]; + provinceCall = false; + cityCall = true; + getCities(); + }); + } catch (e) { + context.read().add(CallErrorState()); + } + } + + Future getCities() async { + try { + List newCities = await LocationUtils.instance + .getCities(code: selectedProvince!.code.toString()); + citymuns = newCities; + setState(() { + selectedMunicipality = newCities[0]; + cityCall = false; + barangayCall = true; + getBarangays(); + }); + } catch (e) { + context.read().add(CallErrorState()); + } + } + + Future getBarangays() async { + try { + List newBarangays = await LocationUtils.instance + .getBarangay(code: selectedMunicipality!.code.toString()); + barangays = newBarangays; + setState(() { + selectedBarangay = newBarangays[0]; + barangayCall = false; + }); + } catch (e) { + context.read().add(CallErrorState()); + } + } +} + +class Area { + final int group; + final String value; + const Area({required this.group, required this.value}); +} diff --git a/lib/screens/profile/components/basic_information/address_screen.dart b/lib/screens/profile/components/basic_information/address_screen.dart index b394883..9801069 100644 --- a/lib/screens/profile/components/basic_information/address_screen.dart +++ b/lib/screens/profile/components/basic_information/address_screen.dart @@ -1,10 +1,15 @@ +import 'package:app_popup_menu/app_popup_menu.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; import 'package:unit2/bloc/profile/primary_information/address/address_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/basic_information/adress.dart'; +import 'package:unit2/screens/profile/components/basic_information/address/add_modal.dart'; +import 'package:unit2/screens/profile/components/basic_information/address/edit_modal.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; @@ -13,23 +18,37 @@ import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; -class AddressScreen extends StatelessWidget { +import '../../../../utils/alerts.dart'; +class AddressScreen extends StatelessWidget { const AddressScreen({super.key}); @override Widget build(BuildContext context) { + int? profileId; + String? token; return Scaffold( appBar: AppBar( title: const Text(adressScreenTitle), centerTitle: true, backgroundColor: primary, - actions: [AddLeading(onPressed: () {})], + actions: [ + AddLeading(onPressed: () { + context.read().add(ShowAddAddressForm()); + }) + ], ), body: ProgressHUD( + padding: const EdgeInsets.all(24), + indicatorWidget: const SpinKitFadingCircle( + color: Colors.white, + ), + backgroundColor: Colors.black87, child: BlocBuilder( builder: (context, state) { if (state is UserLoggedIn) { + token = state.userData!.user!.login!.token; + profileId = state.userData!.user!.login!.user!.profileId; return BlocBuilder( builder: (context, state) { if (state is ProfileLoaded) { @@ -40,7 +59,9 @@ class AddressScreen extends StatelessWidget { progress!.showWithText("Please wait..."); } if (state is AddressLoadedState || - state is AddressErrorState) { + state is AddressErrorState || + state is AddAddressState || + state is EditAddressState) { final progress = ProgressHUD.of(context); progress!.dismiss(); } @@ -48,7 +69,7 @@ class AddressScreen extends StatelessWidget { builder: (context, state) { if (state is AddressLoadedState) { if (state.addresses.isNotEmpty) { - return ListView.builder( + return ListView.builder( padding: const EdgeInsets.symmetric( vertical: 8, horizontal: 10), itemCount: state.addresses.length, @@ -57,25 +78,25 @@ class AddressScreen extends StatelessWidget { String? subdivision = state.addresses[index].details ?? ''; String category = state.addresses[index] - .address! - .category! - .name!; + .address!.category!.name!; String? barangay = state.addresses[index] - .address! - .barangay != + .address!.barangay != null ? '${state.addresses[index].address!.barangay!.description!.toUpperCase()},' : ''; - String cityMunicipality = state.addresses[index] + String cityMunicipality = state + .addresses[index] .address! .cityMunicipality! .description!; - String province = state.addresses[index] + String province = state + .addresses[index] .address! .cityMunicipality! .province! .description!; - String region = state.addresses[index] + String region = state + .addresses[index] .address! .cityMunicipality! .province! @@ -128,12 +149,59 @@ class AddressScreen extends StatelessWidget { ), ], )), - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - )) + AppPopupMenu( + offset: + const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + ////delete eligibilty-= = = = = = = = =>> + if (value == 2) { + confirmAlert(context, () { + final progress = + ProgressHUD.of( + context); + progress!.showWithText( + "Loading..."); + }, "Delete?", + "Confirm Delete?"); + } + if (value == 1) { + ////edit eligibilty-= = = = = = = = =>> + final progress = + ProgressHUD.of( + context); + progress!.showWithText( + "Loading..."); + + context + .read() + .add(ShowEditAddressForm( + address: state + .addresses[ + index])); + } + }, + menuItems: [ + popMenuItem( + text: "Edit", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Delete", + value: 2, + icon: Icons.delete), + popMenuItem( + text: "Attachment", + value: 3, + icon: + FontAwesome.attach) + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ) ]), ), ], @@ -150,6 +218,16 @@ class AddressScreen extends StatelessWidget { "You don't have address added. Please click + to add."); } } + if (state is AddressErrorState) { + return SomethingWentWrong( + message: state.message, onpressed: () {}); + } + if (state is AddAddressState) { + return AddAddressScreen( + profileId: profileId!, token: token!); + }if(state is EditAddressState){ + return EditAddressScreen(profileId: profileId!, token: token!); + } return Container(); }, ); @@ -163,4 +241,23 @@ class AddressScreen extends StatelessWidget { ), )); } + + PopupMenuItem popMenuItem({String? text, int? value, IconData? icon}) { + return PopupMenuItem( + value: value, + child: Row( + children: [ + Icon( + icon, + ), + const SizedBox( + width: 10, + ), + Text( + text!, + ), + ], + ), + ); + } } diff --git a/lib/screens/profile/components/education/add_modal.dart b/lib/screens/profile/components/education/add_modal.dart new file mode 100644 index 0000000..a4701e1 --- /dev/null +++ b/lib/screens/profile/components/education/add_modal.dart @@ -0,0 +1,510 @@ +import 'package:date_time_picker/date_time_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:searchfield/searchfield.dart'; +import 'package:unit2/bloc/profile/education/education_bloc.dart'; +import 'package:unit2/model/profile/educational_background.dart'; +import 'package:unit2/theme-data.dart/box_shadow.dart'; +import 'package:unit2/theme-data.dart/form-style.dart'; +import 'package:unit2/utils/global.dart'; +import 'package:multi_dropdown/multiselect_dropdown.dart'; + +import '../../../../theme-data.dart/btn-style.dart'; +import '../../../../theme-data.dart/colors.dart'; +import '../../../../utils/text_container.dart'; +import '../../shared/add_for_empty_search.dart'; + +class AddEducationScreen extends StatefulWidget { + final int profileId; + final String token; + const AddEducationScreen( + {super.key, required this.profileId, required this.token}); + + @override + State createState() => _AddEducationScreenState(); +} + +class _AddEducationScreenState extends State { + final List educationLevel = [ + const EducationLevel(type: "label", value: "Basic Education", group: 1), + const EducationLevel(type: "level", value: "Elementary", group: 1), + const EducationLevel(type: "level", value: "Secondary (non-K12)", group: 1), + const EducationLevel(type: "level", value: "Junior High", group: 1), + const EducationLevel(type: "level", value: "Senior High", group: 1), + const EducationLevel(type: "label", value: "Higher Education", group: 2), + const EducationLevel(type: "level", value: "Collegiate", group: 2), + const EducationLevel(type: "level", value: "Vocational", group: 2), + const EducationLevel(type: "label", value: "Post Graduate", group: 2), + const EducationLevel(type: "level", value: "Masteral", group: 2), + const EducationLevel(type: "level", value: "Doctorate", group: 2), + ]; + List valueItemHonorList = []; +////selected + EducationLevel? selectedLevel; + School? selectedSchool; + Course? selectedProgram; + List selectedHonors = []; + List? selectedValueItem; + final formKey = GlobalKey(); + ////congrollers + final addProgramController = TextEditingController(); + final addSchoolController = TextEditingController(); + final fromController = TextEditingController(); + final untilController = TextEditingController(); + final yearGraduated = TextEditingController(); + ////focus node + final programFocusNode = FocusNode(); + final schoolFocusNode = FocusNode(); + final honorFocusNode = FocusNode(); + ////booleans + bool graduated = true; + int? unitsEarned; + //// + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is AddEducationState) { + valueItemHonorList = state.honors.map((Honor honor) { + return ValueItem(label: honor.name!, value: honor.name); + }).toList(); + return Container( + padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 18), + child: FormBuilder( + key: formKey, + child: SingleChildScrollView( + child: SizedBox( + height: blockSizeVertical * 85, + child: Column(children: [ + const SizedBox( + height: 12, + ), + //// LEVEL + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderDropdown( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: + normalTextFieldStyle("level*", "level"), + name: "education_level", + onChanged: (EducationLevel? level) { + setState(() { + selectedLevel = level; + }); + }, + items: educationLevel + .map>( + (EducationLevel level) { + return level.type == "label" + ? DropdownMenuItem( + enabled: false, + value: level, + child: Text(level.value.toUpperCase(), + style: const TextStyle( + color: Colors.black38))) + : DropdownMenuItem( + value: level, + enabled: true, + child: Text( + " ${level.value.toUpperCase()}")); + }).toList()), + const SizedBox( + height: 12, + ), + ////school + StatefulBuilder(builder: (context, setState) { + return SearchField( + itemHeight: 50, + suggestionsDecoration: box1(), + suggestions: state.schools + .map((School school) => + SearchFieldListItem(school.name!, + item: school, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10), + child: ListTile( + title: Text(school.name!)), + ))) + .toList(), + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + focusNode: schoolFocusNode, + searchInputDecoration: + normalTextFieldStyle("School *", "").copyWith( + suffixIcon: + const Icon(Icons.arrow_drop_down)), + onSuggestionTap: (school) { + setState(() { + selectedSchool = school.item; + schoolFocusNode.unfocus(); + }); + }, + emptyWidget: EmptyWidget( + title: "Add School", + controller: addSchoolController, + onpressed: () { + setState(() { + School newSchool = School( + id: null, + name: addSchoolController.text.toUpperCase()); + state.schools.insert(0, newSchool); + addSchoolController.text = ""; + + Navigator.pop(context); + }); + }), + ); + }), + const SizedBox( + height: 12, + ), + ////Programs + Container( + child: selectedLevel != null && + selectedLevel!.group != 1 + ? SearchField( + itemHeight: 50, + suggestionsDecoration: box1(), + suggestions: state.programs + .map((Course program) => + SearchFieldListItem( + program.program!, + item: program, + child: Padding( + padding: const EdgeInsets + .symmetric( + horizontal: 10), + child: ListTile( + title: Text( + program.program!)), + ))) + .toList(), + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + focusNode: programFocusNode, + searchInputDecoration: + normalTextFieldStyle( + "Course/Programs *", "") + .copyWith( + suffixIcon: const Icon( + Icons.arrow_drop_down)), + onSuggestionTap: (position) { + setState(() { + selectedProgram = position.item; + programFocusNode.unfocus(); + }); + }, + emptyWidget: EmptyWidget( + title: "Add Program", + controller: addProgramController, + onpressed: () { + setState(() { + Course newProgram = Course( + id: null, + program: addProgramController + .text.toUpperCase()); + state.programs.insert(0,newProgram); + addProgramController.text = ""; + + Navigator.pop(context); + }); + }), + ) + : Container()) + ], + ); + }), + const SizedBox( + height: 12, + ), + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + //// GRADUATED SWITCH + FormBuilderSwitch( + initialValue: graduated, + activeColor: second, + onChanged: (value) { + setState(() { + graduated = value!; + if (graduated) { + unitsEarned = null; + } else { + yearGraduated.text = ""; + } + }); + }, + decoration: normalTextFieldStyle( + "Graduated?", 'Graduated?'), + name: 'graudated', + title: Text(graduated ? "YES" : "NO"), + ), + const SizedBox( + height: 12, + ), + ////FROM + SizedBox( + width: screenWidth, + child: Row( + children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This fied is required"), + decoration: + normalTextFieldStyle("from *", "from"), + name: "", + controller: fromController, + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + content: SizedBox( + width: 300, + height: 300, + child: YearPicker( + firstDate: DateTime( + DateTime.now().year - 100, + 1), + lastDate: DateTime( + DateTime.now().year + 100, + 1), + initialDate: DateTime.now(), + selectedDate: DateTime.now(), + onChanged: (DateTime dateTime) { + fromController.text = + dateTime.year.toString(); + Navigator.pop(context); + }, + ), + ), + ); + }, + ); + }, + ), + ), + const SizedBox( + width: 8, + ), + ////UNTIL + Flexible( + flex: 1, + child: FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This fied is required"), + decoration: normalTextFieldStyle( + "until *", "until"), + name: "", + controller: untilController, + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + content: SizedBox( + width: 300, + height: 300, + child: YearPicker( + firstDate: DateTime( + DateTime.now().year - 100, + 1), + lastDate: DateTime( + DateTime.now().year + 100, + 1), + initialDate: DateTime.now(), + selectedDate: DateTime.now(), + onChanged: (DateTime dateTime) { + untilController.text = + dateTime.year.toString(); + Navigator.pop(context); + }, + ), + ), + ); + }, + ); + }, + ), + ), + const SizedBox( + width: 8, + ), + ], + ), + ), + const SizedBox( + height: 12, + ), + SizedBox( + child: graduated + ////GRADUATED YEAR + ? FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This fied is required"), + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + content: SizedBox( + width: 300, + height: 300, + child: YearPicker( + firstDate: DateTime( + DateTime.now().year - 100, + 1), + lastDate: DateTime( + DateTime.now().year + 100, + 1), + initialDate: DateTime.now(), + selectedDate: DateTime.now(), + onChanged: (DateTime dateTime) { + yearGraduated.text = + dateTime.year.toString(); + Navigator.pop(context); + }, + ), + ), + ); + }, + ); + }, + name: "year_graduated", + controller: yearGraduated, + decoration: normalTextFieldStyle( + "Year Graduated *", "Year Graduated *"), + ) + //// HIGHEST UNITS EARNED + : FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This fied is required"), + name: "units_earned", + decoration: normalTextFieldStyle( + "Highest Level/Units Earned *", + "Highest Level/Units Earned *")), + ), + ], + ); + }), + + const SizedBox( + height: 12, + ), + //// HONORS + MultiSelectDropDown( + onOptionSelected: (List selectedOptions) { + selectedValueItem = selectedOptions; + }, + borderColor: Colors.grey, + borderWidth: 1, + borderRadius: 5, + hint: "Honors", + padding: const EdgeInsets.all(8), + options: valueItemHonorList, + selectionType: SelectionType.multi, + chipConfig: const ChipConfig(wrapType: WrapType.wrap), + dropdownHeight: 300, + optionTextStyle: const TextStyle(fontSize: 16), + selectedOptionIcon: const Icon(Icons.check_circle), + ), + ////sumit button + const Expanded(child: SizedBox()), + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: + mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState!.saveAndValidate()) { + //// program + if (selectedLevel!.value == "Elementary" || + selectedLevel!.value == + "Secondary (non-K12)" || + selectedLevel!.value == "Junior High" || + selectedLevel!.value == "Senior High") { + selectedProgram = null; + } + if (!graduated) { + unitsEarned = int.parse(formKey + .currentState!.value['units_earned']); + } + ////education + Education newEducation = Education( + id: null, + level: selectedLevel!.value, + course: selectedProgram, + school: selectedSchool); + ////honors + if (selectedValueItem != null) { + for (var honor in selectedValueItem!) { + Honor newHonor = state.honors.firstWhere((element) => element.name == honor.value); + selectedHonors.add(newHonor); + } + } + + EducationalBackground educationalBackground = + EducationalBackground( + id: null, + honors: null, + education: newEducation, + periodTo: untilController.text, + periodFrom: fromController.text, + yearGraduated: + graduated ? yearGraduated.text : null, + unitsEarned: !graduated ? unitsEarned : null, + attachments: null, + ); + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add(AddEducation( + educationalBackground: educationalBackground, + profileId: widget.profileId, + token: widget.token, + honors: selectedHonors)); + } + }, + child: const Text(submit)), + ), + const SizedBox( + height: 20, + ), + ]), + ), + ), + ), + ); + } + return Container(); + }, + ); + } +} + +class EducationLevel { + final String value; + final String type; + final int group; + const EducationLevel( + {required this.type, required this.value, required this.group}); +} diff --git a/lib/screens/profile/components/education/edit_modal.dart b/lib/screens/profile/components/education/edit_modal.dart new file mode 100644 index 0000000..1038bbf --- /dev/null +++ b/lib/screens/profile/components/education/edit_modal.dart @@ -0,0 +1,564 @@ +import 'package:date_time_picker/date_time_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:searchfield/searchfield.dart'; +import 'package:unit2/bloc/profile/education/education_bloc.dart'; +import 'package:unit2/model/profile/educational_background.dart'; +import 'package:unit2/theme-data.dart/box_shadow.dart'; +import 'package:unit2/theme-data.dart/form-style.dart'; +import 'package:unit2/utils/global.dart'; + +import '../../../../theme-data.dart/btn-style.dart'; +import '../../../../theme-data.dart/colors.dart'; +import '../../../../utils/text_container.dart'; +import '../../shared/add_for_empty_search.dart'; +import 'package:multi_dropdown/multiselect_dropdown.dart'; + +class EditEducationScreen extends StatefulWidget { + final int profileId; + final String token; + const EditEducationScreen( + {super.key, required this.profileId, required this.token}); + + @override + State createState() => _EditEducationScreenState(); +} + +class _EditEducationScreenState extends State { + final List educationLevel = [ + const EducationLevel(type: "label", value: "Basic Education", group: 1), + const EducationLevel(type: "level", value: "Elementary", group: 1), + const EducationLevel(type: "level", value: "Secondary (non-K12)", group: 1), + const EducationLevel(type: "level", value: "Junior High", group: 1), + const EducationLevel(type: "level", value: "Senior High", group: 1), + const EducationLevel(type: "label", value: "Higher Education", group: 2), + const EducationLevel(type: "level", value: "Collegiate", group: 2), + const EducationLevel(type: "level", value: "Vocational", group: 2), + const EducationLevel(type: "label", value: "Post Graduate", group: 2), + const EducationLevel(type: "level", value: "Masteral", group: 2), + const EducationLevel(type: "level", value: "Doctorate", group: 2), + ]; + List valueItemHonorList = []; + List selectedValueItem = []; +////selected + EducationLevel? selectedLevel; + School? selectedSchool; + Course? selectedProgram; + List selectedHonors = []; + final formKey = GlobalKey(); + ////congrollers + final addProgramController = TextEditingController(); + final addSchoolController = TextEditingController(); + final fromController = TextEditingController(); + final untilController = TextEditingController(); + final yearGraduated = TextEditingController(); + final currentSchoolController = TextEditingController(); + final currentProgramController = TextEditingController(); + final unitsController = TextEditingController(); ////focus node + final programFocusNode = FocusNode(); + final schoolFocusNode = FocusNode(); + final honorFocusNode = FocusNode(); + ////booleans + bool graduated = true; + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is EditEducationState) { + ////selected level + selectedLevel = educationLevel.firstWhere((element) => + element.value.toUpperCase() == + state.educationalBackground.education!.level); + + currentSchoolController.text = + state.educationalBackground.education!.school!.name!; + if (selectedLevel != null && selectedLevel!.group != 1) { + currentProgramController.text = + state.educationalBackground.education!.course!.program!; + } +////year grduated and units earned + if (state.educationalBackground.yearGraduated != null) { + graduated = true; + yearGraduated.text = state.educationalBackground.yearGraduated!; + } else { + unitsController.text = + state.educationalBackground.unitsEarned.toString(); + graduated = false; + } + fromController.text = state.educationalBackground.periodFrom!; + untilController.text = state.educationalBackground.periodTo!; + ////honors + + ////get all honors + valueItemHonorList = state.honors.map((Honor honor) { + return ValueItem(label: honor.name!, value: honor.name); + }).toList(); + return Container( + padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 18), + child: FormBuilder( + key: formKey, + child: SingleChildScrollView( + child: SizedBox( + height: blockSizeVertical * 85, + child: Column(children: [ + const SizedBox( + height: 12, + ), + //// LEVEL + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderDropdown( + initialValue: selectedLevel, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: + normalTextFieldStyle("level*", "level"), + name: "education_level", + onChanged: (EducationLevel? level) { + setState(() { + selectedLevel = level; + }); + }, + items: educationLevel + .map>( + (EducationLevel level) { + return level.type == "label" + ? DropdownMenuItem( + enabled: false, + value: level, + child: Text(level.value.toUpperCase(), + style: const TextStyle( + color: Colors.black38))) + : DropdownMenuItem( + value: level, + enabled: true, + child: Text( + " ${level.value.toUpperCase()}")); + }).toList()), + const SizedBox( + height: 12, + ), + ////school + StatefulBuilder(builder: (context, setState) { + return SearchField( + controller: currentSchoolController, + itemHeight: 50, + suggestionsDecoration: box1(), + suggestions: state.schools + .map((School school) => + SearchFieldListItem(school.name!, + item: school, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10), + child: ListTile( + title: Text(school.name!)), + ))) + .toList(), + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + focusNode: schoolFocusNode, + searchInputDecoration: + normalTextFieldStyle("School *", "").copyWith( + suffixIcon: + const Icon(Icons.arrow_drop_down)), + onSuggestionTap: (school) { + setState(() { + selectedSchool = school.item; + schoolFocusNode.unfocus(); + }); + }, + emptyWidget: EmptyWidget( + title: "Add School", + controller: addSchoolController, + onpressed: () { + setState(() { + School newSchool = School( + id: null, + name: addSchoolController.text + .toUpperCase()); + state.schools.insert(0, newSchool); + addSchoolController.text = ""; + + Navigator.pop(context); + }); + }), + ); + }), + const SizedBox( + height: 12, + ), + ////Programs + Container( + child: selectedLevel != null && + selectedLevel!.group != 1 + ? SearchField( + suggestionAction: + SuggestionAction.unfocus, + controller: currentProgramController, + itemHeight: 50, + suggestionsDecoration: box1(), + suggestions: state.programs + .map((Course program) => + SearchFieldListItem( + program.program!, + item: program, + child: Padding( + padding: const EdgeInsets + .symmetric( + horizontal: 10), + child: ListTile( + title: Text( + program.program!)), + ))) + .toList(), + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + focusNode: programFocusNode, + searchInputDecoration: + normalTextFieldStyle( + "Course/Programs *", "") + .copyWith( + suffixIcon: const Icon( + Icons.arrow_drop_down)), + onSuggestionTap: (position) { + setState(() { + selectedProgram = position.item; + programFocusNode.unfocus(); + }); + }, + emptyWidget: EmptyWidget( + title: "Add Program", + controller: addProgramController, + onpressed: () { + setState(() { + Course newProgram = Course( + id: null, + program: addProgramController + .text + .toUpperCase()); + state.programs + .insert(0, newProgram); + addProgramController.text = ""; + + Navigator.pop(context); + }); + }), + ) + : Container()) + ], + ); + }), + const SizedBox( + height: 12, + ), + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + //// GRADUATED SWITCH + FormBuilderSwitch( + initialValue: graduated, + activeColor: second, + onChanged: (value) { + setState(() { + graduated = value!; + if (graduated) { + unitsController.text = ""; + } else { + yearGraduated.text = ""; + } + }); + }, + decoration: normalTextFieldStyle( + "Graduated?", 'Graduated?'), + name: 'graudated', + title: Text(graduated ? "YES" : "NO"), + ), + const SizedBox( + height: 12, + ), + ////FROM + SizedBox( + width: screenWidth, + child: Row( + children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This fied is required"), + decoration: + normalTextFieldStyle("from *", "from"), + name: "", + controller: fromController, + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + content: SizedBox( + width: 300, + height: 300, + child: YearPicker( + firstDate: DateTime( + DateTime.now().year - 100, + 1), + lastDate: DateTime( + DateTime.now().year + 100, + 1), + initialDate: DateTime.now(), + selectedDate: DateTime.now(), + onChanged: (DateTime dateTime) { + fromController.text = + dateTime.year.toString(); + Navigator.pop(context); + }, + ), + ), + ); + }, + ); + }, + ), + ), + const SizedBox( + width: 8, + ), + ////UNTIL + Flexible( + flex: 1, + child: FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This fied is required"), + decoration: normalTextFieldStyle( + "until *", "until"), + name: "", + controller: untilController, + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + content: SizedBox( + width: 300, + height: 300, + child: YearPicker( + firstDate: DateTime( + DateTime.now().year - 100, + 1), + lastDate: DateTime( + DateTime.now().year + 100, + 1), + initialDate: DateTime.now(), + selectedDate: DateTime.now(), + onChanged: (DateTime dateTime) { + untilController.text = + dateTime.year.toString(); + Navigator.pop(context); + }, + ), + ), + ); + }, + ); + }, + ), + ), + const SizedBox( + width: 8, + ), + Flexible( + flex: 1, + child: graduated + ////GRADUATED YEAR + ? FormBuilderTextField( + validator: + FormBuilderValidators.required( + errorText: + "This fied is required"), + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + content: SizedBox( + width: 300, + height: 300, + child: YearPicker( + firstDate: DateTime( + DateTime.now().year - + 100, + 1), + lastDate: DateTime( + DateTime.now().year + + 100, + 1), + initialDate: + DateTime.now(), + selectedDate: + DateTime.now(), + onChanged: + (DateTime dateTime) { + yearGraduated.text = + dateTime.year + .toString(); + Navigator.pop(context); + }, + ), + ), + ); + }, + ); + }, + name: "year_graduated", + controller: yearGraduated, + decoration: normalTextFieldStyle( + "Year Graduated *", + "Year Graduated *"), + ) + //// HIGHEST UNITS EARNED + : FormBuilderTextField( + validator: + FormBuilderValidators.required( + errorText: + "This fied is required"), + controller: unitsController, + name: "units_earned", + decoration: normalTextFieldStyle( + "Highest Level/Units Earned *", + "Highest Level/Units Earned *")), + ) + ], + ), + ) + ], + ); + }), + const SizedBox( + height: 12, + ), + //// HONORS + + StatefulBuilder(builder: (context, setState) { + return MultiSelectDropDown( + onOptionSelected: (List selectedOptions) { + selectedValueItem = selectedOptions; + }, + borderColor: Colors.grey, + borderWidth: 1, + borderRadius: 5, + hint: "Honors", + padding: const EdgeInsets.all(8), + options: valueItemHonorList, + selectionType: SelectionType.multi, + chipConfig: const ChipConfig(wrapType: WrapType.wrap), + dropdownHeight: 300, + optionTextStyle: const TextStyle(fontSize: 16), + selectedOptionIcon: const Icon(Icons.check_circle), + selectedOptions: + (state.educationalBackground.honors!.isNotEmpty && + state.educationalBackground.honors != null) + ? selectedValueItem = state + .educationalBackground.honors! + .map((Honor honor) => ValueItem( + label: honor.name!, value: honor.name)) + .toList() + : [], + ); + }), + ////sumit button + const Expanded(child: SizedBox()), + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: + mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState!.saveAndValidate()) { + //// program + if (selectedLevel!.value == "Elementary" || + selectedLevel!.value == + "Secondary (non-K12)" || + selectedLevel!.value == "Junior High" || + selectedLevel!.value == "Senior High") { + selectedProgram = null; + }else{ + selectedProgram ??= state.educationalBackground.education!.course; + } + selectedSchool ??= state.educationalBackground.education!.school; + ////education + Education newEducation = Education( + id: null, + level: selectedLevel!.value, + course: selectedProgram, + school: selectedSchool); + ////honors + if (selectedValueItem.isNotEmpty) { + for (var honor in selectedValueItem) { + Honor newHonor = state.honors.firstWhere( + (element) => element.name == honor.value); + selectedHonors.add(newHonor); + } + } + EducationalBackground educationalBackground = + EducationalBackground( + id: state.educationalBackground.id, + honors: null, + education: newEducation, + periodTo: untilController.text, + periodFrom: fromController.text, + yearGraduated: + graduated ? yearGraduated.text : null, + unitsEarned: !graduated + ? int.parse(unitsController.text) + : null, + attachments: null, + ); + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add(UpdateEducation( + educationalBackground: educationalBackground, + profileId: widget.profileId, + token: widget.token, + honors: selectedHonors)); + } + }, + child: const Text(submit)), + ), + const SizedBox( + height: 20, + ), + ]), + ), + ), + ), + ); + } + return Container(); + }, + ); + } +} + +class EducationLevel { + final String value; + final String type; + final int group; + const EducationLevel( + {required this.type, required this.value, required this.group}); +} diff --git a/lib/screens/profile/components/education_screen.dart b/lib/screens/profile/components/education_screen.dart index 4771db8..a7f68df 100644 --- a/lib/screens/profile/components/education_screen.dart +++ b/lib/screens/profile/components/education_screen.dart @@ -1,11 +1,14 @@ +import 'package:app_popup_menu/app_popup_menu.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/educational_background.dart'; +import 'package:unit2/screens/profile/components/education/add_modal.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; @@ -14,18 +17,26 @@ import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; import '../../../bloc/profile/education/education_bloc.dart'; +import '../../../utils/alerts.dart'; +import 'education/edit_modal.dart'; class EducationScreen extends StatelessWidget { const EducationScreen({super.key}); @override Widget build(BuildContext context) { + int profileId; + String? token; return Scaffold( appBar: AppBar( title: const Text(educationScreenTitle), centerTitle: true, backgroundColor: primary, - actions: [AddLeading(onPressed: () {})], + actions: [ + AddLeading(onPressed: () { + context.read().add(ShowAddEducationForm()); + }) + ], ), //userbloc body: ProgressHUD( @@ -35,6 +46,8 @@ class EducationScreen extends StatelessWidget { child: BlocBuilder( builder: (context, state) { if (state is UserLoggedIn) { + token = state.userData!.user!.login!.token; + profileId = state.userData!.user!.login!.user!.profileId!; //profilebloc return BlocBuilder( builder: (context, state) { @@ -47,10 +60,77 @@ class EducationScreen extends StatelessWidget { progress!.showWithText("Please wait..."); } if (state is EducationalBackgroundLoadedState || - state is EducationalBackgroundErrorState) { + state is EducationalBackgroundErrorState || + state is AddEducationState || + state is EditEducationState || + state is EducationDeletedState || state is EditedEducationState) { final progress = ProgressHUD.of(context); progress!.dismiss(); } + ////ADDED STATE + if (state is EducationAddedState) { + if (state.response['success']) { + successAlert(context, "Adding Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context + .read() + .add(LoadEducations()); + }); + } else { + errorAlert(context, "Adding Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context + .read() + .add(LoadEducations()); + }); + } + } + ////EDITED STATE + + if (state is EditedEducationState) { + if (state.response['success']) { + successAlert(context, "Update Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context + .read() + .add(LoadEducations()); + }); + } else { + errorAlert(context, "Updated Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context + .read() + .add(LoadEducations()); + }); + } + } + ////DELETED STATE + if (state is EducationDeletedState) { + if (state.success) { + successAlert(context, "Deletion Successfull", + "Educational Background has been deleted successfully", + () { + Navigator.of(context).pop(); + context + .read() + .add(LoadEducations()); + }); + } else { + errorAlert(context, "Deletion Failed", + "Error deleting Education Background", () { + Navigator.of(context).pop(); + context + .read() + .add(LoadEducations()); + }); + } + } }, builder: (context, state) { if (state is EducationalBackgroundLoadedState) { @@ -181,12 +261,67 @@ class EducationScreen extends StatelessWidget { ), ]), ), - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - )) + AppPopupMenu( + offset: const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + ////delete -= = = = = = = = =>> + if (value == 2) { + confirmAlert(context, () { + final progress = + ProgressHUD.of( + context); + progress!.showWithText( + "Loading..."); + context + .read() + .add(DeleteEducation( + educationalBackground: + state.educationalBackground[ + index], + profileId: + profileId, + token: token!)); + }, "Delete?", + "Confirm Delete?"); + } + if (value == 1) { + ////edit -= = = = = = = = =>> + final progress = + ProgressHUD.of(context); + progress!.showWithText( + "Loading..."); + context + .read() + .add(ShowEditEducationForm( + profileId: + profileId, + token: token!, + educationalBackground: + state.educationalBackground[ + index])); + } + }, + menuItems: [ + popMenuItem( + text: "Edit", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Delete", + value: 2, + icon: Icons.delete), + popMenuItem( + text: "Attachment", + value: 3, + icon: FontAwesome.attach) + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ) ], ), ), @@ -206,6 +341,18 @@ class EducationScreen extends StatelessWidget { return SomethingWentWrong( message: state.message, onpressed: () {}); } + if (state is AddEducationState) { + return AddEducationScreen( + token: token!, + profileId: profileId, + ); + } + if (state is EditEducationState) { + return EditEducationScreen( + token: token!, + profileId: profileId, + ); + } return Container(); }, ); @@ -219,4 +366,23 @@ class EducationScreen extends StatelessWidget { ), )); } + + PopupMenuItem popMenuItem({String? text, int? value, IconData? icon}) { + return PopupMenuItem( + value: value, + child: Row( + children: [ + Icon( + icon, + ), + const SizedBox( + width: 10, + ), + Text( + text!, + ), + ], + ), + ); + } } diff --git a/lib/screens/profile/components/eligibility/add_modal.dart b/lib/screens/profile/components/eligibility/add_modal.dart index 07068e9..453bdf3 100644 --- a/lib/screens/profile/components/eligibility/add_modal.dart +++ b/lib/screens/profile/components/eligibility/add_modal.dart @@ -64,383 +64,399 @@ class _AddEligibilityScreenState extends State { return SingleChildScrollView( child: SizedBox( height: screenHeight * .95, - child: ProgressHUD( - child: Center( - child: Padding( - padding: - const EdgeInsets.symmetric(vertical: 25, horizontal: 18), - child: FormBuilder( - key: formKey, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - ////ELIGIBILITIES DROPDOWN - FormBuilderDropdown( - onChanged: (Eligibility? eligibility) { - selectedEligibility = eligibility; - }, - autovalidateMode: - AutovalidateMode.onUserInteraction, - validator: (value) => - value == null ? 'required' : null, - items: state.eligibilities - .map>( - (Eligibility eligibility) { - return DropdownMenuItem( - value: eligibility, - child: Text(eligibility.title)); - }).toList(), - name: "eligibility", - decoration: normalTextFieldStyle( - "Eligibility", "Eligibility")), - const SizedBox( - height: 8, + child: Center( + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, horizontal: 18), + child: FormBuilder( + key: formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + ////ELIGIBILITIES DROPDOWN + FormBuilderDropdown( + onChanged: (Eligibility? eligibility) { + selectedEligibility = eligibility; + }, + autovalidateMode: + AutovalidateMode.onUserInteraction, + validator: (value) => + value == null ? 'required' : null, + items: state.eligibilities + .map>( + (Eligibility eligibility) { + return DropdownMenuItem( + value: eligibility, + child: Text(eligibility.title)); + }).toList(), + name: "eligibility", + decoration: normalTextFieldStyle( + "Eligibility", "Eligibility")), + const SizedBox( + height: 8, + ), + + SizedBox( + width: screenWidth, + child: Row( + children: [ + ////LICENSE NUMBER + Flexible( + flex: 1, + child: FormBuilderTextField( + onChanged: (value) { + license = value; + }, + name: 'license_number', + decoration: normalTextFieldStyle( + "license number", "license number"), + ), + ), + const SizedBox( + width: 8, + ), + ////RATING + Flexible( + flex: 1, + child: FormBuilderTextField( + keyboardType: const TextInputType + .numberWithOptions(), + onChanged: (value) { + rating = value; + }, + name: 'rating', + decoration: normalTextFieldStyle( + 'rating %', 'rating'), + ), + ), + ], ), - - SizedBox( - width: screenWidth, - child: Row( - children: [ - ////LICENSE NUMBER - Flexible( - flex: 1, - child: FormBuilderTextField( - onChanged: (value) { - license = value; - }, - name: 'license_number', - decoration: normalTextFieldStyle( - "license number", "license number"), - ), - ), - const SizedBox( - width: 8, - ), - ////RATING - Flexible( - flex: 1, - child: FormBuilderTextField( - keyboardType: - const TextInputType.numberWithOptions(), - onChanged: (value) { - rating = value; - }, - name: 'rating', - decoration: normalTextFieldStyle( - 'rating %', 'rating'), - ), - ), - ], - ), - ), - const SizedBox( - height: 8, - ), - SizedBox( - width: screenWidth, - child: Row( - children: [ - ////EXAM DATE - Flexible( - flex: 1, - child: DateTimePicker( - validator: FormBuilderValidators.required( - errorText: "This field is required"), - use24HourFormat: false, - icon: const Icon(Icons.date_range), - controller: examDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), - timeHintText: - "Date of Examination/Conferment", - decoration: - normalTextFieldStyle("Exam date", "") - .copyWith( - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - )), - initialValue: null, - )), - const SizedBox( - width: 8, - ), - ////VALIDITY DATE - Flexible( + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + ////EXAM DATE + Flexible( flex: 1, child: DateTimePicker( - validator: FormBuilderValidators.required( - errorText: "This field is required"), - controller: validityDateController, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + use24HourFormat: false, + icon: const Icon(Icons.date_range), + controller: examDateController, firstDate: DateTime(1970), lastDate: DateTime(2100), + timeHintText: + "Date of Examination/Conferment", decoration: normalTextFieldStyle( - "Validity date", "Validity date") + "Exam date", "") .copyWith( prefixIcon: const Icon( Icons.date_range, color: Colors.black87, )), initialValue: null, - ), - ), - ], - ), - ), - const SizedBox( - height: 8, - ), - Text( - "Placement of Examination/Conferment", - style: Theme.of(context) - .textTheme - .displaySmall! - .copyWith(fontSize: blockSizeVertical * 2), - ), - const SizedBox( - height: 8, - ), - ////OVERSEAS ADDRESS SWITCH - Column( - children: [ - FormBuilderSwitch( - validator: FormBuilderValidators.required( - errorText: 'This field is required'), - initialValue: overseas, - activeColor: second, - onChanged: (value) { - setState(() { - overseas = value; - }); - }, - decoration: normalTextFieldStyle("", ''), - name: 'overseas', - title: const Text("Overseas Address?"), - ), + )), const SizedBox( - height: 8, + width: 8, ), - ////COUNTRY DROPDOWN - SizedBox( - child: overseas == true - ? FormBuilderDropdown( - initialValue: null, - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - items: state.countries - .map>( - (Country country) { - return DropdownMenuItem( - value: country, - child: FittedBox( - child: Text(country.name!))); - }).toList(), - name: 'country', - decoration: normalTextFieldStyle( - "Country*", "Country"), - onChanged: (Country? value) { - selectedCountry = value; - }, - ) - : Column( - children: [ - ////REGION DROPDOWN - FormBuilderDropdown( - autovalidateMode: AutovalidateMode - .onUserInteraction, - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - //// region onchange - onChanged: (Region? region) async { - - if(selectedRegion != region){ - setState(() { + ////VALIDITY DATE + Flexible( + flex: 1, + child: DateTimePicker( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + controller: validityDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + decoration: normalTextFieldStyle( + "Validity date", "Validity date") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + initialValue: null, + ), + ), + ], + ), + ), + const SizedBox( + height: 8, + ), + Text( + "Placement of Examination/Conferment", + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith(fontSize: blockSizeVertical * 2), + ), + const SizedBox( + height: 8, + ), + ////OVERSEAS ADDRESS SWITCH + Column( + children: [ + FormBuilderSwitch( + validator: FormBuilderValidators.required( + errorText: 'This field is required'), + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value; + }); + }, + decoration: normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Overseas Address?"), + ), + const SizedBox( + height: 8, + ), + ////COUNTRY DROPDOWN + SizedBox( + child: overseas == true + ? FormBuilderDropdown( + initialValue: null, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + items: state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox( + child: + Text(country.name!))); + }).toList(), + name: 'country', + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) { + selectedCountry = value; + }, + ) + : Column( + children: [ + ////REGION DROPDOWN + FormBuilderDropdown( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + //// region onchange + onChanged: + (Region? region) async { + if (selectedRegion != + region) { + setState(() { provinceCall = true; }); - selectedRegion = region; + selectedRegion = region; getProvinces(); - } - }, - initialValue: selectedRegion, - decoration: normalTextFieldStyle( - "Region*", "Region"), - name: 'region', - items: state.regions - .map>( - (Region region) { - return DropdownMenuItem( - value: region, - child: Text( - region.description!)); - }).toList(), - ), - const SizedBox( - height: 8, - ), - ////PROVINCE DROPDOWN - SizedBox( - height: 70, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: provinceCall, - child: DropdownButtonFormField< - Province?>( - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - value: selectedProvince, - onChanged: - (Province? province) { - - if(selectedProvince != province){ - setState(() { - cityCall = true; - }); - selectedProvince = province; - getCities(); - } - }, - items: provinces == null - ? [] - : provinces!.map< - DropdownMenuItem< - Province>>( - (Province province) { - return DropdownMenuItem( - value: province, - child: FittedBox( - child: Text(province - .description!), - )); - }).toList(), - decoration: - normalTextFieldStyle( - "Province*", - "Province")), - ), - ), - - //// CityMunicipalities dropdown - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: cityCall, - child: DropdownButtonFormField< - CityMunicipality>( + } + }, + initialValue: selectedRegion, + decoration: + normalTextFieldStyle( + "Region*", "Region"), + name: 'region', + items: state.regions.map< + DropdownMenuItem< + Region>>( + (Region region) { + return DropdownMenuItem< + Region>( + value: region, + child: Text( + region.description!)); + }).toList(), + ), + const SizedBox( + height: 8, + ), + ////PROVINCE DROPDOWN + SizedBox( + height: 70, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, validator: (value) => value == null ? 'required' : null, isExpanded: true, + value: selectedProvince, onChanged: - (CityMunicipality? city) { - selectedMunicipality = city; + (Province? province) { + if (selectedProvince != + province) { + setState(() { + cityCall = true; + }); + selectedProvince = + province; + getCities(); + } }, + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province + province) { + return DropdownMenuItem( + value: + province, + child: + FittedBox( + child: Text( + province + .description!), + )); + }).toList(), decoration: normalTextFieldStyle( - "Municipality*", - "Municipality"), - value: selectedMunicipality, - items: citymuns == null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality c) { - return DropdownMenuItem( - value: c, - child: Text(c - .description!)); - }).toList(), - ), + "Province*", + "Province")), + ), + ), + + //// CityMunicipalities dropdown + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: + DropdownButtonFormField< + CityMunicipality>( + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + onChanged: + (CityMunicipality? + city) { + selectedMunicipality = + city; + }, + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality + c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), ), ), - - ], - )), - ], - ), - - const Expanded( - child: SizedBox(), - ), - - SizedBox( - width: screenWidth, - height: 60, - child: ElevatedButton( - style: mainBtnStyle( - primary, Colors.transparent, second), - onPressed: () { - //rating - double? rate = rating == null - ? null - : double.parse(rating!); - //lisence - String? licenseNumber = license; - CityMunicipality? cityMunicipality = - selectedMunicipality; - DateTime? examDate = examDateController - .text.isEmpty - ? null - : DateTime.parse(examDateController.text); - DateTime? validityDate = - validityDateController.text.isEmpty - ? null - : DateTime.parse( - validityDateController.text); - - ExamAddress examAddress = ExamAddress( - barangay: null, - id: null, - addressCategory: null, - examAddressClass: null, - country: selectedCountry ?? - Country( - id: 175, - name: 'Philippines', - code: 'PH'), - cityMunicipality: cityMunicipality); - EligibityCert eligibityCert = EligibityCert( - id: null, - rating: rate, - examDate: examDate, - attachments: null, - eligibility: selectedEligibility, - examAddress: examAddress, - validityDate: validityDate, - licenseNumber: licenseNumber, - overseas: overseas); - if (formKey.currentState!.saveAndValidate()) { - final progress = ProgressHUD.of(context); - progress!.showWithText("Loading..."); - context.read().add( - AddEligibility( - eligibityCert: eligibityCert, - profileId: - widget.profileId.toString(), - token: widget.token)); - } - // context.read().add(AddEligibility(eligibityCert: eligibityCert, profileId: profileId, token: token)) - }, - child: const Text(submit)), - ), - const SizedBox( - height: 20, - ), - ]), - ), + ), + ], + )), + ], + ), + + const Expanded( + child: SizedBox(), + ), + + SizedBox( + width: screenWidth, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + //rating + double? rate = rating == null + ? null + : double.parse(rating!); + //lisence + String? licenseNumber = license; + CityMunicipality? cityMunicipality = + selectedMunicipality; + DateTime? examDate = + examDateController.text.isEmpty + ? null + : DateTime.parse( + examDateController.text); + DateTime? validityDate = + validityDateController.text.isEmpty + ? null + : DateTime.parse( + validityDateController.text); + + ExamAddress examAddress = ExamAddress( + barangay: null, + id: null, + addressCategory: null, + examAddressClass: null, + country: selectedCountry ?? + Country( + id: 175, + name: 'Philippines', + code: 'PH'), + cityMunicipality: cityMunicipality); + EligibityCert eligibityCert = EligibityCert( + id: null, + rating: rate, + examDate: examDate, + attachments: null, + eligibility: selectedEligibility, + examAddress: examAddress, + validityDate: validityDate, + licenseNumber: licenseNumber, + overseas: overseas); + if (formKey.currentState! + .saveAndValidate()) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add( + AddEligibility( + eligibityCert: eligibityCert, + profileId: + widget.profileId.toString(), + token: widget.token)); + } + // context.read().add(AddEligibility(eligibityCert: eligibityCert, profileId: profileId, token: token)) + }, + child: const Text(submit)), + ), + const SizedBox( + height: 20, + ), + ]), ), ), ), diff --git a/lib/screens/profile/components/voluntary_works_screen.dart b/lib/screens/profile/components/voluntary_works_screen.dart index a26ff67..fbe0da8 100644 --- a/lib/screens/profile/components/voluntary_works_screen.dart +++ b/lib/screens/profile/components/voluntary_works_screen.dart @@ -5,6 +5,7 @@ import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:intl/intl.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; +import 'package:unit2/screens/profile/components/work_history/voluntary_works/add_modal.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; @@ -18,12 +19,16 @@ class VolunataryWorkScreen extends StatelessWidget { @override Widget build(BuildContext context) { + String? token; + int? profileId; DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); return Scaffold( appBar: AppBar( title: const Text(voluntaryScreenTitle), backgroundColor: primary, - actions: [AddLeading(onPressed: () {})], + actions: [AddLeading(onPressed: () { + context.read().add(ShowAddVoluntaryWorks()); + })], ), body: ProgressHUD( padding: const EdgeInsets.all(24), @@ -32,6 +37,9 @@ class VolunataryWorkScreen extends StatelessWidget { child: BlocBuilder( builder: (context, state) { if (state is UserLoggedIn) { + token = state.userData!.user!.login!.token; + profileId = + state.userData!.user!.login!.user!.profileId; return BlocBuilder( builder: (context, state) { if (state is ProfileLoaded) { @@ -43,7 +51,7 @@ class VolunataryWorkScreen extends StatelessWidget { progress!.showWithText("Please wait..."); } if (state is VoluntaryWorkLoadedState || - state is VoluntaryWorkErrorState) { + state is VoluntaryWorkErrorState|| state is AddVoluntaryWorkState) { final progress = ProgressHUD.of(context); progress!.dismiss(); } @@ -138,6 +146,8 @@ class VolunataryWorkScreen extends StatelessWidget { message: "You don't have any Voluntary Works added. Please click + to add."); } + }if(state is AddVoluntaryWorkState){ + return AddVoluntaryWorkScreen(profileId: profileId!,token: token!,); } return Container(); }, diff --git a/lib/screens/profile/components/work_history/add_modal.dart b/lib/screens/profile/components/work_history/add_modal.dart index 3ba5b84..6db6239 100644 --- a/lib/screens/profile/components/work_history/add_modal.dart +++ b/lib/screens/profile/components/work_history/add_modal.dart @@ -22,11 +22,13 @@ import 'package:unit2/utils/text_container.dart'; import 'package:unit2/utils/validators.dart'; import '../../../../model/utils/position.dart'; +import '../../shared/add_for_empty_search.dart'; class AddWorkHistoryScreen extends StatefulWidget { final int profileId; final String token; - const AddWorkHistoryScreen({super.key, required this.profileId, required this.token}); + const AddWorkHistoryScreen( + {super.key, required this.profileId, required this.token}); @override State createState() => _AddWorkHistoryScreenState(); @@ -54,8 +56,6 @@ class _AddWorkHistoryScreenState extends State { final positionFocusNode = FocusNode(); final appointmentStatusNode = FocusNode(); final agencyCategoryFocusNode = FocusNode(); - int? profileId; - String? token; @override void dispose() { addPositionController.dispose(); @@ -67,705 +67,526 @@ class _AddWorkHistoryScreenState extends State { @override Widget build(BuildContext context) { - - return BlocConsumer( - listener: (context, state) { - if (state is AddWorkHistoryState) { - final progress = ProgressHUD.of(context); - progress!.dismiss(); - } - }, builder: (context, state) { - if (state is AddWorkHistoryState) { - return SingleChildScrollView( - child: SizedBox( - height: blockSizeVertical * 90, - child: FormBuilder( - key: _formKey, - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 25, horizontal: 18), - child: Column( - children: [ - ////POSITIONS - StatefulBuilder(builder: (context, setState) { - return SearchField( - itemHeight: 50, - suggestionsDecoration: box1(), - suggestions: state.agencyPositions - .map((Position position) => - SearchFieldListItem(position.title!, - item: position, - child: Padding( - padding: const EdgeInsets - .symmetric( - horizontal: 10), - child: ListTile( - title: - Text(position.title!), - )))) + return BlocConsumer( + listener: (context, state) { + if (state is AddWorkHistoryState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + }, builder: (context, state) { + if (state is AddWorkHistoryState) { + return SingleChildScrollView( + child: SizedBox( + height: blockSizeVertical * 90, + child: FormBuilder( + key: _formKey, + child: Padding( + padding: + const EdgeInsets.symmetric(vertical: 25, horizontal: 18), + child: Column( + children: [ + ////POSITIONS + StatefulBuilder(builder: (context, setState) { + return SearchField( + itemHeight: 50, + suggestionsDecoration: box1(), + suggestions: state.agencyPositions + .map((Position position) => SearchFieldListItem( + position.title!, + item: position, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10), + child: ListTile( + title: Text(position.title!), + )))) + .toList(), + focusNode: positionFocusNode, + searchInputDecoration: + normalTextFieldStyle("Position *", "").copyWith( + suffixIcon: const Icon(Icons.arrow_drop_down)), + onSuggestionTap: (position) { + setState(() { + selectedPosition = position.item; + positionFocusNode.unfocus(); + }); + }, + ////EMPTY WIDGET + emptyWidget: EmptyWidget( + title: "Add Position", + controller: addPositionController, + onpressed: () { + setState(() { + Position newAgencyPosition = Position( + id: null, + title: addPositionController.text + .toUpperCase()); + + state.agencyPositions + .insert(0, newAgencyPosition); + selectedPosition = newAgencyPosition; + addPositionController.text = ""; + Navigator.pop(context); + }); + }), + validator: (position) { + if (position!.isEmpty) { + return "This field is required"; + } + return null; + }, + ); + }), + const SizedBox( + height: 12, + ), + ////APPOINTMENT STATUS' + SearchField( + suggestions: state.appointmentStatus + .map((AppoinemtStatus status) => + SearchFieldListItem(status.label, item: status)) + .toList(), + focusNode: appointmentStatusNode, + validator: (value) { + if (value!.isEmpty) { + return "This field is required"; + } + return null; + }, + onSuggestionTap: (status) { + selectedStatus = status.item; + appointmentStatusNode.unfocus(); + }, + searchInputDecoration: normalTextFieldStyle( + "Appointment Status", "") + .copyWith( + suffixIcon: const Icon(Icons.arrow_drop_down)), + ), + + const SizedBox( + height: 12, + ), + + ////AGENCY + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + SearchField( + itemHeight: 70, + focusNode: agencyFocusNode, + suggestions: state.agencies + .map((Agency agency) => SearchFieldListItem( + agency.name!, + item: agency, + child: ListTile( + title: Text( + agency.name!, + overflow: TextOverflow.ellipsis, + ), + subtitle: + Text(agency.privateEntity == true + ? "Private" + : agency.privateEntity == false + ? "Government" + : ""), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle("Agency *", "").copyWith( + suffixIcon: + const Icon(Icons.arrow_drop_down)), + onSuggestionTap: (agency) { + setState(() { + selectedAgency = agency.item; + + if (selectedAgency!.privateEntity == null) { + showIsPrivateRadio = true; + } else { + showIsPrivateRadio = false; + } + if (selectedAgency!.privateEntity == true) { + showSalaryGradeAndSalaryStep = false; + } + if (selectedAgency!.privateEntity == false) { + showSalaryGradeAndSalaryStep = true; + } + agencyFocusNode.unfocus(); + }); + }, + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + emptyWidget: EmptyWidget( + controller: addAgencyController, + onpressed: () { + setState(() { + Agency newAgency = Agency( + id: null, + name: addAgencyController.text + .toUpperCase(), + category: null, + privateEntity: null); + + state.agencies.insert(0, newAgency); + selectedAgency = newAgency; + addAgencyController.text = ""; + showAgency = true; + + showIsPrivateRadio = true; + + Navigator.pop(context); + }); + }, + title: "Add Agency")), + + SizedBox( + height: showAgency ? 12 : 0, + ), + ////SHOW CATEGORY AGENCY + SizedBox( + child: showAgency + ? SearchField( + focusNode: agencyCategoryFocusNode, + itemHeight: 70, + suggestions: state.agencyCategory + .map((Category category) => + SearchFieldListItem(category.name!, + item: category, + child: ListTile( + title: Text(category.name!), + subtitle: Text(category + .industryClass!.name!), + ))) .toList(), - focusNode: positionFocusNode, + emptyWidget: Container( + height: 100, + decoration: box1(), + child: const Center( + child: Text("No result found ...")), + ), + onSuggestionTap: (agencyCategory) { + setState(() { + selectedAgencyCategory = + agencyCategory.item; + agencyCategoryFocusNode.unfocus(); + selectedAgency = Agency( + id: null, + name: selectedAgency!.name, + category: selectedAgencyCategory, + privateEntity: null); + }); + }, searchInputDecoration: - normalTextFieldStyle("Position *", "") + normalTextFieldStyle("Category *", "") .copyWith( suffixIcon: const Icon( Icons.arrow_drop_down)), - onSuggestionTap: (position) { - setState(() { - selectedPosition = position.item; - positionFocusNode.unfocus(); - }); - }, - ////EMPTY WIDGET - emptyWidget: Container( - decoration: box1(), - height: 100, - child: Column( - mainAxisAlignment: - MainAxisAlignment.center, - crossAxisAlignment: - CrossAxisAlignment.center, - children: [ - const SizedBox( - height: 20, - ), - const Text("No result found..."), - const SizedBox( - height: 10, - ), - TextButton( - onPressed: () { - ////ADD POSITION DIALOG - showDialog( - context: context, - builder: (BuildContext - context) { - return AlertDialog( - title: const Text( - "Add Position?"), - content: SizedBox( - height: 130, - child: Column( - children: [ - TextFormField( - controller: - addPositionController, - decoration: - normalTextFieldStyle( - "", - ""), - ), - const SizedBox( - height: 12, - ), - SizedBox( - width: double - .infinity, - height: 50, - child: ElevatedButton( - style: mainBtnStyle(primary, Colors.transparent, second), - onPressed: () { - setState( - () { - Position - newAgencyPosition = - Position(id: null, title: addPositionController.text.toUpperCase()); - - state.agencyPositions.insert(0, - newAgencyPosition); - selectedPosition = - newAgencyPosition; - addPositionController.text = - ""; - Navigator.pop(context); - }); - }, - child: const Text("Add"))), - ], - ), - ), - ); - }); - }, - child: - const Text("Add position")) - ]), - ), - validator: (position) { - if (position!.isEmpty) { + validator: (value) { + if (value!.isEmpty) { return "This field is required"; } return null; }, - ); - }), - const SizedBox( - height: 12, - ), - ////APPOINTMENT STATUS' - SearchField( - suggestions: state.appointmentStatus - .map((AppoinemtStatus status) => - SearchFieldListItem(status.label, - item: status)) - .toList(), - focusNode: appointmentStatusNode, - validator: (value) { - if (value!.isEmpty) { - return "This field is required"; - } - return null; - }, - onSuggestionTap: (status) { - selectedStatus = status.item; - appointmentStatusNode.unfocus(); - }, - searchInputDecoration: normalTextFieldStyle( - "Appointment Status", "") - .copyWith( - suffixIcon: const Icon( - Icons.arrow_drop_down)), - ), + ) + : const SizedBox(), + ), - const SizedBox( - height: 12, - ), - - ////AGENCY - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - SearchField( - itemHeight: 70, - focusNode: agencyFocusNode, - suggestions: state.agencies - .map((Agency agency) => - SearchFieldListItem( - agency.name!, - item: agency, - child: ListTile( - title: Text( - agency.name!, - overflow: TextOverflow - .ellipsis, - ), - subtitle: Text( - agency.privateEntity == - true - ? "Private" - : agency.privateEntity == false? "Government":""), - ))) - .toList(), - searchInputDecoration: - normalTextFieldStyle("Agency *", "") - .copyWith( - suffixIcon: const Icon( - Icons.arrow_drop_down)), - onSuggestionTap: (agency) { - setState(() { - selectedAgency = agency.item; - - if (selectedAgency!.privateEntity == - null) { - showIsPrivateRadio = true; - } else { - showIsPrivateRadio = false; - } - if (selectedAgency!.privateEntity == - true) { - showSalaryGradeAndSalaryStep = - false; - } - if (selectedAgency!.privateEntity == - false) { - showSalaryGradeAndSalaryStep = - true; - } - agencyFocusNode.unfocus(); - }); - }, - validator: (agency) { - if (agency!.isEmpty) { - return "This field is required"; - } - return null; - }, - emptyWidget: Container( - decoration: box1(), - height: 100, - child: Column( - mainAxisAlignment: - MainAxisAlignment.center, - crossAxisAlignment: - CrossAxisAlignment.center, - children: [ - const SizedBox( - height: 20, - ), - const Text("No result found"), - const SizedBox( - height: 10, - ), - TextButton( - onPressed: () { - showDialog( - context: context, - builder: (BuildContext - context) { - return AlertDialog( - title: const Text( - "Add Position"), - content: SizedBox( - height: 130, - child: Column( - children: [ - TextFormField( - controller: - addAgencyController, - decoration: - normalTextFieldStyle( - "", - ""), - ), - const SizedBox( - height: - 12, - ), - SizedBox( - width: double - .infinity, - height: - 50, - child: ElevatedButton( - style: mainBtnStyle(primary, Colors.transparent, second), - onPressed: () { - setState(() { - Agency newAgency = Agency(id: null, name: addAgencyController.text.toUpperCase(), category: null, privateEntity: null); - - state.agencies.insert(0, newAgency); - selectedAgency = newAgency; - addAgencyController.text = ""; - showAgency = true; - - showIsPrivateRadio = true; - - Navigator.pop(context); - }); - }, - child: const Text("Add"))), - ], - ), - ), - ); - }); - }, - child: const Text( - "Add Agency")) - ]), - ), - ), - - SizedBox( - height: showAgency ? 12 : 0, - ), - ////SHOW CATEGORY AGENCY - SizedBox( - child: showAgency - ? SearchField( - focusNode: - agencyCategoryFocusNode, - itemHeight: 70, - suggestions: state - .agencyCategory - .map((Category category) => - SearchFieldListItem( - category.name!, - item: category, - child: ListTile( - title: Text( - category - .name!), - subtitle: Text( - category - .industryClass! - .name!), - ))) - .toList(), - emptyWidget: Container( - height: 100, - decoration: box1(), - child: const Center( - child: Text( - "No result found ...")), - ), - onSuggestionTap: - (agencyCategory) { - setState(() { - selectedAgencyCategory = - agencyCategory.item; - agencyCategoryFocusNode - .unfocus(); - selectedAgency = Agency( - id: null, - name: selectedAgency! - .name, - category: - selectedAgencyCategory, - privateEntity: null); - }); - }, - searchInputDecoration: - normalTextFieldStyle( - "Category *", "") - .copyWith( - suffixIcon: - const Icon(Icons - .arrow_drop_down)), - validator: (value) { - if (value!.isEmpty) { - return "This field is required"; - } - return null; - }, - ) - : const SizedBox(), - ), - - ////PRVIATE SECTOR - SizedBox( - child: showIsPrivateRadio - ? FormBuilderRadioGroup( - decoration: InputDecoration( - border: InputBorder.none, - label: Row( - children: [ - Text( - "Is this private sector? ", - style: Theme.of( - context) - .textTheme - .headlineSmall! - .copyWith( - fontSize: 24), - ), - const Icon(FontAwesome - .help_circled) - ], - ), - ), - - ////onvhange private sector - onChanged: (value) { - setState(() { - if (value.toString() == - "YES") { - isPrivate = true; - showSalaryGradeAndSalaryStep = - false; - } else { - isPrivate = false; - showSalaryGradeAndSalaryStep = - true; - } - selectedAgency = Agency( - id: null, - name: selectedAgency! - .name, - category: - selectedAgencyCategory, - privateEntity: value == "YES"?true:false); - agencyFocusNode.unfocus(); - agencyCategoryFocusNode - .unfocus(); - }); - }, - - name: 'isPrivate', - validator: - FormBuilderValidators - .required(), - options: ["YES", "NO"] - .map((lang) => - FormBuilderFieldOption( - value: lang)) - .toList(growable: false), - ) - : const SizedBox()), - ////SALARY GRADE AND SALARY GRADE STEP - SizedBox( - child: showSalaryGradeAndSalaryStep - ? Column( - children: [ - Row( - children: [ - Flexible( - flex: 1, - child: - FormBuilderTextField( - name: - 'salary_grade', - keyboardType: - TextInputType - .number, - decoration: - normalTextFieldStyle( - "Salary Grade (SG)", - "0"), - onChanged: (value) { - salaryGrade = - value; - }, - validator: - FormBuilderValidators - .compose([ - FormBuilderValidators - .integer( - radix: 10, - errorText: - "Please enter a number"), - FormBuilderValidators - .numeric( - errorText: - "Please enter a number") - ]), - autovalidateMode: - AutovalidateMode - .onUserInteraction, - ), - ), - const SizedBox( - width: 12, - ), - Flexible( - flex: 1, - child: - FormBuilderTextField( - name: 'salary_step', - keyboardType: - TextInputType - .number, - decoration: - normalTextFieldStyle( - "SG Step (SG)", - "0"), - validator: - FormBuilderValidators - .compose([ - FormBuilderValidators - .integer( - radix: 10, - errorText: - "Please enter a number"), - FormBuilderValidators - .numeric( - errorText: - "Please enter a number") - ]), - autovalidateMode: - AutovalidateMode - .onUserInteraction, - onChanged: (value) { - setState(() { - salaryGradeStep = - value; - }); - }, - ), - ) - ], - ) - ], - ) - : null), - ], - ); - }), - const SizedBox( - height: 12, - ), - ////MONTHLY SALARY - FormBuilderTextField( - onChanged: (value) { - setState(() { - salary = value; - }); - }, - validator: numericRequired, - name: "salary", - decoration: normalTextFieldStyle( - "Monthly Salary *", "") - .copyWith(prefix: const Text("₱ ")), - ), - - const SizedBox( - height: 12, - ), - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - ////CURRENTLY EMPLOYED - FormBuilderSwitch( - initialValue: currentlyEmployed, - activeColor: second, - onChanged: (value) { - setState(() { - if (value == true) { - currentlyEmployed = true; - toDateController.text = "PRESENT"; - } else { - currentlyEmployed = false; - toDateController.text = ""; - } - }); - }, - decoration: - normalTextFieldStyle("", ''), - name: 'overseas', - title: - const Text("Currently Employed?"), - ), - const SizedBox( - height: 12, - ), - SizedBox( - width: screenWidth, - child: Row( + ////PRVIATE SECTOR + SizedBox( + child: showIsPrivateRadio + ? FormBuilderRadioGroup( + decoration: InputDecoration( + border: InputBorder.none, + label: Row( children: [ - //// FROM DATE - Flexible( - flex: 1, - child: DateTimePicker( - validator: FormBuilderValidators.required(errorText: "This field is required"), - use24HourFormat: false, - icon: const Icon( - Icons.date_range), - controller: - fromDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), - timeHintText: - "Date of Examination/Conferment", - decoration: - normalTextFieldStyle( - "From *", - "From *") - .copyWith( - prefixIcon: - const Icon( - Icons.date_range, - color: Colors.black87, - )), - initialValue: null, - )), - const SizedBox( - width: 12, - ), - //// TO DATE - Flexible( - flex: 1, - child: currentlyEmployed - ? TextFormField( - enabled: false, - initialValue: "PRESENT", - style: const TextStyle( - color: - Colors.black45), - decoration: - normalTextFieldStyle( - "", "") - .copyWith(), - ) - : DateTimePicker( - validator: FormBuilderValidators.required(errorText: "This field is required"), - controller: - toDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), - decoration: normalTextFieldStyle( - "To *", "To *") - .copyWith( - prefixIcon: - const Icon( - Icons - .date_range, - color: Colors - .black87, - ), - prefixText: - currentlyEmployed - ? "PRESENT" - : ""), - initialValue: null, - ), + Text( + "Is this private sector? ", + style: Theme.of(context) + .textTheme + .headlineSmall! + .copyWith(fontSize: 24), ), + const Icon(FontAwesome.help_circled) ], ), ), - ], - ); - }), - const Expanded(child: SizedBox()), - ////SUBMIT BUTTON - SizedBox( - width: double.infinity, - height: 60, - child: ElevatedButton( - style: mainBtnStyle( - primary, Colors.transparent, second), - onPressed: () { - - if (_formKey.currentState!.validate()) { - final progress = - ProgressHUD.of(context); - progress!.showWithText( - "Loading..."); - WorkHistory workHistory = WorkHistory( - position: selectedPosition, + + ////onvhange private sector + onChanged: (value) { + setState(() { + if (value.toString() == "YES") { + isPrivate = true; + showSalaryGradeAndSalaryStep = + false; + } else { + isPrivate = false; + showSalaryGradeAndSalaryStep = true; + } + selectedAgency = Agency( id: null, - agency: selectedAgency, - fromDate: fromDateController - .text.isEmpty - ? null - : DateTime.parse( - fromDateController.text), - toDate: toDateController.text.isEmpty || - toDateController.text - .toUpperCase() == - "PRESENT" - ? null - : DateTime.parse( - toDateController.text), - salaryGrade: salaryGrade == null - ? null - : int.parse(salaryGrade!), - sgStep: salaryGradeStep == null - ? null - : int.parse(salaryGradeStep!), - monthlySalary: - double.parse(salary!), - appointmentStatus: - selectedStatus!.value); - context.read().add( - AddWorkHostory( - workHistory: workHistory, - profileId: profileId!, - token: token!, - isPrivate: isPrivate!)); - } + name: selectedAgency!.name, + category: selectedAgencyCategory, + privateEntity: value == "YES" + ? true + : false); + agencyFocusNode.unfocus(); + agencyCategoryFocusNode.unfocus(); + }); }, - child: const Text(submit)), - ), + + name: 'isPrivate', + validator: + FormBuilderValidators.required(), + options: ["YES", "NO"] + .map((lang) => FormBuilderFieldOption( + value: lang)) + .toList(growable: false), + ) + : const SizedBox()), + ////SALARY GRADE AND SALARY GRADE STEP + SizedBox( + child: showSalaryGradeAndSalaryStep + ? Column( + children: [ + Row( + children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + name: 'salary_grade', + keyboardType: + TextInputType.number, + decoration: + normalTextFieldStyle( + "Salary Grade (SG)", + "0"), + onChanged: (value) { + salaryGrade = value; + }, + validator: FormBuilderValidators + .compose([ + FormBuilderValidators.integer( + radix: 10, + errorText: + "Please enter a number"), + FormBuilderValidators.numeric( + errorText: + "Please enter a number") + ]), + autovalidateMode: + AutovalidateMode + .onUserInteraction, + ), + ), + const SizedBox( + width: 12, + ), + Flexible( + flex: 1, + child: FormBuilderTextField( + name: 'salary_step', + keyboardType: + TextInputType.number, + decoration: + normalTextFieldStyle( + "SG Step (SG)", "0"), + validator: FormBuilderValidators + .compose([ + FormBuilderValidators.integer( + radix: 10, + errorText: + "Please enter a number"), + FormBuilderValidators.numeric( + errorText: + "Please enter a number") + ]), + autovalidateMode: + AutovalidateMode + .onUserInteraction, + onChanged: (value) { + setState(() { + salaryGradeStep = value; + }); + }, + ), + ) + ], + ) + ], + ) + : null), + ], + ); + }), + const SizedBox( + height: 12, + ), + ////MONTHLY SALARY + FormBuilderTextField( + onChanged: (value) { + setState(() { + salary = value; + }); + }, + validator: numericRequired, + name: "salary", + decoration: normalTextFieldStyle("Monthly Salary *", "") + .copyWith(prefix: const Text("₱ ")), + ), + + const SizedBox( + height: 12, + ), + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + ////CURRENTLY EMPLOYED + FormBuilderSwitch( + initialValue: currentlyEmployed, + activeColor: second, + onChanged: (value) { + setState(() { + if (value == true) { + currentlyEmployed = true; + toDateController.text = "PRESENT"; + } else { + currentlyEmployed = false; + toDateController.text = ""; + } + }); + }, + decoration: normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Currently Employed?"), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + //// FROM DATE + Flexible( + flex: 1, + child: DateTimePicker( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + use24HourFormat: false, + icon: const Icon(Icons.date_range), + controller: fromDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + timeHintText: + "Date of Examination/Conferment", + decoration: normalTextFieldStyle( + "From *", "From *") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + initialValue: null, + )), const SizedBox( - height: 20, + width: 12, + ), + //// TO DATE + Flexible( + flex: 1, + child: currentlyEmployed + ? TextFormField( + enabled: false, + initialValue: "PRESENT", + style: const TextStyle( + color: Colors.black45), + decoration: + normalTextFieldStyle("", "") + .copyWith(), + ) + : DateTimePicker( + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + controller: toDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + decoration: normalTextFieldStyle( + "To *", "To *") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + ), + prefixText: currentlyEmployed + ? "PRESENT" + : ""), + initialValue: null, + ), ), ], ), ), - ), - ), - ); - } - return Container(); - }); + ], + ); + }), + const Expanded(child: SizedBox()), + ////SUBMIT BUTTON + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: + mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + if (_formKey.currentState!.validate()) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + WorkHistory workHistory = WorkHistory( + position: selectedPosition, + id: null, + agency: selectedAgency, + fromDate: fromDateController.text.isEmpty + ? null + : DateTime.parse(fromDateController.text), + toDate: toDateController.text.isEmpty || + toDateController.text.toUpperCase() == + "PRESENT" + ? null + : DateTime.parse(toDateController.text), + salaryGrade: salaryGrade == null + ? null + : int.parse(salaryGrade!), + sgStep: salaryGradeStep == null + ? null + : int.parse(salaryGradeStep!), + monthlySalary: double.parse(salary!), + appointmentStatus: selectedStatus!.value); + context.read().add( + AddWorkHostory( + workHistory: workHistory, + profileId: widget.profileId, + token: widget.token, + isPrivate: isPrivate!)); + } + }, + child: const Text(submit)), + ), + const SizedBox( + height: 20, + ), + ], + ), + ), + ), + ), + ); + } + return Container(); + }); } } diff --git a/lib/screens/profile/components/work_history/edit_modal.dart b/lib/screens/profile/components/work_history/edit_modal.dart index bff0072..61303b8 100644 --- a/lib/screens/profile/components/work_history/edit_modal.dart +++ b/lib/screens/profile/components/work_history/edit_modal.dart @@ -6,6 +6,7 @@ import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:fluttericon/font_awesome_icons.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:searchfield/searchfield.dart'; +import 'package:unit2/screens/profile/shared/add_for_empty_search.dart'; import '../../../../bloc/profile/profile_bloc.dart'; import '../../../../bloc/profile/workHistory/workHistory_bloc.dart'; import '../../../../bloc/user/user_bloc.dart'; @@ -129,84 +130,22 @@ class _EditWorkHistoryScreenState extends State { positionFocusNode.unfocus(); }); }, - emptyWidget: Container( - decoration: box1(), - height: 100, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const SizedBox( - height: 20, - ), - const Text("No result found..."), - const SizedBox( - height: 10, - ), - TextButton( - onPressed: () { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: const Text( - "Add Position?"), - content: SizedBox( - height: 130, - child: Column( - children: [ - TextFormField( - controller: - addPositionController, - decoration: - normalTextFieldStyle( - "", ""), - ), - const SizedBox( - height: 12, - ), - SizedBox( - width: - double.infinity, - height: 50, - child: - ElevatedButton( - style: mainBtnStyle( - primary, - Colors - .transparent, - second), - onPressed: - () { - setState( - () { - Position - newAgencyPosition = - Position( - id: null, - title: addPositionController.text.toUpperCase()); - state.agencyPositions.insert( - 0, - newAgencyPosition); - selectedPosition = - newAgencyPosition; - addPositionController.text = - ""; - Navigator.pop( - context); - }); - }, - child: const Text( - "Add"))), - ], - ), - ), - ); - }); - }, - child: const Text("Add position")) - ]), - ), + emptyWidget: EmptyWidget( + controller: addPositionController, + onpressed: () { + setState(() { + Position newAgencyPosition = Position( + id: null, + title: addPositionController.text + .toUpperCase()); + state.agencyPositions + .insert(0, newAgencyPosition); + selectedPosition = newAgencyPosition; + addPositionController.text = ""; + Navigator.pop(context); + }); + }, + title: "Add Position"), validator: (position) { if (position!.isEmpty) { return "This field is required"; @@ -253,139 +192,77 @@ class _EditWorkHistoryScreenState extends State { return Column( children: [ SearchField( - controller: oldAgencyController, - itemHeight: 70, - focusNode: agencyFocusNode, - suggestions: state.agencies - .map((Agency agency) => - SearchFieldListItem(agency.name!, - item: agency, - child: ListTile( - title: Text( - agency.name!, - overflow: TextOverflow.ellipsis, - ), - subtitle: Text( - agency.privateEntity == true - ? "Private" - : "Government"), - ))) - .toList(), - searchInputDecoration: normalTextFieldStyle( - "Agency *", "") - .copyWith( - suffixIcon: - const Icon(Icons.arrow_drop_down)), - onSuggestionTap: (agency) { - setState(() { - selectedAgency = agency.item; - if (selectedAgency!.privateEntity == null) { - showIsPrivateRadio = true; - } else { - showIsPrivateRadio = false; + controller: oldAgencyController, + itemHeight: 70, + focusNode: agencyFocusNode, + suggestions: state.agencies + .map((Agency agency) => + SearchFieldListItem(agency.name!, + item: agency, + child: ListTile( + title: Text( + agency.name!, + overflow: + TextOverflow.ellipsis, + ), + subtitle: Text( + agency.privateEntity == true + ? "Private" + : "Government"), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle("Agency *", "") + .copyWith( + suffixIcon: const Icon( + Icons.arrow_drop_down)), + onSuggestionTap: (agency) { + setState(() { + selectedAgency = agency.item; + if (selectedAgency!.privateEntity == + null) { + showIsPrivateRadio = true; + } else { + showIsPrivateRadio = false; + } + if (selectedAgency!.privateEntity == + true) { + showSalaryGradeAndSalaryStep = false; + } + if (selectedAgency!.privateEntity == + false) { + showSalaryGradeAndSalaryStep = true; + } + agencyFocusNode.unfocus(); + }); + }, + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; } - if (selectedAgency!.privateEntity == true) { - showSalaryGradeAndSalaryStep = false; - } - if (selectedAgency!.privateEntity == - false) { - showSalaryGradeAndSalaryStep = true; - } - agencyFocusNode.unfocus(); - }); - }, - validator: (agency) { - if (agency!.isEmpty) { - return "This field is required"; - } - return null; - }, - emptyWidget: Container( - decoration: box1(), - height: 100, - child: Column( - mainAxisAlignment: - MainAxisAlignment.center, - crossAxisAlignment: - CrossAxisAlignment.center, - children: [ - const SizedBox( - height: 20, - ), - const Text("No result found"), - const SizedBox( - height: 10, - ), - TextButton( - onPressed: () { - showDialog( - context: context, - builder: - (BuildContext context) { - return AlertDialog( - title: const Text( - "Add Position"), - content: SizedBox( - height: 130, - child: Column( - children: [ - TextFormField( - controller: - addAgencyController, - decoration: - normalTextFieldStyle( - "", ""), - ), - const SizedBox( - height: 12, - ), - SizedBox( - width: double - .infinity, - height: 50, - child: - ElevatedButton( - style: mainBtnStyle( - primary, - Colors - .transparent, - second), - onPressed: - () { - setState( - () { - Agency newAgency = Agency( - id: null, - name: addAgencyController.text.toUpperCase(), - category: null, - privateEntity: null); - state.agencies.insert(0, - newAgency); - selectedAgency = - newAgency; - addAgencyController.text = - ""; - showAgencyCategory = - true; + return null; + }, + emptyWidget: EmptyWidget( + controller: addAgencyController, + onpressed: () { + setState(() { + Agency newAgency = Agency( + id: null, + name: addAgencyController.text + .toUpperCase(), + category: null, + privateEntity: null); + state.agencies.insert(0, newAgency); + selectedAgency = newAgency; + addAgencyController.text = ""; + showAgencyCategory = true; - showIsPrivateRadio = - true; + showIsPrivateRadio = true; - Navigator.pop(context); - }); - }, - child: const Text( - "Add"))), - ], - ), - ), - ); - }); - }, - child: const Text("Add Agency")) - ]), - ), - ), + Navigator.pop(context); + }); + }, + title: "Add Agency")), SizedBox( height: showAgencyCategory ? 12 : 0, diff --git a/lib/screens/profile/components/work_history/voluntary_works/add_modal.dart b/lib/screens/profile/components/work_history/voluntary_works/add_modal.dart new file mode 100644 index 0000000..e7cf6cb --- /dev/null +++ b/lib/screens/profile/components/work_history/voluntary_works/add_modal.dart @@ -0,0 +1,597 @@ +import 'package:date_time_picker/date_time_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; +import 'package:searchfield/searchfield.dart'; +import 'package:unit2/bloc/profile/voluntary_works/voluntary_work_bloc.dart'; +import 'package:unit2/utils/global.dart'; + +import '../../../../../model/location/barangay.dart'; +import '../../../../../model/location/city.dart'; +import '../../../../../model/location/country.dart'; +import '../../../../../model/location/provinces.dart'; +import '../../../../../model/location/region.dart'; +import '../../../../../model/utils/agency.dart'; +import '../../../../../model/utils/category.dart'; +import '../../../../../model/utils/position.dart'; +import '../../../../../theme-data.dart/box_shadow.dart'; +import '../../../../../theme-data.dart/colors.dart'; +import '../../../../../theme-data.dart/form-style.dart'; +import '../../../../../utils/location_utilities.dart'; +import '../../../shared/add_for_empty_search.dart'; + +class AddVoluntaryWorkScreen extends StatefulWidget { + final int profileId; + final String token; + const AddVoluntaryWorkScreen( + {super.key, required this.profileId, required this.token}); + + @override + State createState() => _AddVoluntaryWorkScreenState(); +} + +class _AddVoluntaryWorkScreenState extends State { + final formKey = GlobalKey(); + ////controllers + final addPositionController = TextEditingController(); + final addAgencyController = TextEditingController(); + final toDateController = TextEditingController(); + final fromDateController = TextEditingController(); + ////focus nodes + final positionFocusNode = FocusNode(); + final agencyFocusNode = FocusNode(); + final agencyCategoryFocusNode = FocusNode(); + ////booleans + bool showAgency = false; + bool showIsPrivateRadio = false; + bool currentlyInvolved = false; + bool overseas = false; + bool provinceCall = false; + bool cityCall = false; + ////Lists + List? provinces; + List? citymuns; + ////Selected + Region? selectedRegion; + Province? selectedProvince; + CityMunicipality? selectedMunicipality; + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is AddVoluntaryWorkState) { + return SingleChildScrollView( + child: SizedBox( + height: screenHeight * .90, + child: Center( + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 25, horizontal: 18), + child: FormBuilder( + child: Column( + children: [ + ////POSITIONS + StatefulBuilder(builder: (context, setState) { + return SearchField( + itemHeight: 50, + suggestionsDecoration: box1(), + suggestions: state.positions + .map((Position position) => SearchFieldListItem( + position.title!, + item: position, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10), + child: ListTile( + title: Text(position.title!), + )))) + .toList(), + focusNode: positionFocusNode, + searchInputDecoration: + normalTextFieldStyle("Position *", "").copyWith( + suffixIcon: + const Icon(Icons.arrow_drop_down)), + onSuggestionTap: (position) { + setState(() { + positionFocusNode.unfocus(); + }); + }, + ////EMPTY WIDGET + emptyWidget: EmptyWidget( + title: "Add Position", + controller: addPositionController, + onpressed: () { + setState(() { + Navigator.pop(context); + }); + }), + validator: (position) { + if (position!.isEmpty) { + return "This field is required"; + } + return null; + }, + ); + }), + const SizedBox( + height: 12, + ), + ////AGENCY + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + SearchField( + itemHeight: 70, + focusNode: agencyFocusNode, + suggestions: state.agencies + .map((Agency agency) => + SearchFieldListItem(agency.name!, + item: agency, + child: ListTile( + title: Text( + agency.name!, + overflow: + TextOverflow.ellipsis, + ), + subtitle: Text(agency + .privateEntity == + true + ? "Private" + : agency.privateEntity == + false + ? "Government" + : ""), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle("Agency *", "") + .copyWith( + suffixIcon: const Icon( + Icons.arrow_drop_down)), + onSuggestionTap: (agency) { + setState(() { + agencyFocusNode.unfocus(); + }); + }, + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + emptyWidget: EmptyWidget( + controller: addAgencyController, + onpressed: () { + setState(() { + Navigator.pop(context); + }); + }, + title: "Add Agency")), + + SizedBox( + height: showAgency ? 12 : 0, + ), + ////SHOW CATEGORY AGENCY + SizedBox( + child: showAgency + ? SearchField( + focusNode: agencyCategoryFocusNode, + itemHeight: 70, + suggestions: state.agencyCategory + .map((Category category) => + SearchFieldListItem( + category.name!, + item: category, + child: ListTile( + title: + Text(category.name!), + subtitle: Text(category + .industryClass! + .name!), + ))) + .toList(), + emptyWidget: Container( + height: 100, + decoration: box1(), + child: const Center( + child: + Text("No result found ...")), + ), + onSuggestionTap: (agencyCategory) { + setState(() {}); + }, + searchInputDecoration: + normalTextFieldStyle( + "Category *", "") + .copyWith( + suffixIcon: const Icon( + Icons.arrow_drop_down)), + validator: (value) { + if (value!.isEmpty) { + return "This field is required"; + } + return null; + }, + ) + : const SizedBox(), + ), + + ////PRVIATE SECTOR + SizedBox( + child: showIsPrivateRadio + ? FormBuilderRadioGroup( + decoration: InputDecoration( + border: InputBorder.none, + label: Row( + children: [ + Text( + "Is this private sector? ", + style: Theme.of(context) + .textTheme + .headlineSmall! + .copyWith(fontSize: 24), + ), + const Icon( + FontAwesome.help_circled) + ], + ), + ), + + ////onvhange private sector + onChanged: (value) { + setState(() { + agencyCategoryFocusNode.unfocus(); + }); + }, + + name: 'isPrivate', + validator: + FormBuilderValidators.required(), + options: ["YES", "NO"] + .map((lang) => + FormBuilderFieldOption( + value: lang)) + .toList(growable: false), + ) + : const SizedBox()), + const SizedBox( + height: 12, + ), + FormBuilderTextField( + name: "total_hours", + keyboardType: TextInputType.number, + decoration: + normalTextFieldStyle("Total Hours*", "0"), + ), + const SizedBox( + height: 12, + ), + ////Currently Involved + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: currentlyInvolved, + activeColor: second, + onChanged: (value) { + setState(() { + currentlyInvolved = value!; + }); + }, + decoration: normalTextFieldStyle( + "Currently Involved?", 'Graduated?'), + name: 'currently_involved', + title: Text( + currentlyInvolved ? "YES" : "NO"), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + //// FROM DATE + Flexible( + flex: 1, + child: DateTimePicker( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + use24HourFormat: false, + icon: const Icon( + Icons.date_range), + controller: fromDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + timeHintText: + "Date of Examination/Conferment", + decoration: + normalTextFieldStyle( + "From *", "From *") + .copyWith( + prefixIcon: + const Icon( + Icons.date_range, + color: Colors.black87, + )), + initialValue: null, + )), + const SizedBox( + width: 12, + ), + //// TO DATE + Flexible( + flex: 1, + child: currentlyInvolved + ? TextFormField( + enabled: false, + initialValue: "PRESENT", + style: const TextStyle( + color: Colors.black45), + decoration: + normalTextFieldStyle( + "", "") + .copyWith(), + ) + : DateTimePicker( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + controller: + toDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + decoration: normalTextFieldStyle( + "To *", "To *") + .copyWith( + prefixIcon: + const Icon( + Icons.date_range, + color: Colors + .black87, + ), + prefixText: + currentlyInvolved + ? "PRESENT" + : ""), + initialValue: null, + ), + ), + ], + ), + ), + ], + ); + }), + ], + ); + }), + const SizedBox(height: 12,), + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: normalTextFieldStyle( + "Overseas Address?", ''), + name: 'overseas', + title: Text(overseas ? "YES" : "NO"), + ), + SizedBox( + height: overseas == true ? 8 : 0, + ), + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + FormBuilderDropdown( + autovalidateMode: AutovalidateMode + .onUserInteraction, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + onChanged: (Region? region) async { + if (selectedRegion != region) { + setState(() { + provinceCall = true; + }); + selectedRegion = region; + getProvinces(); + } + }, + initialValue: null, + decoration: normalTextFieldStyle( + "Region*", "Region"), + name: 'region', + items: state.regions + .map>( + (Region region) { + return DropdownMenuItem( + value: region, + child: Text( + region.description!)); + }).toList(), + ), + const SizedBox( + height: 8, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + value: selectedProvince, + onChanged: + (Province? province) { + if (selectedProvince != + province) { + setState(() { + cityCall = true; + }); + selectedProvince = + province; + getCities(); + } + }, + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province province) { + return DropdownMenuItem( + value: province, + child: FittedBox( + child: Text(province + .description!), + )); + }).toList(), + decoration: + normalTextFieldStyle( + "Province*", + "Province")), + ), + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: DropdownButtonFormField< + CityMunicipality>( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + isExpanded: true, + onChanged: + (CityMunicipality? city) { + if (selectedMunicipality != + city) { + + selectedMunicipality = city; + + } + }, + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), + ), + ), + ), + + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( + height: 60, + child: FormBuilderDropdown( + initialValue: null, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + items: state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox( + child: + Text(country.name!))); + }).toList(), + name: 'country', + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) {}, + ), + ), + ), + + ], + ); + }), + ], + )), + ), + )), + ); + } + return const Placeholder(); + }, + ); + } + Future getProvinces() async { + try { + List newProvinces = await LocationUtils.instance + .getProvinces(regionCode: selectedRegion!.code.toString()); + setState(() { + provinces = newProvinces; + selectedProvince = provinces![0]; + provinceCall = false; + cityCall = true; + getCities(); + }); + } catch (e) { + context.read().add(ShowErrorState(message: e.toString())); + } + } + + Future getCities() async { + try { + List newCities = await LocationUtils.instance + .getCities(code: selectedProvince!.code.toString()); + citymuns = newCities; + setState(() { + selectedMunicipality = newCities[0]; + cityCall = false; + }); + } catch (e) { + context.read().add(ShowErrorState(message: e.toString())); + } + } +} diff --git a/lib/screens/profile/shared/add_for_empty_search.dart b/lib/screens/profile/shared/add_for_empty_search.dart new file mode 100644 index 0000000..2685e51 --- /dev/null +++ b/lib/screens/profile/shared/add_for_empty_search.dart @@ -0,0 +1,68 @@ +import 'package:flutter/material.dart'; + +import '../../../theme-data.dart/box_shadow.dart'; +import '../../../theme-data.dart/btn-style.dart'; +import '../../../theme-data.dart/colors.dart'; +import '../../../theme-data.dart/form-style.dart'; + +class EmptyWidget extends StatelessWidget { + final TextEditingController controller; + final Function()? onpressed; + final String title; + const EmptyWidget( + {super.key, required this.controller, required this.onpressed,required this.title}); + + @override + Widget build(BuildContext context) { + return Container( + decoration: box1(), + height: 100, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox( + height: 20, + ), + const Text("No result found..."), + const SizedBox( + height: 10, + ), + TextButton( + onPressed: () { + ////ADD POSITION DIALOG + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text(title), + content: SizedBox( + height: 130, + child: Column( + children: [ + TextFormField( + controller: controller, + decoration: normalTextFieldStyle("", ""), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + style: mainBtnStyle(primary, + Colors.transparent, second), + onPressed: onpressed, + child: const Text("Add"))), + ], + ), + ), + ); + }); + }, + child: Text(title)) + ]), + ); + } +} \ No newline at end of file diff --git a/lib/screens/unit2/homepage.dart/components/dashboard.dart b/lib/screens/unit2/homepage.dart/components/dashboard.dart index eed381c..5a94357 100644 --- a/lib/screens/unit2/homepage.dart/components/dashboard.dart +++ b/lib/screens/unit2/homepage.dart/components/dashboard.dart @@ -1,97 +1,224 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:unit2/screens/docsms/components/request_receipt.dart'; +import 'package:unit2/screens/docsms/index.dart'; import 'package:unit2/screens/unit2/homepage.dart/module-screen.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; +import 'package:unit2/utils/global_context.dart'; +import 'package:unit2/utils/qr_scanner.dart'; +import 'package:unit2/utils/text_container.dart'; + +import '../../../../bloc/docsms/docsms_bloc.dart'; class DashBoard extends StatelessWidget { final List roles; const DashBoard({super.key, required this.roles}); @override Widget build(BuildContext context) { + List finishRoles = []; return Container( padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 24), height: MediaQuery.of(context).size.height, - //listview builder + ////listview builder child: ListView.builder( scrollDirection: Axis.vertical, shrinkWrap: true, itemCount: roles.length, itemBuilder: (BuildContext context, int index) { - // gridview.count - return SizedBox( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - roles[index].name.toUpperCase(), - style: Theme.of(context) - .textTheme - .labelLarge! - .copyWith(fontSize: 12), - ), - - const SizedBox( - height: 8, - ), - GridView.count( - shrinkWrap: true, - crossAxisCount: 4, - crossAxisSpacing: 8, - mainAxisSpacing: 10, - physics: const BouncingScrollPhysics(), - padding: const EdgeInsets.symmetric( - vertical: 5, horizontal: 5), - children: roles[index].roles.map((role) { - return Container( - padding: const EdgeInsetsDirectional.fromSTEB(8,5,8,13), - alignment: Alignment.center, - - decoration:const BoxDecoration( - color: Colors.white, - boxShadow:[ BoxShadow(color: Colors.black12,spreadRadius: 2,blurRadius: 3)], - borderRadius: BorderRadius.all(Radius.circular(8)) - ), - child: GestureDetector( - onTap: () { - }, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - child: Icon( - - role.icon, - size: 20, - weight: 100, - grade: 100, - color:second, - ), - ), - const SizedBox( - height: 5, - ), - Text( - role.role.name!.toLowerCase() == "establishment point-person"?"Est. point-person": - role.role.name!, - textAlign: TextAlign.center, - style: Theme.of(context) - .textTheme - .labelLarge! - .copyWith( - fontSize: blockSizeVertical*1.1, - fontWeight: FontWeight.bold), - ), - ]), - ), - ); - }).toList()), - const SizedBox(height: 8,) - ], - ), - ); + //// gridview.count + return roles[index].roles.isNotEmpty + ? SizedBox( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + roles[index].name.toUpperCase(), + style: Theme.of(context) + .textTheme + .labelLarge! + .copyWith(fontSize: 12), + ), + const SizedBox( + height: 8, + ), + GridView.count( + shrinkWrap: true, + crossAxisCount: 4, + crossAxisSpacing: 8, + mainAxisSpacing: 10, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.symmetric( + vertical: 5, horizontal: 5), + children: roles[index].roles.map((role) { + //// if role name is qr code && finishRoles not contain security + //// this card will visible if the user has qr code scanner or security role + if (role.role.name!.toLowerCase() == + 'qr code scanner' && + !finishRoles.contains("security")) { + finishRoles.add('scanner'); + //// loop thru module to find unit module exist + for (var element in role.role.modules!) { + if (element!.name!.toLowerCase() == 'unit2') { + for (var element in element.objects!) { + //// loop thru objects to find pass check exist + if (element!.id == 9 && + //// check if operations contains read and write + element.operations! + .contains("read")) { + //// if all conditions are true return card + return CardLabel( + ontap: () {}, + icon: role.icon, + title: "Pass Check", + ); + } + } + } + } + return Container(); + //// if role name is security + //// this card will visible if the user has qr code scanner or security role + } else if (role.role.name!.toLowerCase() == + 'security guard' && + !finishRoles.contains('scanner')) { + finishRoles.add('security'); + for (var element in role.role.modules!) { + if (element!.name!.toLowerCase() == 'unit2') { + for (var element in element.objects!) { + if (element!.id == 9 && + element.operations! + .contains("read")) { + return CardLabel( + ontap: () {}, + icon: role.icon, + title: "Pass Check", + ); + } + } + } + } + return Container(); + //// if role name is field surveyor + } else if (role.role.name!.toLowerCase() == + 'field surveyor') { + for (var element in role.role.modules!) { + if (element!.name!.toLowerCase() == 'rpass') { + for (var element in element.objects!) { + if (element!.id == 11 && + element.operations! + .contains("read")) { + return CardLabel( + ontap: () {}, + icon: role.icon, + title: "Field Surveyor", + ); + } + } + } + } + return Container(); + //// if role name is process server + } else if (role.role.name!.toLowerCase() == + 'process server') { + for (var element in role.role.modules!) { + if (element!.name!.toLowerCase() == + 'document management') { + for (var element in element.objects!) { + if (element!.id == 3 && + element.operations! + .contains("read")) { + return CardLabel( + ontap: () async { + String? qrBarcode = + await qrScanner(); + if (qrBarcode != null) { + Navigator.push(NavigationService.navigatorKey.currentContext!, MaterialPageRoute(builder: + (BuildContext context) { + return BlocProvider( + create: (context) => DocsmsBloc() + ..add(LoadDocument( + documentId: qrBarcode)), + child: + const AutoReceiveDocument(), + ); + })); + } + }, + icon: role.icon, + title: "Process Server", + ); + } + } + } + } + return Container(); + } else { + return Container(); + } + }).toList()), + const SizedBox( + height: 8, + ) + ], + ), + ) + : Container(); }), ); } } + +// ignore: must_be_immutable +class CardLabel extends StatelessWidget { + final String title; + final IconData icon; + final Function()? ontap; + const CardLabel( + {super.key, + required this.icon, + required this.title, + required this.ontap}); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: ontap, + child: Container( + padding: const EdgeInsetsDirectional.fromSTEB(8, 5, 8, 13), + alignment: Alignment.center, + decoration: const BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow(color: Colors.black12, spreadRadius: 2, blurRadius: 3) + ], + borderRadius: BorderRadius.all(Radius.circular(8))), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Icon( + icon, + size: 20, + weight: 100, + grade: 100, + color: second, + ), + ), + const SizedBox( + height: 5, + ), + Text( + title, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.labelLarge!.copyWith( + fontSize: blockSizeVertical * 1.2, + fontWeight: FontWeight.bold), + ), + ]), + ), + ); + } +} diff --git a/lib/screens/unit2/homepage.dart/module-screen.dart b/lib/screens/unit2/homepage.dart/module-screen.dart index ba232cc..5638de6 100644 --- a/lib/screens/unit2/homepage.dart/module-screen.dart +++ b/lib/screens/unit2/homepage.dart/module-screen.dart @@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_zoom_drawer/flutter_zoom_drawer.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:fluttericon/rpg_awesome_icons.dart'; import 'package:fluttericon/typicons_icons.dart'; import 'package:unit2/screens/unit2/homepage.dart/components/dashboard.dart'; import 'package:unit2/theme-data.dart/colors.dart'; @@ -21,7 +22,8 @@ class MainScreen extends StatefulWidget { class _MainScreenState extends State { List roles = [ Module(name: 'UniT2 module operations', roles: []), - Module(name: 'DocSms module operations', roles: []) + Module(name: 'DocSms module operations', roles: []), + Module(name: "RPAss module operations",roles:[] ) ]; @override Widget build(BuildContext context) { @@ -43,6 +45,10 @@ class _MainScreenState extends State { IconData iconData = iconGenerator(getRole!.name!); Roles newRole = Roles(role: getRole, icon: iconData); roles[1].roles.add(newRole); + } if (module.name!.toLowerCase() == 'rpass') { + IconData iconData = iconGenerator(getRole!.name!); + Roles newRole = Roles(role: getRole, icon: iconData); + roles[2].roles.add(newRole); } } } @@ -97,6 +103,8 @@ class _MainScreenState extends State { case 'process server': iconData = Typicons.doc_text; break; + case 'field surveyor': + iconData = RpgAwesome.telescope; } return iconData!; } diff --git a/lib/sevices/docsms/docsms_service.dart b/lib/sevices/docsms/docsms_service.dart new file mode 100644 index 0000000..f6bb665 --- /dev/null +++ b/lib/sevices/docsms/docsms_service.dart @@ -0,0 +1,36 @@ +import 'dart:convert'; + +import 'package:unit2/utils/request.dart'; +import 'package:unit2/utils/urls.dart'; +import 'package:http/http.dart' as http; +import '../../model/docsms/document.dart'; + +class AutoReceiveDocumentServices{ + static final AutoReceiveDocumentServices _instance = AutoReceiveDocumentServices(); + static AutoReceiveDocumentServices get instance => _instance; + + Future getDocument(String documentId) async { + String path = Url.instance.getDocument(); + Document? document; + Map params = {"t1.id": documentId.toString()}; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + try { + http.Response response = await Request.instance.getRequest(param: params,path:path,headers:headers); + + if (response.statusCode == 200) { + Map data = json.decode(response.body); + if(data['message'].toString().toLowerCase() == "data successfully fetched."){ + document = Document.fromJson(data['data']); + }else{ + document = null; + } + } + } catch (e) { + throw (e.toString()); + } + return document; + } + +} \ No newline at end of file diff --git a/lib/sevices/profile/education_services.dart b/lib/sevices/profile/education_services.dart index 1d6d93e..05a86f5 100644 --- a/lib/sevices/profile/education_services.dart +++ b/lib/sevices/profile/education_services.dart @@ -9,12 +9,12 @@ import 'package:http/http.dart' as http; class EducationService { static final EducationService _instance = EducationService(); static EducationService get instace => _instance; - +////get educational background Future> getEducationalBackground( int profileId, String token) async { List educationalBackgrounds = []; String authToken = "Token $token"; - String path = "${Url.instance.getEducationalBackgrounds()}$profileId/"; + String path = "${Url.instance.educationalBackground()}$profileId/"; Map headers = { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': authToken @@ -38,4 +38,201 @@ class EducationService { return educationalBackgrounds; } + + ////Add + Future> add( + {required EducationalBackground educationalBackground, + required String token, + required int profileId, + required List honors}) async { + String authtoken = "Token $token"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authtoken + }; + String path = '${Url.instance.educationalBackground()}$profileId/'; + Map statusResponse = {}; + Map body = { + "_level": educationalBackground.education!.level, + "_schoolId": educationalBackground.education?.school?.id, + "_schoolName": educationalBackground.education!.school!.name, + "_programId": educationalBackground.education?.course?.id, + "_programName": educationalBackground.education?.course?.program, + "_course": null, + "period_from": educationalBackground.periodFrom.toString(), + "period_to": educationalBackground.periodTo.toString(), + "units_earned": educationalBackground.unitsEarned, + "year_graduated": educationalBackground.yearGraduated, + "honors": honors.isEmpty ? [] : honors, + }; + try { + http.Response response = await Request.instance + .postRequest(path: path, param: {}, body: body, headers: headers); + if (response.statusCode == 201) { + Map data = jsonDecode(response.body); + statusResponse = data; + } else { + statusResponse.addAll({'success': false}); + } + } catch (e) { + throw e.toString(); + } + return statusResponse; + } + + ////Edit + Future> edit( + {required EducationalBackground educationalBackground, + required String token, + required int profileId, + required List honors}) async { + String authtoken = "Token $token"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authtoken + }; + String path = '${Url.instance.educationalBackground()}$profileId/'; + Map statusResponse = {}; + Map body = { + "id": educationalBackground.id, + "_level": educationalBackground.education!.level, + "_schoolId": educationalBackground.education?.school?.id, + "_schoolName": educationalBackground.education!.school!.name, + "_programId": educationalBackground.education?.course?.id, + "_programName": educationalBackground.education?.course?.program, + "_course": null, + "period_from": educationalBackground.periodFrom.toString(), + "period_to": educationalBackground.periodTo.toString(), + "units_earned": educationalBackground.unitsEarned, + "year_graduated": educationalBackground.yearGraduated, + "honors": honors.isEmpty ? [] : honors, + }; + try { + http.Response response = await Request.instance + .putRequest(path: path, param: {}, body: body, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + statusResponse = data; + } else { + statusResponse.addAll({'success': false}); + } + } catch (e) { + throw e.toString(); + } + return statusResponse; + } + + //// delete + Future delete( + {required int profileId, + required String token, + required EducationalBackground educationalBackground}) async { + String authToken = "Token $token"; + String path = "${Url.instance.educationalBackground()}$profileId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + bool? success; + Map params = {"force_mode": "true"}; + Map body = { + "id": educationalBackground.id, + "_level": educationalBackground.education!.level, + "_schoolId": educationalBackground.education!.school!.id, + "education_id": educationalBackground.education!.id, + "period_from": educationalBackground.periodFrom, + "period_to": educationalBackground.periodTo, + "units_earned": educationalBackground.unitsEarned, + "year_graduated": educationalBackground.yearGraduated, + "honors": educationalBackground.honors + }; + try { + http.Response response = await Request.instance.deleteRequest( + path: path, headers: headers, body: body, param: params); + + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + success = data['success']; + } else { + success = false; + } + } catch (e) { + throw e.toString(); + } + return success!; + } + +//// get schools + Future> getSchools() async { + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + List schools = []; + String path = Url.instance.getSchools(); + try { + http.Response response = await Request.instance + .getRequest(param: {}, path: path, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((school) { + School newSchool = School.fromJson(school); + schools.add(newSchool); + }); + } + } + } catch (e) { + throw e.toString(); + } + return schools; + } + + Future> getPrograms() async { + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + List programs = []; + String path = Url.instance.getPrograms(); + try { + http.Response response = await Request.instance + .getRequest(param: {}, path: path, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((course) { + Course newCourse = Course.fromJson(course); + programs.add(newCourse); + }); + } + } + } catch (e) { + throw e.toString(); + } + return programs; + } + +////get honors + Future> getHonors() async { + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + List honors = []; + String path = Url.instance.getHonors(); + try { + http.Response response = await Request.instance + .getRequest(param: {}, path: path, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((honor) { + Honor newHonor = Honor.fromJson(honor); + honors.add(newHonor); + }); + } + } + } catch (e) { + throw e.toString(); + } + return honors; + } } diff --git a/lib/sevices/profile/work_history_services.dart b/lib/sevices/profile/work_history_services.dart index 8e45820..99d7582 100644 --- a/lib/sevices/profile/work_history_services.dart +++ b/lib/sevices/profile/work_history_services.dart @@ -164,7 +164,7 @@ class WorkHistoryService { } -//get agency position +////get agency position Future> getAgencyPosition() async { List agencyPositions = []; String path = Url.instance.getPositions(); diff --git a/lib/utils/app_router.dart b/lib/utils/app_router.dart index 9db2326..2769d80 100644 --- a/lib/utils/app_router.dart +++ b/lib/utils/app_router.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:unit2/bloc/docsms/docsms_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/sos/sos_bloc.dart'; +import 'package:unit2/screens/docsms/index.dart'; import 'package:unit2/screens/sos/index.dart'; import 'package:unit2/screens/unit2/homepage.dart/components/menu.dart'; import 'package:unit2/screens/unit2/login/login.dart'; @@ -51,6 +53,7 @@ class AppRouter { child: const SosScreen(), ); }); + default: return MaterialPageRoute(builder: (context) { return Container(); diff --git a/lib/utils/profile_utilities.dart b/lib/utils/profile_utilities.dart index 310dc61..6debf6f 100644 --- a/lib/utils/profile_utilities.dart +++ b/lib/utils/profile_utilities.dart @@ -1,4 +1,3 @@ - import 'dart:convert'; import 'package:unit2/model/location/country.dart'; @@ -11,35 +10,63 @@ import 'package:unit2/utils/urls.dart'; import '../model/profile/basic_information/contact_information.dart'; import '../model/utils/agency.dart'; import '../model/utils/category.dart'; +import '../model/utils/position.dart'; + class ProfileUtilities { static final ProfileUtilities _instance = ProfileUtilities(); static ProfileUtilities get instance => _instance; - Future>getEligibilities()async{ - List eligibilities=[]; + Future> getEligibilities() async { + List eligibilities = []; String path = Url.instance.eligibilities(); - + Map headers = { 'Content-Type': 'application/json; charset=UTF-8', }; - try{ - http.Response response = await Request.instance.getRequest(path: path, param: {},headers: headers); - if(response.statusCode == 200){ - Map data = jsonDecode(response.body); - if(data['data'] != null){ - data['data'].forEach((var eligibility){ - Eligibility newEligibilities = Eligibility.fromJson(eligibility); - eligibilities.add(newEligibilities); - }); + try { + http.Response response = await Request.instance + .getRequest(path: path, param: {}, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((var eligibility) { + Eligibility newEligibilities = Eligibility.fromJson(eligibility); + eligibilities.add(newEligibilities); + }); + } + } + } catch (e) { + throw (e.toString()); } - } - }catch(e){ - throw(e.toString()); - } - return eligibilities; + return eligibilities; } - //get agencies +////get agency position + Future> getAgencyPosition() async { + List agencyPositions = []; + String path = Url.instance.getPositions(); + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + try { + http.Response response = await Request.instance + .getRequest(param: {}, path: path, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((var agencyPosition) { + Position position = Position.fromJson(agencyPosition); + agencyPositions.add(position); + }); + } + } + } catch (e) { + throw (e.toString()); + } + return agencyPositions; + } + + ////get agencies Future> getAgecies() async { List agencies = []; String path = Url.instance.getAgencies(); @@ -64,8 +91,7 @@ class ProfileUtilities { return agencies; } - -//get agency category +////get agency category Future> agencyCategory() async { List agencyCategory = []; String path = Url.instance.getAgencyCategory(); @@ -91,28 +117,28 @@ class ProfileUtilities { } //// get service type - Future> getServiceType()async{ + Future> getServiceType() async { List serviceTypes = []; - Map headers = { + Map headers = { 'Content-Type': 'application/json; charset=UTF-8', }; String path = Url.instance.getServiceTypes(); - try{ - http.Response response = await Request.instance.getRequest(param: {},path:path,headers: headers ); - if(response.statusCode == 200){ + try { + http.Response response = await Request.instance + .getRequest(param: {}, path: path, headers: headers); + if (response.statusCode == 200) { Map data = jsonDecode(response.body); - if(data['data'] != null){ - for(var element in data['data']){ + if (data['data'] != null) { + for (var element in data['data']) { ServiceType newServiceType = ServiceType.fromJson(element); serviceTypes.add(newServiceType); } } } - }catch(e){ + } catch (e) { throw e.toString(); } return serviceTypes; } - -} \ No newline at end of file +} diff --git a/lib/utils/qr_scanner.dart b/lib/utils/qr_scanner.dart new file mode 100644 index 0000000..003161d --- /dev/null +++ b/lib/utils/qr_scanner.dart @@ -0,0 +1,17 @@ +import 'package:barcode_scan2/barcode_scan2.dart'; +import 'package:unit2/utils/scanner.dart'; + +Future qrScanner() async { + String? result; + try { + ScanResult afterScan = await QRCodeBarCodeScanner.instance.scanner(); + if (afterScan.type == ResultType.Barcode) { + result = afterScan.rawContent; + return result; + } + } catch (e) { + throw e.toString(); + } + + return null; +} diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index 0d165e9..de84310 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -6,8 +6,8 @@ class Url { // return '192.168.10.183:3000'; // return 'agusandelnorte.gov.ph'; // return "192.168.10.219:3000"; - return "devweb.agusandelnorte.gov.ph"; - // return 'devapi.agusandelnorte.gov.ph:3004'; + // return "devweb.agusandelnorte.gov.ph"; + return 'devapi.agusandelnorte.gov.ph:3004'; } String authentication() { @@ -27,6 +27,11 @@ class Url { return "/api/sos_app/sos_request/"; } + //// DOCSMS paths + String getDocument(){ + return "/api/web_app/public/document_viewer/"; + } + ////ELIGIBILITIES PATHS String eligibilities(){ return "/api/jobnet_app/eligibilities/"; @@ -57,17 +62,24 @@ String getAgencies(){ return "/api/jobnet_app/agencies/"; } - - String getAgencyCategory(){ return "api/jobnet_app/agency_categories/"; } ////educational background paths -String getEducationalBackgrounds(){ +String educationalBackground(){ return "/api/jobnet_app/profile/pds/education/"; } +String getSchools(){ + return "/api/jobnet_app/schools/"; +} +String getPrograms(){ + return "api/jobnet_app/education_programs/"; +} +String getHonors(){ + return "/api/jobnet_app/honors"; +} //// learning and development paths diff --git a/pubspec.lock b/pubspec.lock index c34bf28..1f2ef3f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -677,6 +677,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" + location: + dependency: "direct main" + description: + name: location + sha256: "9051959f6f2ccadd887b28b66e9cbbcc25b6838e37cf9e894c421ccc0ebf80b5" + url: "https://pub.dev" + source: hosted + version: "4.4.0" + location_platform_interface: + dependency: transitive + description: + name: location_platform_interface + sha256: "62eeaf1658e92e4459b727f55a3c328eccbac8ba043fa6d262ac5286ad48384c" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + location_web: + dependency: transitive + description: + name: location_web + sha256: "6c08c408a040534c0269c4ff9fe17eebb5a36dea16512fbaf116b9c8bc21545b" + url: "https://pub.dev" + source: hosted + version: "3.1.1" logging: dependency: transitive description: @@ -741,6 +765,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.0" + multi_dropdown: + dependency: "direct main" + description: + name: multi_dropdown + sha256: "65b8f505b251c5173e8eff64c09cd37460d8f8be906c2069a03815ad2641dcd1" + url: "https://pub.dev" + source: hosted + version: "1.0.9" nested: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index ab0bffe..943b2ce 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -77,7 +77,7 @@ dependencies: hive_flutter: ^1.1.0 mask_text_input_formatter: ^2.4.0 location: ^4.3.0 - platform_device_id: ^1.0.1 + platform_device_id: ^1.0.1, dev_dependencies: flutter_test: From 1c6b2918894f4fd590988133f4b49e9a8ffce931 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Tue, 2 May 2023 08:26:42 +0800 Subject: [PATCH 66/86] Implemnent Voluntary works and civic API --- .../address/address_bloc.dart | 2 + .../address/address_event.dart | 9 + .../address/address_state.dart | 9 + .../voluntary_works/voluntary_work_bloc.dart | 225 +++++- .../voluntary_works/voluntary_work_event.dart | 64 +- .../voluntary_works/voluntary_work_state.dart | 42 +- lib/model/profile/voluntary_works.dart | 4 +- .../basic_information/address/add_modal.dart | 19 +- .../components/education/add_modal.dart | 3 + .../components/education/edit_modal.dart | 17 +- .../profile/components/education_screen.dart | 9 +- .../components/eligibility/add_modal.dart | 4 +- .../components/voluntary_works/add_modal.dart | 685 ++++++++++++++++ .../voluntary_works/edit_modal.dart | 754 ++++++++++++++++++ .../components/voluntary_works_screen.dart | 211 ++++- .../voluntary_works/add_modal.dart | 597 -------------- lib/sevices/profile/volunatary_services.dart | 157 +++- lib/utils/urls.dart | 4 +- pubspec.yaml | 3 +- 19 files changed, 2139 insertions(+), 679 deletions(-) create mode 100644 lib/screens/profile/components/voluntary_works/add_modal.dart create mode 100644 lib/screens/profile/components/voluntary_works/edit_modal.dart delete mode 100644 lib/screens/profile/components/work_history/voluntary_works/add_modal.dart diff --git a/lib/bloc/profile/primary_information/address/address_bloc.dart b/lib/bloc/profile/primary_information/address/address_bloc.dart index 00f09c0..75cafa4 100644 --- a/lib/bloc/profile/primary_information/address/address_bloc.dart +++ b/lib/bloc/profile/primary_information/address/address_bloc.dart @@ -1,6 +1,8 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:unit2/bloc/profile/eligibility/eligibility_bloc.dart'; import 'package:unit2/model/location/barangay.dart'; +import 'package:unit2/model/profile/voluntary_works.dart'; import '../../../../model/location/city.dart'; import '../../../../model/location/country.dart'; diff --git a/lib/bloc/profile/primary_information/address/address_event.dart b/lib/bloc/profile/primary_information/address/address_event.dart index f1d36f4..9446ba2 100644 --- a/lib/bloc/profile/primary_information/address/address_event.dart +++ b/lib/bloc/profile/primary_information/address/address_event.dart @@ -23,3 +23,12 @@ class ShowEditAddressForm extends AddressEvent{ class CallErrorState extends AddressEvent{ } + +class AddAddress extends EligibilityEvent{ + final MainAdress address; + final String token; + final int profileId; + const AddAddress({required this.address, required this.profileId, required this.token}); + @override + List get props => [address,token,profileId]; +} diff --git a/lib/bloc/profile/primary_information/address/address_state.dart b/lib/bloc/profile/primary_information/address/address_state.dart index 563a5f4..6c9e70d 100644 --- a/lib/bloc/profile/primary_information/address/address_state.dart +++ b/lib/bloc/profile/primary_information/address/address_state.dart @@ -37,6 +37,15 @@ class AddAddressState extends AddressState { List get props => [countries, regions,]; } +////AddedState +class AddressAddedState extends AddressState{ + final Map response; + const AddressAddedState({required this.response}); + @override + List get props => [response]; + +} + class EditAddressState extends AddressState{ final MainAdress address; final List countries; diff --git a/lib/bloc/profile/voluntary_works/voluntary_work_bloc.dart b/lib/bloc/profile/voluntary_works/voluntary_work_bloc.dart index 0480f9c..5c5afa2 100644 --- a/lib/bloc/profile/voluntary_works/voluntary_work_bloc.dart +++ b/lib/bloc/profile/voluntary_works/voluntary_work_bloc.dart @@ -1,14 +1,19 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; import 'package:unit2/sevices/profile/volunatary_services.dart'; import 'package:unit2/utils/profile_utilities.dart'; +import '../../../model/location/city.dart'; import '../../../model/location/country.dart'; +import '../../../model/location/provinces.dart'; import '../../../model/location/region.dart'; import '../../../model/profile/voluntary_works.dart'; import '../../../model/utils/agency.dart'; import '../../../model/utils/category.dart'; import '../../../model/utils/position.dart'; +import '../../../utils/location_utilities.dart'; part 'voluntary_work_event.dart'; part 'voluntary_work_state.dart'; @@ -16,27 +21,42 @@ part 'voluntary_work_state.dart'; class VoluntaryWorkBloc extends Bloc { VoluntaryWorkBloc() : super(VoluntaryWorkInitial()) { List voluntaryWorks = []; - List globalCountries=[]; - List agencyCategory = []; - List globalRegions=[]; - List agencyPositions = []; - List agencies = []; - ////Get Voluntary Works - on((event, emit) async{ + List globalCountries = []; + List agencyCategory = []; + List globalRegions = []; + List agencyPositions = []; + List agencies = []; + List provinces = []; + List cities = []; + ///// current + Position currentPosition; + Agency currentAgency; + Region? currentRegion; + Country currentCountry; + Province? currentProvince; + CityMunicipality? currentCity; + ////Get Voluntary Works + on((event, emit) async { emit(VoluntaryWorkLoadingState()); - try{ - List works = await VoluntaryService.instance.getVoluntaryWorks(event.profileId, event.token); - voluntaryWorks = works; - emit(VoluntaryWorkLoadedState(voluntaryWorks: voluntaryWorks)); - }catch(e){ - emit(VoluntaryWorkErrorState(message: e.toString())); - } + try { + List works = await VoluntaryService.instance + .getVoluntaryWorks(event.profileId, event.token); + voluntaryWorks = works; + emit(VoluntaryWorkLoadedState(voluntaryWorks: voluntaryWorks)); + } catch (e) { + emit(VoluntaryWorkErrorState(message: e.toString())); + } + }); + //// Load + on((event, emit) { + emit(VoluntaryWorkLoadedState(voluntaryWorks: voluntaryWorks)); }); //// Show Add form Event - on((event,emit)async{ - try{ + on((event, emit) async { + try { emit(VoluntaryWorkLoadingState()); - if (agencyPositions.isEmpty) { + //// POSITIONS + if (agencyPositions.isEmpty) { List positions = await ProfileUtilities.instance.getAgencyPosition(); agencyPositions = positions; @@ -55,12 +75,177 @@ class VoluntaryWorkBloc extends Bloc { await ProfileUtilities.instance.agencyCategory(); agencyCategory = categoryAgencies; } - emit(AddVoluntaryWorkState(agencies: agencies,positions: agencyPositions,agencyCategory: agencyCategory,regions: globalRegions,countries: globalCountries)); + + ////regions + if (globalRegions.isEmpty) { + List regions = await LocationUtils.instance.getRegions(); + globalRegions = regions; + } + + //// country + if (globalCountries.isEmpty) { + List countries = await LocationUtils.instance.getCountries(); + globalCountries = countries; + } + + emit(AddVoluntaryWorkState( + agencies: agencies, + positions: agencyPositions, + agencyCategory: agencyCategory, + regions: globalRegions, + countries: globalCountries)); + } catch (e) { + emit(VoluntaryWorkErrorState(message: e.toString())); + } + }); + on((event, emit) { + emit(VoluntaryWorkErrorState(message: event.message)); + }); + //// Add Voluntary Work + on((event, emit) async { + try { + Map status = await VoluntaryService.instance.add( + voluntaryWork: event.work, + profileId: event.profileId, + token: event.token); + if (status['success']) { + VoluntaryWork work = VoluntaryWork.fromJson(status['data']); + voluntaryWorks.add(work); + emit(VoluntaryWorkAddedState(response: status)); + } else { + emit(VoluntaryWorkAddedState(response: status)); + } + } catch (e) { + emit(VoluntaryWorkErrorState(message: e.toString())); + } + }); + ////Update + on((event, emit) async { + final DateFormat formatter = DateFormat('yyyy-MM-dd'); + try{ + Map status = await VoluntaryService.instance.update( + voluntaryWork: event.work, + profileId: event.profileId, + token: event.token, + oldPosId: event.oldPosId, + oldAgencyId: event.oldAgencyId, + oldFromDate: formatter.format(DateTime.parse(event.oldFromDate))); + if (status['success']) { + VoluntaryWork work = VoluntaryWork.fromJson(status['data']); + voluntaryWorks.removeWhere((VoluntaryWork element) => + element.position!.id == event.oldPosId && + element.agency!.id == event.oldAgencyId && + element.fromDate.toString() == event.oldFromDate.toString()); + voluntaryWorks.add(work); + emit(VoluntaryWorkEditedState(response: status)); + } else { + emit(VoluntaryWorkEditedState(response: status)); + } }catch(e){ emit(VoluntaryWorkErrorState(message: e.toString())); } - });on((event,emit){ - emit(VoluntaryWorkErrorState(message: event.message)); + }); +/////SHOW EDIT FORM + on((event, emit) async { + try { + //// POSITIONS + if (agencyPositions.isEmpty) { + List positions = + await ProfileUtilities.instance.getAgencyPosition(); + agencyPositions = positions; + } + currentPosition = event.work.position!; + + /////AGENCIES------------------------------------------ + if (agencies.isEmpty) { + List newAgencies = + await ProfileUtilities.instance.getAgecies(); + agencies = newAgencies; + } + currentAgency = event.work.agency!; + + /////Category Agency------------------------------------------ + if (agencyCategory.isEmpty) { + List categoryAgencies = + await ProfileUtilities.instance.agencyCategory(); + agencyCategory = categoryAgencies; + } + + ////regions + if (globalRegions.isEmpty) { + List regions = await LocationUtils.instance.getRegions(); + globalRegions = regions; + } + + //// country + if (globalCountries.isEmpty) { + List countries = await LocationUtils.instance.getCountries(); + globalCountries = countries; + } + currentCountry = globalCountries.firstWhere((Country country) => + event.work.address!.country!.code == country.code); + ////If overseas + if (!event.isOverseas) { + //// if not overseas + currentRegion = globalRegions.firstWhere((Region region) => + event.work.address!.cityMunicipality!.province!.region!.code == + region.code); + provinces = await LocationUtils.instance + .getProvinces(regionCode: currentRegion!.code.toString()); + currentProvince = provinces.firstWhere((Province province) => + event.work.address!.cityMunicipality!.province!.code == + province.code); + + cities = await LocationUtils.instance + .getCities(code: currentProvince!.code.toString()); + + currentCity = cities.firstWhere((CityMunicipality cityMunicipality) => + event.work.address!.cityMunicipality!.code == + cityMunicipality.code); + } + emit(EditVoluntaryWorks( + overseas: event.isOverseas, + agencies: agencies, + agencyCategory: agencyCategory, + cities: cities, + countries: globalCountries, + positions: agencyPositions, + provinces: provinces, + regions: globalRegions, + work: event.work, + currentAgency: currentAgency, + currentCity: currentCity, + currentCountry: currentCountry, + currentPosition: currentPosition, + currentProvince: currentProvince, + currentRegion: currentRegion)); + } catch (e) { + emit(VoluntaryWorkErrorState(message: e.toString())); + } + }); + //// Delete + on((event, emit) async { + try { + final DateFormat formatter = DateFormat('yyyy-MM-dd'); + + final bool success = await VoluntaryService.instance.delete( + agencyId: event.work.agency!.id!, + positionId: event.work.position!.id!, + fromDate: formatter.format(event.work.fromDate!), + token: event.token, + profileId: event.profileId); + if (success) { + voluntaryWorks.removeWhere((VoluntaryWork element) => + element.position!.id == event.work.position!.id && + element.agency!.id == event.work.agency!.id && + element.fromDate == event.work.fromDate); + emit(VoluntaryWorkDeletedState(success: success)); + } else { + emit(VoluntaryWorkDeletedState(success: success)); + } + } catch (e) { + emit(VoluntaryWorkErrorState(message: e.toString())); + } }); } } diff --git a/lib/bloc/profile/voluntary_works/voluntary_work_event.dart b/lib/bloc/profile/voluntary_works/voluntary_work_event.dart index 52aba14..02ed79b 100644 --- a/lib/bloc/profile/voluntary_works/voluntary_work_event.dart +++ b/lib/bloc/profile/voluntary_works/voluntary_work_event.dart @@ -7,18 +7,68 @@ abstract class VoluntaryWorkEvent extends Equatable { List get props => []; } -class GetVoluntarWorks extends VoluntaryWorkEvent{ +class GetVoluntarWorks extends VoluntaryWorkEvent { final int profileId; final String token; const GetVoluntarWorks({required this.profileId, required this.token}); - @override - List get props => [profileId,token]; -} -class ShowAddVoluntaryWorks extends VoluntaryWorkEvent{ - + @override + List get props => [profileId, token]; } -class ShowErrorState extends VoluntaryWorkEvent{ +class ShowAddVoluntaryWorks extends VoluntaryWorkEvent {} + +class ShowEditVoluntaryWorks extends VoluntaryWorkEvent { + final VoluntaryWork work; + final int profileId; + final String token; + final bool isOverseas; + // final int oldPosId; + // final int oldAgencyId; + // final String oldFromDate; + const ShowEditVoluntaryWorks( + {required this.profileId, + required this.token, + required this.work, + // required this.oldAgencyId, + // required this.oldFromDate, + // required this.oldPosId, + required this.isOverseas}); + @override + List get props => [profileId, token, work]; +} + +class LoadVoluntaryWorks extends VoluntaryWorkEvent {} + +class ShowErrorState extends VoluntaryWorkEvent { final String message; const ShowErrorState({required this.message}); } + +class UpdateVolunataryWork extends VoluntaryWorkEvent{ + final int oldPosId; + final int oldAgencyId; + final String oldFromDate; + final VoluntaryWork work; + final int profileId; + final String token; + const UpdateVolunataryWork({required this.oldAgencyId, required this.oldFromDate, required this.oldPosId, required this.profileId, required this.token, required this.work}); +} +class AddVoluntaryWork extends VoluntaryWorkEvent { + final int profileId; + final String token; + final VoluntaryWork work; + const AddVoluntaryWork( + {required this.profileId, required this.token, required this.work}); + @override + List get props => [profileId, token, work]; +} + +class DeleteVoluntaryWork extends VoluntaryWorkEvent { + final String token; + final int profileId; + final VoluntaryWork work; + const DeleteVoluntaryWork( + {required this.profileId, required this.token, required this.work}); + @override + List get props => [profileId, token, work]; +} diff --git a/lib/bloc/profile/voluntary_works/voluntary_work_state.dart b/lib/bloc/profile/voluntary_works/voluntary_work_state.dart index 3024e12..a5baeeb 100644 --- a/lib/bloc/profile/voluntary_works/voluntary_work_state.dart +++ b/lib/bloc/profile/voluntary_works/voluntary_work_state.dart @@ -26,8 +26,41 @@ class VoluntaryWorkErrorState extends VoluntaryWorkState{ class VoluntaryWorkLoadingState extends VoluntaryWorkState{ } +////Added State +class VoluntaryWorkAddedState extends VoluntaryWorkState{ + final Map response; + const VoluntaryWorkAddedState({required this.response}); + @override + List get props => [response]; +} +////Added State +class VoluntaryWorkEditedState extends VoluntaryWorkState{ + final Map response; + const VoluntaryWorkEditedState({required this.response}); + @override + List get props => [response]; +} +class EditVoluntaryWorks extends VoluntaryWorkState{ + final VoluntaryWork work; + final List positions; + final List agencies; + final List agencyCategory; + final List countries; + final List regions; + final List provinces; + final List cities; + final Position currentPosition; + final Agency currentAgency; + final Region? currentRegion; + final Country currentCountry; + final Province? currentProvince; + final CityMunicipality? currentCity; + final bool overseas; + const EditVoluntaryWorks({required this.agencies, required this.agencyCategory, required this.cities, required this.countries,required this.positions, required this.provinces, required this.regions, required this.work, required this.currentAgency, required this.currentCity,required this.currentCountry, required this.currentPosition, required this.currentProvince, required this.currentRegion,required this.overseas}); +} +////Adding State class AddVoluntaryWorkState extends VoluntaryWorkState{ final List positions; final List agencies; @@ -37,4 +70,11 @@ class AddVoluntaryWorkState extends VoluntaryWorkState{ const AddVoluntaryWorkState({required this.agencies,required this.countries, required this.positions, required this.regions,required this.agencyCategory}); @override List get props => [positions,agencies,countries,regions]; -} \ No newline at end of file +} +//// Deleted State +class VoluntaryWorkDeletedState extends VoluntaryWorkState{ + final bool success; + const VoluntaryWorkDeletedState({required this.success}); + @override + List get props => [success]; +} diff --git a/lib/model/profile/voluntary_works.dart b/lib/model/profile/voluntary_works.dart index 8f5cdae..c86d35b 100644 --- a/lib/model/profile/voluntary_works.dart +++ b/lib/model/profile/voluntary_works.dart @@ -29,12 +29,12 @@ class VoluntaryWork { final DateTime? toDate; final Position? position; final DateTime? fromDate; - final int? totalHours; + final double? totalHours; factory VoluntaryWork.fromJson(Map json) => VoluntaryWork( agency: json["agency"] == null ? null : Agency.fromJson(json["agency"]), address: json["address"] == null ? null : Address.fromJson(json["address"]), - toDate: json["to_date"] == null? null : DateTime.parse(json['to_data']), + toDate: json["to_date"] == null? null : DateTime.parse(json['to_date']), position: json["position"] == null ? null : Position.fromJson(json["position"]), fromDate: json["from_date"] == null ? null : DateTime.parse(json["from_date"]), totalHours: json["total_hours"], diff --git a/lib/screens/profile/components/basic_information/address/add_modal.dart b/lib/screens/profile/components/basic_information/address/add_modal.dart index c790d4d..af4e8af 100644 --- a/lib/screens/profile/components/basic_information/address/add_modal.dart +++ b/lib/screens/profile/components/basic_information/address/add_modal.dart @@ -6,6 +6,7 @@ import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; import 'package:unit2/bloc/profile/primary_information/address/address_bloc.dart'; +import 'package:unit2/model/location/address_category.dart'; import 'package:unit2/model/utils/category.dart'; import 'package:unit2/utils/global.dart'; @@ -38,6 +39,8 @@ class _AddAddressScreenState extends State { bool cityCall = false; bool barangayCall = false; ////selected + AddressCategory? selectedAddressCategory; + String? selectedAreaClass; Region? selectedRegion; Province? selectedProvince; CityMunicipality? selectedMunicipality; @@ -54,7 +57,7 @@ class _AddAddressScreenState extends State { const Area(value: "Metropolis", group: 1), const Area(value: "Megacity", group: 1), ]; - final List category = ["Permanent", "Residential", "Birthplace"]; + final List category = [AddressCategory(id: 1, name: "Permanent", type: "home"), AddressCategory(id: 2, name: "Residential", type: "home"), AddressCategory(id: 3, name: "Birthplace", type: "home")]; List? provinces; List? citymuns; List? barangays; @@ -75,17 +78,17 @@ class _AddAddressScreenState extends State { child: Column( children: [ //// category - FormBuilderDropdown( + FormBuilderDropdown( validator: FormBuilderValidators.required( errorText: "This field is required"), decoration: normalTextFieldStyle("Category*", "Category"), name: "category", - onChanged: (String? category) {}, - items: category.map>( - (String category) { + onChanged: (AddressCategory? category) {}, + items: category.map>( + (AddressCategory category) { return DropdownMenuItem( - value: category, child: Text(category)); + value: category, child: Text(category.name!)); }).toList()), const SizedBox( height: 12, @@ -97,7 +100,9 @@ class _AddAddressScreenState extends State { decoration: normalTextFieldStyle( "Area class *", "Area class"), name: "area_class", - onChanged: (Area? area) {}, + onChanged: (Area? area) { + selectedAreaClass = area!.value; + }, items: areaClass .map>((Area area) { return area.group == 0 diff --git a/lib/screens/profile/components/education/add_modal.dart b/lib/screens/profile/components/education/add_modal.dart index a4701e1..556f36b 100644 --- a/lib/screens/profile/components/education/add_modal.dart +++ b/lib/screens/profile/components/education/add_modal.dart @@ -448,6 +448,9 @@ class _AddEducationScreenState extends State { if (!graduated) { unitsEarned = int.parse(formKey .currentState!.value['units_earned']); + yearGraduated.text = ''; + }else{ + } ////education Education newEducation = Education( diff --git a/lib/screens/profile/components/education/edit_modal.dart b/lib/screens/profile/components/education/edit_modal.dart index 1038bbf..2c969c3 100644 --- a/lib/screens/profile/components/education/edit_modal.dart +++ b/lib/screens/profile/components/education/edit_modal.dart @@ -92,7 +92,6 @@ class _EditEducationScreenState extends State { } fromController.text = state.educationalBackground.periodFrom!; untilController.text = state.educationalBackground.periodTo!; - ////honors ////get all honors valueItemHonorList = state.honors.map((Honor honor) { @@ -147,6 +146,10 @@ class _EditEducationScreenState extends State { ////school StatefulBuilder(builder: (context, setState) { return SearchField( + suggestionAction: SuggestionAction.next, + onSubmit: (p0) { + schoolFocusNode.unfocus(); + }, controller: currentSchoolController, itemHeight: 50, suggestionsDecoration: box1(), @@ -171,7 +174,9 @@ class _EditEducationScreenState extends State { searchInputDecoration: normalTextFieldStyle("School *", "").copyWith( suffixIcon: - const Icon(Icons.arrow_drop_down)), + GestureDetector(child: const Icon(Icons.arrow_drop_down),onTap: (){ + schoolFocusNode.unfocus(); + },)), onSuggestionTap: (school) { setState(() { selectedSchool = school.item; @@ -497,10 +502,12 @@ class _EditEducationScreenState extends State { selectedLevel!.value == "Junior High" || selectedLevel!.value == "Senior High") { selectedProgram = null; - }else{ - selectedProgram ??= state.educationalBackground.education!.course; + } else { + selectedProgram ??= state + .educationalBackground.education!.course; } - selectedSchool ??= state.educationalBackground.education!.school; + selectedSchool ??= + state.educationalBackground.education!.school; ////education Education newEducation = Education( id: null, diff --git a/lib/screens/profile/components/education_screen.dart b/lib/screens/profile/components/education_screen.dart index a7f68df..6b94216 100644 --- a/lib/screens/profile/components/education_screen.dart +++ b/lib/screens/profile/components/education_screen.dart @@ -18,6 +18,7 @@ import 'package:unit2/widgets/error_state.dart'; import '../../../bloc/profile/education/education_bloc.dart'; import '../../../utils/alerts.dart'; +import '../../../widgets/Leadings/close_leading.dart'; import 'education/edit_modal.dart'; class EducationScreen extends StatelessWidget { @@ -32,11 +33,15 @@ class EducationScreen extends StatelessWidget { title: const Text(educationScreenTitle), centerTitle: true, backgroundColor: primary, - actions: [ + actions: context.watch().state is EducationalBackgroundLoadedState?[ AddLeading(onPressed: () { context.read().add(ShowAddEducationForm()); }) - ], + ]:(context.watch().state is AddEducationState || context.watch().state is EditEducationState)?[ + CloseLeading(onPressed: () { + context.read().add( LoadEducations()); + }) + ]:[], ), //userbloc body: ProgressHUD( diff --git a/lib/screens/profile/components/eligibility/add_modal.dart b/lib/screens/profile/components/eligibility/add_modal.dart index 453bdf3..f5a71a6 100644 --- a/lib/screens/profile/components/eligibility/add_modal.dart +++ b/lib/screens/profile/components/eligibility/add_modal.dart @@ -398,11 +398,11 @@ class _AddEligibilityScreenState extends State { style: mainBtnStyle( primary, Colors.transparent, second), onPressed: () { - //rating + ////rating double? rate = rating == null ? null : double.parse(rating!); - //lisence + ////lisence String? licenseNumber = license; CityMunicipality? cityMunicipality = selectedMunicipality; diff --git a/lib/screens/profile/components/voluntary_works/add_modal.dart b/lib/screens/profile/components/voluntary_works/add_modal.dart new file mode 100644 index 0000000..58fedba --- /dev/null +++ b/lib/screens/profile/components/voluntary_works/add_modal.dart @@ -0,0 +1,685 @@ +import 'package:date_time_picker/date_time_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; +import 'package:searchfield/searchfield.dart'; +import 'package:unit2/bloc/profile/voluntary_works/voluntary_work_bloc.dart'; +import 'package:unit2/utils/global.dart'; + +import '../../../../model/location/barangay.dart'; +import '../../../../model/location/city.dart'; +import '../../../../model/location/country.dart'; +import '../../../../model/location/provinces.dart'; +import '../../../../model/location/region.dart'; +import '../../../../model/profile/voluntary_works.dart'; +import '../../../../model/utils/agency.dart'; +import '../../../../model/utils/category.dart'; +import '../../../../model/utils/position.dart'; +import '../../../../theme-data.dart/box_shadow.dart'; +import '../../../../theme-data.dart/btn-style.dart'; +import '../../../../theme-data.dart/colors.dart'; +import '../../../../theme-data.dart/form-style.dart'; +import '../../../../utils/location_utilities.dart'; +import '../../../../utils/text_container.dart'; +import '../../shared/add_for_empty_search.dart'; + +class AddVoluntaryWorkScreen extends StatefulWidget { + final int profileId; + final String token; + const AddVoluntaryWorkScreen( + {super.key, required this.profileId, required this.token}); + + @override + State createState() => _AddVoluntaryWorkScreenState(); +} + +class _AddVoluntaryWorkScreenState extends State { + final formKey = GlobalKey(); + ////controllers + final addPositionController = TextEditingController(); + final addAgencyController = TextEditingController(); + final toDateController = TextEditingController(); + final fromDateController = TextEditingController(); + ////focus nodes + final positionFocusNode = FocusNode(); + final agencyFocusNode = FocusNode(); + final agencyCategoryFocusNode = FocusNode(); + ////booleans + bool showAgency = false; + bool showIsPrivateRadio = false; + bool currentlyInvolved = false; + bool overseas = false; + bool provinceCall = false; + bool cityCall = false; + bool isPrivate = false; + ////Lists + List? provinces; + List? citymuns; + ////Selected + Position? selectedPosition; + Agency? selectedAgency; + Category? selectedCategoty; + Region? selectedRegion; + Province? selectedProvince; + CityMunicipality? selectedMunicipality; + Country? selectedCountry; + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is AddVoluntaryWorkState) { + return Center( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 18), + child: FormBuilder( + key: formKey, + child: ListView( + children: [ + ////POSITIONS + StatefulBuilder(builder: (context, setState) { + return SearchField( + itemHeight: 50, + suggestionsDecoration: box1(), + suggestions: state.positions + .map((Position position) => SearchFieldListItem( + position.title!, + item: position, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10), + child: ListTile( + title: Text(position.title!), + )))) + .toList(), + focusNode: positionFocusNode, + searchInputDecoration: + normalTextFieldStyle("Position *", "").copyWith( + suffixIcon: GestureDetector( + child: const Icon(Icons.arrow_drop_down), + onTap: () => positionFocusNode.unfocus(), + )), + onSuggestionTap: (position) { + setState(() { + selectedPosition = position.item; + positionFocusNode.unfocus(); + }); + }, + ////EMPTY WIDGET + emptyWidget: EmptyWidget( + title: "Add Position", + controller: addPositionController, + onpressed: () { + setState(() { + Position newAgencyPosition = Position( + id: null, + title: addPositionController.text + .toUpperCase()); + + state.positions.insert(0, newAgencyPosition); + + addPositionController.text = ""; + Navigator.pop(context); + }); + }), + validator: (position) { + if (position!.isEmpty) { + return "This field is required"; + } + return null; + }, + ); + }), + const SizedBox( + height: 12, + ), + ////AGENCY + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + SearchField( + itemHeight: 70, + focusNode: agencyFocusNode, + suggestions: state.agencies + .map((Agency agency) => + SearchFieldListItem(agency.name!, + item: agency, + child: ListTile( + title: Text( + agency.name!, + overflow: TextOverflow.ellipsis, + ), + subtitle: Text( + agency.privateEntity == true + ? "Private" + : agency.privateEntity == + false + ? "Government" + : ""), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle("Agency *", "") + .copyWith( + suffixIcon: GestureDetector( + child: const Icon( + Icons.arrow_drop_down, + ), + onTap: () => agencyFocusNode.unfocus(), + )), + ////SELETECTED + onSuggestionTap: (agency) { + setState(() { + selectedAgency = agency.item; + if (selectedAgency!.privateEntity == null) { + showAgency = true; + showIsPrivateRadio = true; + } else { + showAgency = false; + showIsPrivateRadio = false; + } + agencyFocusNode.unfocus(); + }); + }, + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + emptyWidget: EmptyWidget( + controller: addAgencyController, + onpressed: () { + setState(() { + Agency newAgency = Agency( + id: null, + name: addAgencyController.text + .toUpperCase(), + category: null, + privateEntity: null); + state.agencies.insert(0, newAgency); + + addAgencyController.text = ""; + Navigator.pop(context); + }); + }, + title: "Add Agency")), + + SizedBox( + height: showAgency ? 12 : 0, + ), + ////SHOW CATEGORY AGENCY + SizedBox( + child: showAgency + ? SearchField( + focusNode: agencyCategoryFocusNode, + itemHeight: 70, + suggestions: state.agencyCategory + .map((Category category) => + SearchFieldListItem( + category.name!, + item: category, + child: ListTile( + title: Text(category.name!), + subtitle: Text(category + .industryClass!.name!), + ))) + .toList(), + emptyWidget: Container( + height: 100, + decoration: box1(), + child: const Center( + child: Text("No result found ...")), + ), + onSuggestionTap: (agencyCategory) { + setState(() { + selectedCategoty = + agencyCategory.item; + agencyCategoryFocusNode.unfocus(); + }); + }, + searchInputDecoration: + normalTextFieldStyle("Category *", "") + .copyWith( + suffixIcon: const Icon( + Icons.arrow_drop_down)), + validator: (value) { + if (value!.isEmpty) { + return "This field is required"; + } + return null; + }, + ) + : const SizedBox(), + ), + SizedBox( + height: showIsPrivateRadio ? 12 : 0, + ), + ////PRVIATE SECTOR + SizedBox( + child: showIsPrivateRadio + ? FormBuilderSwitch( + initialValue: false, + title: Text(isPrivate ? "YES" : "NO"), + decoration: normalTextFieldStyle( + "Private Entity?", + 'Private Entity?'), + + ////onvhange private sector + onChanged: (value) { + setState(() { + isPrivate = value!; + agencyCategoryFocusNode.unfocus(); + }); + }, + + name: 'isPrivate', + validator: + FormBuilderValidators.required(), + ) + : const SizedBox()), + const SizedBox( + height: 12, + ), + //// total hours + FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This Field is required"), + name: "total_hours", + keyboardType: TextInputType.number, + decoration: + normalTextFieldStyle("Total Hours*", "0"), + ), + const SizedBox( + height: 12, + ), + ////Currently Involved + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: currentlyInvolved, + activeColor: second, + onChanged: (value) { + setState(() { + currentlyInvolved = value!; + }); + }, + decoration: normalTextFieldStyle( + "Currently Involved?", 'Graduated?'), + name: 'currently_involved', + title: + Text(currentlyInvolved ? "YES" : "NO"), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + //// FROM DATE + Flexible( + flex: 1, + child: DateTimePicker( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + use24HourFormat: false, + icon: + const Icon(Icons.date_range), + controller: fromDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + timeHintText: + "Date of Examination/Conferment", + decoration: normalTextFieldStyle( + "From *", "From *") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + initialValue: null, + )), + const SizedBox( + width: 12, + ), + //// TO DATE + Flexible( + flex: 1, + child: currentlyInvolved + ? TextFormField( + enabled: false, + initialValue: "PRESENT", + style: const TextStyle( + color: Colors.black45), + decoration: + normalTextFieldStyle( + "", "") + .copyWith(), + ) + : DateTimePicker( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + controller: toDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + decoration: normalTextFieldStyle( + "To *", "To *") + .copyWith( + prefixIcon: + const Icon( + Icons.date_range, + color: + Colors.black87, + ), + prefixText: + currentlyInvolved + ? "PRESENT" + : ""), + initialValue: null, + ), + ), + ], + ), + ), + ], + ); + }), + ], + ); + }), + const SizedBox( + height: 12, + ), + //// OVERSEAS + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: + normalTextFieldStyle("Overseas Address?", ''), + name: 'overseas', + title: Text(overseas ? "YES" : "NO"), + ), + SizedBox( + height: overseas == true ? 8 : 0, + ), + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + FormBuilderDropdown( + autovalidateMode: AutovalidateMode + .onUserInteraction, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + onChanged: (Region? region) async { + if (selectedRegion != region) { + setState(() { + provinceCall = true; + }); + selectedRegion = region; + getProvinces(); + } + }, + initialValue: null, + decoration: normalTextFieldStyle( + "Region*", "Region"), + name: 'region', + items: state.regions + .map>( + (Region region) { + return DropdownMenuItem( + value: region, + child: + Text(region.description!)); + }).toList(), + ), + const SizedBox( + height: 8, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + value: selectedProvince, + onChanged: + (Province? province) { + if (selectedProvince != + province) { + setState(() { + cityCall = true; + }); + selectedProvince = province; + getCities(); + } + }, + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province province) { + return DropdownMenuItem( + value: province, + child: FittedBox( + child: Text(province + .description!), + )); + }).toList(), + decoration: + normalTextFieldStyle( + "Province*", + "Province")), + ), + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: DropdownButtonFormField< + CityMunicipality>( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + isExpanded: true, + onChanged: + (CityMunicipality? city) { + if (selectedMunicipality != + city) { + selectedMunicipality = city; + } + }, + decoration: normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality c) { + return DropdownMenuItem( + value: c, + child: Text( + c.description!)); + }).toList(), + ), + ), + ), + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( + height: 60, + child: FormBuilderDropdown( + initialValue: null, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + items: state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox( + child: Text(country.name!))); + }).toList(), + name: 'country', + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) { + selectedCountry = value; + }, + ), + ), + ), + ], + ); + }), + Expanded( + child: SizedBox( + height: blockSizeVertical * 8, + )), + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState!.saveAndValidate()) { + Country country = selectedCountry ??= + Country( + id: 175, + name: 'Philippines', + code: 'PH'); + Address address = Address( + barangay: null, + id: null, + addressCategory: null, + addressClass: null, + cityMunicipality: selectedMunicipality, + country: country); + if (selectedCategoty != null) { + selectedAgency = Agency( + id: selectedAgency?.id, + name: selectedAgency!.name, + category: selectedCategoty, + privateEntity: isPrivate); + } + VoluntaryWork work = VoluntaryWork( + ////address + address: address, + //// agency + agency: selectedAgency, + //// + position: selectedPosition, + ////total hours + totalHours: double.parse(formKey + .currentState!.value["total_hours"]), + ////to date + toDate: toDateController.text.isEmpty || + toDateController.text.toUpperCase() == + "PRESENT" + ? null + : DateTime.parse(toDateController.text), + ////from date + fromDate: + DateTime.parse(fromDateController.text), + ); + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add( + AddVoluntaryWork( + profileId: widget.profileId, + token: widget.token, + work: work)); + } + }, + child: const Text(submit)), + ) + ], + )), + ), + ); + } + return const Placeholder(); + }, + ); + } + + Future getProvinces() async { + try { + List newProvinces = await LocationUtils.instance + .getProvinces(regionCode: selectedRegion!.code.toString()); + setState(() { + provinces = newProvinces; + selectedProvince = provinces![0]; + provinceCall = false; + cityCall = true; + getCities(); + }); + } catch (e) { + context + .read() + .add(ShowErrorState(message: e.toString())); + } + } + + Future getCities() async { + try { + List newCities = await LocationUtils.instance + .getCities(code: selectedProvince!.code.toString()); + citymuns = newCities; + setState(() { + selectedMunicipality = newCities[0]; + cityCall = false; + }); + } catch (e) { + context + .read() + .add(ShowErrorState(message: e.toString())); + } + } +} diff --git a/lib/screens/profile/components/voluntary_works/edit_modal.dart b/lib/screens/profile/components/voluntary_works/edit_modal.dart new file mode 100644 index 0000000..3ad39ce --- /dev/null +++ b/lib/screens/profile/components/voluntary_works/edit_modal.dart @@ -0,0 +1,754 @@ +import 'package:date_time_picker/date_time_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; +import 'package:searchfield/searchfield.dart'; +import 'package:unit2/bloc/profile/voluntary_works/voluntary_work_bloc.dart'; +import 'package:unit2/utils/global.dart'; +import 'package:unit2/utils/global_context.dart'; + +import '../../../../model/location/barangay.dart'; +import '../../../../model/location/city.dart'; +import '../../../../model/location/country.dart'; +import '../../../../model/location/provinces.dart'; +import '../../../../model/location/region.dart'; +import '../../../../model/profile/voluntary_works.dart'; +import '../../../../model/utils/agency.dart'; +import '../../../../model/utils/category.dart'; +import '../../../../model/utils/position.dart'; +import '../../../../theme-data.dart/box_shadow.dart'; +import '../../../../theme-data.dart/btn-style.dart'; +import '../../../../theme-data.dart/colors.dart'; +import '../../../../theme-data.dart/form-style.dart'; +import '../../../../utils/location_utilities.dart'; +import '../../../../utils/text_container.dart'; +import '../../shared/add_for_empty_search.dart'; + +class EditVoluntaryWorkScreen extends StatefulWidget { + final int profileId; + final String token; + const EditVoluntaryWorkScreen( + {super.key, required this.profileId, required this.token}); + + @override + State createState() => + _EditVoluntaryWorkScreenState(); +} + +class _EditVoluntaryWorkScreenState extends State { + final formKey = GlobalKey(); + ////controllers + final addPositionController = TextEditingController(); + final addAgencyController = TextEditingController(); + final toDateController = TextEditingController(); + final fromDateController = TextEditingController(); + final positionController = TextEditingController(); + final agencyController = TextEditingController(); + ////focus nodes + final positionFocusNode = FocusNode(); + final agencyFocusNode = FocusNode(); + final agencyCategoryFocusNode = FocusNode(); + ////booleans + bool showAgency = false; + bool showIsPrivateRadio = false; + bool currentlyInvolved = false; + bool overseas = false; + bool provinceCall = false; + bool cityCall = false; + bool isPrivate = false; + ////Lists + List? provinces; + List? citymuns; + + ////Selected + Position? selectedPosition; + Agency? selectedAgency; + Category? selectedCategoty; + Region? selectedRegion; + Province? selectedProvince; + CityMunicipality? selectedMunicipality; + Country? selectedCountry; + @override + Widget build(BuildContext context) { + return BlocBuilder( + buildWhen: (previous, current) { + return false; + }, + builder: (context, state) { + if (state is EditVoluntaryWorks) { + ////config + String? todate = state.work.toDate?.toString(); + provinces = state.provinces; + citymuns = state.cities; + positionController.text = state.currentPosition.title!; + agencyController.text = state.currentAgency.name!; + fromDateController.text = state.work.fromDate.toString(); + toDateController.text = todate ??= ""; + currentlyInvolved = todate.isEmpty || todate == "null" ? true : false; + overseas = state.overseas; + selectedCountry = state.currentCountry; + selectedPosition = state.currentPosition; + selectedAgency = state.currentAgency; + selectedRegion = state.currentRegion; + selectedProvince = state.currentProvince; + selectedMunicipality = state.currentCity; + return Center( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 18), + child: FormBuilder( + key: formKey, + child: ListView( + children: [ + ////POSITIONS + StatefulBuilder(builder: (context, setState) { + return SearchField( + controller: positionController, + itemHeight: 50, + suggestionsDecoration: box1(), + suggestions: state.positions + .map((Position position) => SearchFieldListItem( + position.title!, + item: position, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10), + child: ListTile( + title: Text(position.title!), + )))) + .toList(), + focusNode: positionFocusNode, + searchInputDecoration: + normalTextFieldStyle("Position *", "").copyWith( + suffixIcon: GestureDetector( + child: const Icon(Icons.arrow_drop_down), + onTap: () => positionFocusNode.unfocus(), + )), + onSuggestionTap: (position) { + setState(() { + selectedPosition = position.item; + positionFocusNode.unfocus(); + }); + }, + ////EMPTY WIDGET + emptyWidget: EmptyWidget( + title: "Add Position", + controller: addPositionController, + onpressed: () { + setState(() { + Position newAgencyPosition = Position( + id: null, + title: addPositionController.text + .toUpperCase()); + + state.positions.insert(0, newAgencyPosition); + + addPositionController.text = ""; + Navigator.pop(context); + }); + }), + validator: (position) { + if (position!.isEmpty) { + return "This field is required"; + } + return null; + }, + ); + }), + const SizedBox( + height: 12, + ), + ////AGENCY + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + SearchField( + controller: agencyController, + itemHeight: 70, + focusNode: agencyFocusNode, + suggestions: state.agencies + .map((Agency agency) => + SearchFieldListItem(agency.name!, + item: agency, + child: ListTile( + title: Text( + agency.name!, + overflow: TextOverflow.ellipsis, + ), + subtitle: Text( + agency.privateEntity == true + ? "Private" + : agency.privateEntity == + false + ? "Government" + : ""), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle("Agency *", "") + .copyWith( + suffixIcon: GestureDetector( + child: const Icon( + Icons.arrow_drop_down, + ), + onTap: () => agencyFocusNode.unfocus(), + )), + ////SELETECTED + onSuggestionTap: (agency) { + setState(() { + selectedAgency = agency.item; + if (selectedAgency!.privateEntity == null) { + showAgency = true; + showIsPrivateRadio = true; + } else { + showAgency = false; + showIsPrivateRadio = false; + } + agencyFocusNode.unfocus(); + }); + }, + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + emptyWidget: EmptyWidget( + controller: addAgencyController, + onpressed: () { + setState(() { + Agency newAgency = Agency( + id: null, + name: addAgencyController.text + .toUpperCase(), + category: null, + privateEntity: null); + state.agencies.insert(0, newAgency); + + addAgencyController.text = ""; + Navigator.pop(context); + }); + }, + title: "Add Agency")), + + SizedBox( + height: showAgency ? 12 : 0, + ), + ////SHOW CATEGORY AGENCY + SizedBox( + child: showAgency + ? SearchField( + focusNode: agencyCategoryFocusNode, + itemHeight: 70, + suggestions: state.agencyCategory + .map((Category category) => + SearchFieldListItem( + category.name!, + item: category, + child: ListTile( + title: Text(category.name!), + subtitle: Text(category + .industryClass!.name!), + ))) + .toList(), + emptyWidget: Container( + height: 100, + decoration: box1(), + child: const Center( + child: Text("No result found ...")), + ), + onSuggestionTap: (agencyCategory) { + setState(() { + selectedCategoty = + agencyCategory.item; + agencyCategoryFocusNode.unfocus(); + }); + }, + searchInputDecoration: + normalTextFieldStyle("Category *", "") + .copyWith( + suffixIcon: const Icon( + Icons.arrow_drop_down)), + validator: (value) { + if (value!.isEmpty) { + return "This field is required"; + } + return null; + }, + ) + : const SizedBox(), + ), + SizedBox( + height: showIsPrivateRadio ? 12 : 0, + ), + ////PRVIATE SECTOR + SizedBox( + child: showIsPrivateRadio + ? FormBuilderSwitch( + initialValue: false, + title: Text(isPrivate ? "YES" : "NO"), + decoration: normalTextFieldStyle( + "Private Entity?", + 'Private Entity?'), + + ////onvhange private sector + onChanged: (value) { + setState(() { + isPrivate = value!; + agencyCategoryFocusNode.unfocus(); + }); + }, + + name: 'isPrivate', + validator: + FormBuilderValidators.required(), + ) + : const SizedBox()), + const SizedBox( + height: 12, + ), + //// total hours + FormBuilderTextField( + initialValue: state.work.totalHours.toString(), + validator: FormBuilderValidators.required( + errorText: "This Field is required"), + name: "total_hours", + keyboardType: TextInputType.number, + decoration: + normalTextFieldStyle("Total Hours*", "0"), + ), + const SizedBox( + height: 12, + ), + ////Currently Involved + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: currentlyInvolved, + activeColor: second, + onChanged: (value) { + setState(() { + currentlyInvolved = value!; + }); + }, + decoration: normalTextFieldStyle( + "Currently Involved?", 'Graduated?'), + name: 'currently_involved', + title: + Text(currentlyInvolved ? "YES" : "NO"), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + //// FROM DATE + Flexible( + flex: 1, + child: DateTimePicker( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + use24HourFormat: false, + icon: + const Icon(Icons.date_range), + controller: fromDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + timeHintText: + "Date of Examination/Conferment", + decoration: normalTextFieldStyle( + "From *", "From *") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + initialValue: null, + )), + const SizedBox( + width: 12, + ), + //// TO DATE + Flexible( + flex: 1, + child: currentlyInvolved + ? TextFormField( + enabled: false, + initialValue: "PRESENT", + style: const TextStyle( + color: Colors.black45), + decoration: + normalTextFieldStyle( + "", "") + .copyWith(), + ) + : DateTimePicker( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + controller: toDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + decoration: normalTextFieldStyle( + "To *", "To *") + .copyWith( + prefixIcon: + const Icon( + Icons.date_range, + color: + Colors.black87, + ), + prefixText: + currentlyInvolved + ? "PRESENT" + : ""), + initialValue: null, + ), + ), + ], + ), + ), + ], + ); + }), + ], + ); + }), + const SizedBox( + height: 12, + ), + //// OVERSEAS + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: + normalTextFieldStyle("Overseas Address?", ''), + name: 'overseas', + title: Text(overseas ? "YES" : "NO"), + ), + SizedBox( + height: overseas == true ? 8 : 0, + ), + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + DropdownButtonFormField( + isExpanded: true, + autovalidateMode: AutovalidateMode + .onUserInteraction, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + onChanged: (Region? region) async { + if (selectedRegion != region) { + setState(() { + provinceCall = true; + }); + selectedRegion = region; + try { + provinces = await LocationUtils + .instance + .getProvinces( + regionCode: + selectedRegion!.code + .toString()); + selectedProvince = + provinces![0]; + setState(() { + provinceCall = false; + cityCall = true; + }); + try { + citymuns = await LocationUtils + .instance + .getCities( + code: + selectedProvince! + .code + .toString()); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + }); + } catch (e) { + NavigationService.navigatorKey + .currentContext + ?.read< + VoluntaryWorkBloc>() + .add(ShowErrorState( + message: + e.toString())); + } + } catch (e) { + context + .read() + .add(ShowErrorState( + message: e.toString())); + } + } + }, + value: selectedRegion, + decoration: normalTextFieldStyle( + "Region*", "Region"), + items: state.regions + .map>( + (Region region) { + return DropdownMenuItem( + value: region, + child: + Text(region.description!)); + }).toList(), + ), + const SizedBox( + height: 8, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + value: selectedProvince, + onChanged: + (Province? province) async { + if (selectedProvince != + province) { + setState(() { + cityCall = true; + }); + selectedProvince = province; + try { + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code + .toString()); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + }); + } catch (e) { + context + .read< + VoluntaryWorkBloc>() + .add(ShowErrorState( + message: e + .toString())); + } + } + }, + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province province) { + return DropdownMenuItem( + value: province, + child: FittedBox( + child: Text(province + .description!), + )); + }).toList(), + decoration: + normalTextFieldStyle( + "Province*", + "Province")), + ), + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: DropdownButtonFormField< + CityMunicipality>( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + isExpanded: true, + onChanged: + (CityMunicipality? city) { + if (selectedMunicipality != + city) { + selectedMunicipality = city; + } + }, + decoration: normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality c) { + return DropdownMenuItem( + value: c, + child: Text( + c.description!)); + }).toList(), + ), + ), + ), + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( + height: 60, + child: DropdownButtonFormField( + isExpanded: true, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + items: state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox( + child: Text(country.name!))); + }).toList(), + value: selectedCountry, + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) { + selectedCountry = value; + }, + ), + ), + ), + ], + ); + }), + Expanded( + child: SizedBox( + height: blockSizeVertical * 8, + )), + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState!.saveAndValidate()) { + Address address; + //// if not overseas + if (!overseas) { + address = Address( + barangay: null, + id: state.work.address?.id, + addressCategory: + state.work.address?.addressCategory, + addressClass: + state.work.address?.addressClass , + cityMunicipality: selectedMunicipality, + country: selectedCountry); + } else { + ////if overseas + address = Address( + barangay: null, + id: state.work.address?.id, + addressCategory: + state.work.address?.addressCategory, + addressClass: + state.work.address?.addressClass, + cityMunicipality: null, + country: selectedCountry); + } + + if (selectedCategoty != null) { + selectedAgency = Agency( + id: selectedAgency?.id, + name: selectedAgency!.name, + category: selectedCategoty, + privateEntity: isPrivate); + } + VoluntaryWork work = VoluntaryWork( + ////address + address: address, + //// agency + agency: selectedAgency, + //// + position: selectedPosition, + ////total hours + totalHours: double.parse(formKey + .currentState!.value["total_hours"]), + ////to date + toDate: toDateController.text.isEmpty || + toDateController.text.toUpperCase() == + "PRESENT" + ? null + : DateTime.parse(toDateController.text), + ////from date + fromDate: + DateTime.parse(fromDateController.text), + ); + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add( + UpdateVolunataryWork( + oldAgencyId: state.work.agency!.id!, + oldFromDate: + state.work.fromDate.toString(), + oldPosId: state.work.position!.id!, + profileId: widget.profileId, + token: widget.token, + work: work)); + } + }, + child: const Text(submit)), + ) + ], + )), + ), + ); + } + return const Placeholder(); + }, + ); + } +} diff --git a/lib/screens/profile/components/voluntary_works_screen.dart b/lib/screens/profile/components/voluntary_works_screen.dart index fbe0da8..20becc4 100644 --- a/lib/screens/profile/components/voluntary_works_screen.dart +++ b/lib/screens/profile/components/voluntary_works_screen.dart @@ -1,34 +1,41 @@ +import 'package:app_popup_menu/app_popup_menu.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; import 'package:intl/intl.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; -import 'package:unit2/screens/profile/components/work_history/voluntary_works/add_modal.dart'; +import 'package:unit2/screens/profile/components/voluntary_works/add_modal.dart'; +import 'package:unit2/screens/profile/components/voluntary_works/edit_modal.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; +import 'package:unit2/widgets/error_state.dart'; import '../../../bloc/profile/voluntary_works/voluntary_work_bloc.dart'; +import '../../../utils/alerts.dart'; class VolunataryWorkScreen extends StatelessWidget { const VolunataryWorkScreen({super.key}); @override Widget build(BuildContext context) { - String? token; + String? token; int? profileId; DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); return Scaffold( appBar: AppBar( title: const Text(voluntaryScreenTitle), backgroundColor: primary, - actions: [AddLeading(onPressed: () { - context.read().add(ShowAddVoluntaryWorks()); - })], + actions: [ + AddLeading(onPressed: () { + context.read().add(ShowAddVoluntaryWorks()); + }) + ], ), body: ProgressHUD( padding: const EdgeInsets.all(24), @@ -37,9 +44,8 @@ class VolunataryWorkScreen extends StatelessWidget { child: BlocBuilder( builder: (context, state) { if (state is UserLoggedIn) { - token = state.userData!.user!.login!.token; - profileId = - state.userData!.user!.login!.user!.profileId; + token = state.userData!.user!.login!.token; + profileId = state.userData!.user!.login!.user!.profileId; return BlocBuilder( builder: (context, state) { if (state is ProfileLoaded) { @@ -51,10 +57,78 @@ class VolunataryWorkScreen extends StatelessWidget { progress!.showWithText("Please wait..."); } if (state is VoluntaryWorkLoadedState || - state is VoluntaryWorkErrorState|| state is AddVoluntaryWorkState) { + state is VoluntaryWorkErrorState || + state is AddVoluntaryWorkState || + state is VoluntaryWorkAddedState || + state is VoluntaryWorkDeletedState || + state is EditVoluntaryWorks) { final progress = ProgressHUD.of(context); progress!.dismiss(); } + //// Added State + if (state is VoluntaryWorkAddedState) { + if (state.response['success']) { + successAlert(context, "Adding Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context + .read() + .add(LoadVoluntaryWorks()); + }); + } else { + errorAlert(context, "Adding Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context + .read() + .add(LoadVoluntaryWorks()); + }); + } + } + //// Updated State + + if (state is VoluntaryWorkEditedState) { + if (state.response['success']) { + successAlert(context, "Updated Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context + .read() + .add(LoadVoluntaryWorks()); + }); + } else { + errorAlert(context, "Update Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context + .read() + .add(LoadVoluntaryWorks()); + }); + } + } + ////Deleted State + if (state is VoluntaryWorkDeletedState) { + if (state.success) { + successAlert(context, "Deletion Successfull!", + "Deleted Successfully", () { + Navigator.of(context).pop(); + context + .read() + .add(LoadVoluntaryWorks()); + }); + } else { + errorAlert(context, "Deletion Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context + .read() + .add(LoadVoluntaryWorks()); + }); + } + } }, builder: (context, state) { if (state is VoluntaryWorkLoadedState) { @@ -128,10 +202,84 @@ class VolunataryWorkScreen extends StatelessWidget { "$numberOfHours : $hours hours"), ]), ), - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.more_vert)) + AppPopupMenu( + offset: const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + ////delete eligibilty-= = = = = = = = =>> + if (value == 2) { + confirmAlert(context, () { + final progress = + ProgressHUD.of( + context); + progress!.showWithText( + "Loading..."); + BlocProvider.of< + VoluntaryWorkBloc>( + context) + .add(DeleteVoluntaryWork( + work: state + .voluntaryWorks[ + index], + profileId: + profileId!, + token: token!)); + }, "Delete?", + "Confirm Delete?"); + } + if (value == 1) { + bool isOverseas; + ////edit voluntary work-= = = = = = = = =>> + final progress = + ProgressHUD.of(context); + progress!.showWithText( + "Loading..."); + + if (state + .voluntaryWorks[ + index] + .address?.cityMunicipality == null + ) { + isOverseas = true; + }else{ + isOverseas = false; + } + + + context + .read< + VoluntaryWorkBloc>() + .add(ShowEditVoluntaryWorks( + profileId: + profileId!, + token: token!, + work: state + .voluntaryWorks[ + index], + isOverseas: + isOverseas)); + } + }, + menuItems: [ + popMenuItem( + text: "Edit", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Delete", + value: 2, + icon: Icons.delete), + popMenuItem( + text: "Attachment", + value: 3, + icon: FontAwesome.attach) + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ) ], ), ), @@ -146,8 +294,22 @@ class VolunataryWorkScreen extends StatelessWidget { message: "You don't have any Voluntary Works added. Please click + to add."); } - }if(state is AddVoluntaryWorkState){ - return AddVoluntaryWorkScreen(profileId: profileId!,token: token!,); + } + if (state is AddVoluntaryWorkState) { + return AddVoluntaryWorkScreen( + profileId: profileId!, + token: token!, + ); + } + if (state is VoluntaryWorkErrorState) { + return SomethingWentWrong( + message: state.toString(), onpressed: () {}); + } + if (state is EditVoluntaryWorks) { + return EditVoluntaryWorkScreen( + profileId: profileId!, + token: token!, + ); } return Container(); }, @@ -162,4 +324,23 @@ class VolunataryWorkScreen extends StatelessWidget { )), ); } + + PopupMenuItem popMenuItem({String? text, int? value, IconData? icon}) { + return PopupMenuItem( + value: value, + child: Row( + children: [ + Icon( + icon, + ), + const SizedBox( + width: 10, + ), + Text( + text!, + ), + ], + ), + ); + } } diff --git a/lib/screens/profile/components/work_history/voluntary_works/add_modal.dart b/lib/screens/profile/components/work_history/voluntary_works/add_modal.dart deleted file mode 100644 index e7cf6cb..0000000 --- a/lib/screens/profile/components/work_history/voluntary_works/add_modal.dart +++ /dev/null @@ -1,597 +0,0 @@ -import 'package:date_time_picker/date_time_picker.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_form_builder/flutter_form_builder.dart'; -import 'package:fluttericon/font_awesome_icons.dart'; -import 'package:form_builder_validators/form_builder_validators.dart'; -import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; -import 'package:searchfield/searchfield.dart'; -import 'package:unit2/bloc/profile/voluntary_works/voluntary_work_bloc.dart'; -import 'package:unit2/utils/global.dart'; - -import '../../../../../model/location/barangay.dart'; -import '../../../../../model/location/city.dart'; -import '../../../../../model/location/country.dart'; -import '../../../../../model/location/provinces.dart'; -import '../../../../../model/location/region.dart'; -import '../../../../../model/utils/agency.dart'; -import '../../../../../model/utils/category.dart'; -import '../../../../../model/utils/position.dart'; -import '../../../../../theme-data.dart/box_shadow.dart'; -import '../../../../../theme-data.dart/colors.dart'; -import '../../../../../theme-data.dart/form-style.dart'; -import '../../../../../utils/location_utilities.dart'; -import '../../../shared/add_for_empty_search.dart'; - -class AddVoluntaryWorkScreen extends StatefulWidget { - final int profileId; - final String token; - const AddVoluntaryWorkScreen( - {super.key, required this.profileId, required this.token}); - - @override - State createState() => _AddVoluntaryWorkScreenState(); -} - -class _AddVoluntaryWorkScreenState extends State { - final formKey = GlobalKey(); - ////controllers - final addPositionController = TextEditingController(); - final addAgencyController = TextEditingController(); - final toDateController = TextEditingController(); - final fromDateController = TextEditingController(); - ////focus nodes - final positionFocusNode = FocusNode(); - final agencyFocusNode = FocusNode(); - final agencyCategoryFocusNode = FocusNode(); - ////booleans - bool showAgency = false; - bool showIsPrivateRadio = false; - bool currentlyInvolved = false; - bool overseas = false; - bool provinceCall = false; - bool cityCall = false; - ////Lists - List? provinces; - List? citymuns; - ////Selected - Region? selectedRegion; - Province? selectedProvince; - CityMunicipality? selectedMunicipality; - @override - Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, state) { - if (state is AddVoluntaryWorkState) { - return SingleChildScrollView( - child: SizedBox( - height: screenHeight * .90, - child: Center( - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 25, horizontal: 18), - child: FormBuilder( - child: Column( - children: [ - ////POSITIONS - StatefulBuilder(builder: (context, setState) { - return SearchField( - itemHeight: 50, - suggestionsDecoration: box1(), - suggestions: state.positions - .map((Position position) => SearchFieldListItem( - position.title!, - item: position, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10), - child: ListTile( - title: Text(position.title!), - )))) - .toList(), - focusNode: positionFocusNode, - searchInputDecoration: - normalTextFieldStyle("Position *", "").copyWith( - suffixIcon: - const Icon(Icons.arrow_drop_down)), - onSuggestionTap: (position) { - setState(() { - positionFocusNode.unfocus(); - }); - }, - ////EMPTY WIDGET - emptyWidget: EmptyWidget( - title: "Add Position", - controller: addPositionController, - onpressed: () { - setState(() { - Navigator.pop(context); - }); - }), - validator: (position) { - if (position!.isEmpty) { - return "This field is required"; - } - return null; - }, - ); - }), - const SizedBox( - height: 12, - ), - ////AGENCY - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - SearchField( - itemHeight: 70, - focusNode: agencyFocusNode, - suggestions: state.agencies - .map((Agency agency) => - SearchFieldListItem(agency.name!, - item: agency, - child: ListTile( - title: Text( - agency.name!, - overflow: - TextOverflow.ellipsis, - ), - subtitle: Text(agency - .privateEntity == - true - ? "Private" - : agency.privateEntity == - false - ? "Government" - : ""), - ))) - .toList(), - searchInputDecoration: - normalTextFieldStyle("Agency *", "") - .copyWith( - suffixIcon: const Icon( - Icons.arrow_drop_down)), - onSuggestionTap: (agency) { - setState(() { - agencyFocusNode.unfocus(); - }); - }, - validator: (agency) { - if (agency!.isEmpty) { - return "This field is required"; - } - return null; - }, - emptyWidget: EmptyWidget( - controller: addAgencyController, - onpressed: () { - setState(() { - Navigator.pop(context); - }); - }, - title: "Add Agency")), - - SizedBox( - height: showAgency ? 12 : 0, - ), - ////SHOW CATEGORY AGENCY - SizedBox( - child: showAgency - ? SearchField( - focusNode: agencyCategoryFocusNode, - itemHeight: 70, - suggestions: state.agencyCategory - .map((Category category) => - SearchFieldListItem( - category.name!, - item: category, - child: ListTile( - title: - Text(category.name!), - subtitle: Text(category - .industryClass! - .name!), - ))) - .toList(), - emptyWidget: Container( - height: 100, - decoration: box1(), - child: const Center( - child: - Text("No result found ...")), - ), - onSuggestionTap: (agencyCategory) { - setState(() {}); - }, - searchInputDecoration: - normalTextFieldStyle( - "Category *", "") - .copyWith( - suffixIcon: const Icon( - Icons.arrow_drop_down)), - validator: (value) { - if (value!.isEmpty) { - return "This field is required"; - } - return null; - }, - ) - : const SizedBox(), - ), - - ////PRVIATE SECTOR - SizedBox( - child: showIsPrivateRadio - ? FormBuilderRadioGroup( - decoration: InputDecoration( - border: InputBorder.none, - label: Row( - children: [ - Text( - "Is this private sector? ", - style: Theme.of(context) - .textTheme - .headlineSmall! - .copyWith(fontSize: 24), - ), - const Icon( - FontAwesome.help_circled) - ], - ), - ), - - ////onvhange private sector - onChanged: (value) { - setState(() { - agencyCategoryFocusNode.unfocus(); - }); - }, - - name: 'isPrivate', - validator: - FormBuilderValidators.required(), - options: ["YES", "NO"] - .map((lang) => - FormBuilderFieldOption( - value: lang)) - .toList(growable: false), - ) - : const SizedBox()), - const SizedBox( - height: 12, - ), - FormBuilderTextField( - name: "total_hours", - keyboardType: TextInputType.number, - decoration: - normalTextFieldStyle("Total Hours*", "0"), - ), - const SizedBox( - height: 12, - ), - ////Currently Involved - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - FormBuilderSwitch( - initialValue: currentlyInvolved, - activeColor: second, - onChanged: (value) { - setState(() { - currentlyInvolved = value!; - }); - }, - decoration: normalTextFieldStyle( - "Currently Involved?", 'Graduated?'), - name: 'currently_involved', - title: Text( - currentlyInvolved ? "YES" : "NO"), - ), - const SizedBox( - height: 12, - ), - SizedBox( - width: screenWidth, - child: Row( - children: [ - //// FROM DATE - Flexible( - flex: 1, - child: DateTimePicker( - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - use24HourFormat: false, - icon: const Icon( - Icons.date_range), - controller: fromDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), - timeHintText: - "Date of Examination/Conferment", - decoration: - normalTextFieldStyle( - "From *", "From *") - .copyWith( - prefixIcon: - const Icon( - Icons.date_range, - color: Colors.black87, - )), - initialValue: null, - )), - const SizedBox( - width: 12, - ), - //// TO DATE - Flexible( - flex: 1, - child: currentlyInvolved - ? TextFormField( - enabled: false, - initialValue: "PRESENT", - style: const TextStyle( - color: Colors.black45), - decoration: - normalTextFieldStyle( - "", "") - .copyWith(), - ) - : DateTimePicker( - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - controller: - toDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), - decoration: normalTextFieldStyle( - "To *", "To *") - .copyWith( - prefixIcon: - const Icon( - Icons.date_range, - color: Colors - .black87, - ), - prefixText: - currentlyInvolved - ? "PRESENT" - : ""), - initialValue: null, - ), - ), - ], - ), - ), - ], - ); - }), - ], - ); - }), - const SizedBox(height: 12,), - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - setState(() { - overseas = value!; - }); - }, - decoration: normalTextFieldStyle( - "Overseas Address?", ''), - name: 'overseas', - title: Text(overseas ? "YES" : "NO"), - ), - SizedBox( - height: overseas == true ? 8 : 0, - ), - SizedBox( - child: overseas == false - ? Column( - children: [ - const SizedBox( - height: 12, - ), - ////REGION DROPDOWN - FormBuilderDropdown( - autovalidateMode: AutovalidateMode - .onUserInteraction, - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - onChanged: (Region? region) async { - if (selectedRegion != region) { - setState(() { - provinceCall = true; - }); - selectedRegion = region; - getProvinces(); - } - }, - initialValue: null, - decoration: normalTextFieldStyle( - "Region*", "Region"), - name: 'region', - items: state.regions - .map>( - (Region region) { - return DropdownMenuItem( - value: region, - child: Text( - region.description!)); - }).toList(), - ), - const SizedBox( - height: 8, - ), - //// PROVINCE DROPDOWN - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: provinceCall, - child: DropdownButtonFormField< - Province?>( - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - value: selectedProvince, - onChanged: - (Province? province) { - if (selectedProvince != - province) { - setState(() { - cityCall = true; - }); - selectedProvince = - province; - getCities(); - } - }, - items: provinces == null - ? [] - : provinces!.map< - DropdownMenuItem< - Province>>( - (Province province) { - return DropdownMenuItem( - value: province, - child: FittedBox( - child: Text(province - .description!), - )); - }).toList(), - decoration: - normalTextFieldStyle( - "Province*", - "Province")), - ), - ), - ////CITY MUNICIPALITY - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: cityCall, - child: DropdownButtonFormField< - CityMunicipality>( - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - isExpanded: true, - onChanged: - (CityMunicipality? city) { - if (selectedMunicipality != - city) { - - selectedMunicipality = city; - - } - }, - decoration: - normalTextFieldStyle( - "Municipality*", - "Municipality"), - value: selectedMunicipality, - items: citymuns == null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality c) { - return DropdownMenuItem( - value: c, - child: Text(c - .description!)); - }).toList(), - ), - ), - ), - - ], - ) - //// COUNTRY DROPDOWN - : SizedBox( - height: 60, - child: FormBuilderDropdown( - initialValue: null, - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - items: state.countries - .map>( - (Country country) { - return DropdownMenuItem( - value: country, - child: FittedBox( - child: - Text(country.name!))); - }).toList(), - name: 'country', - decoration: normalTextFieldStyle( - "Country*", "Country"), - onChanged: (Country? value) {}, - ), - ), - ), - - ], - ); - }), - ], - )), - ), - )), - ); - } - return const Placeholder(); - }, - ); - } - Future getProvinces() async { - try { - List newProvinces = await LocationUtils.instance - .getProvinces(regionCode: selectedRegion!.code.toString()); - setState(() { - provinces = newProvinces; - selectedProvince = provinces![0]; - provinceCall = false; - cityCall = true; - getCities(); - }); - } catch (e) { - context.read().add(ShowErrorState(message: e.toString())); - } - } - - Future getCities() async { - try { - List newCities = await LocationUtils.instance - .getCities(code: selectedProvince!.code.toString()); - citymuns = newCities; - setState(() { - selectedMunicipality = newCities[0]; - cityCall = false; - }); - } catch (e) { - context.read().add(ShowErrorState(message: e.toString())); - } - } -} diff --git a/lib/sevices/profile/volunatary_services.dart b/lib/sevices/profile/volunatary_services.dart index 41d4974..0defef6 100644 --- a/lib/sevices/profile/volunatary_services.dart +++ b/lib/sevices/profile/volunatary_services.dart @@ -1,4 +1,3 @@ - import 'dart:convert'; import 'package:http/http.dart' as http; @@ -6,34 +5,156 @@ import 'package:unit2/utils/request.dart'; import '../../model/profile/voluntary_works.dart'; import '../../utils/urls.dart'; -class VoluntaryService{ +class VoluntaryService { static final VoluntaryService _instance = VoluntaryService(); static VoluntaryService get instance => _instance; - Future< List> getVoluntaryWorks(int profileId, String token)async{ + Future> getVoluntaryWorks( + int profileId, String token) async { List voluntaryWorks = []; - String authToken = "Token $token"; - String path = "${Url.instance.getVoluntaryWorks()}$profileId/"; + String authToken = "Token $token"; + String path = "${Url.instance.getVoluntaryWorks()}$profileId/"; Map headers = { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': authToken }; - try{ - http.Response response = await Request.instance.getRequest(path: path,param: {},headers: headers); - if(response.statusCode == 200){ + // try { + http.Response response = await Request.instance + .getRequest(path: path, param: {}, headers: headers); + if (response.statusCode == 200) { Map data = jsonDecode(response.body); - if(data['data'] != null){ - data['data'].forEach((var work){ - VoluntaryWork voluntaryWork = VoluntaryWork.fromJson(work); - voluntaryWorks.add(voluntaryWork); + if (data['data'] != null) { + data['data'].forEach((var work) { + VoluntaryWork voluntaryWork = VoluntaryWork.fromJson(work); + voluntaryWorks.add(voluntaryWork); }); } } - }catch(e){ - throw(e.toString()); - } + // } catch (e) { + // throw (e.toString()); + // } -return voluntaryWorks; - } -} \ No newline at end of file + return voluntaryWorks; + } + + Future> add( + {required VoluntaryWork voluntaryWork, + required int profileId, + required String token}) async { + Map? responseData = {}; + String authToken = "Token $token"; + String path = "${Url.instance.getVoluntaryWorks()}$profileId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + Map body = { + "position_id": voluntaryWork.position?.id, + "agency_id": voluntaryWork.agency?.id, + "address_id": voluntaryWork.address?.id, + "from_date": voluntaryWork.fromDate.toString(), + "to_date": voluntaryWork.toDate == null?null:voluntaryWork.toDate.toString(), + "total_hours": voluntaryWork.totalHours, + "_positionName": voluntaryWork.position!.title, + "_agencyName": voluntaryWork.agency!.name, + "_agencyCatId": voluntaryWork.agency!.category!.id, + "_privateEntity": voluntaryWork.agency!.privateEntity, + "_citymunCode": voluntaryWork.address?.cityMunicipality?.code, + "_countryId": voluntaryWork.address?.country?.id + }; + try { + http.Response response = await Request.instance + .postRequest(param: {}, path: path, body: body, headers: headers); + if (response.statusCode == 201) { + Map data = jsonDecode(response.body); + responseData = data; + } else { + responseData.addAll({'success': false}); + } + } catch (e) { + throw e.toString(); + } + return responseData; + } + +////update + Future> update( + {required VoluntaryWork voluntaryWork, + required int profileId, + required String token, + required int oldPosId, + required int oldAgencyId, + required String oldFromDate}) async { + Map? responseData = {}; + String authToken = "Token $token"; + String path = "${Url.instance.getVoluntaryWorks()}$profileId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + Map body = { + "position_id": voluntaryWork.position?.id, + "agency_id": voluntaryWork.agency?.id, + "address_id": voluntaryWork.address!.id, + "from_date": voluntaryWork.fromDate.toString(), + "to_date": voluntaryWork.toDate == null?null:voluntaryWork.toDate.toString(), + "total_hours": voluntaryWork.totalHours, + "_positionName": voluntaryWork.position!.title, + "_agencyName": voluntaryWork.agency!.name, + "_agencyCatId": voluntaryWork.agency!.category!.id, + "_privateEntity": voluntaryWork.agency!.privateEntity, + "_citymunCode": voluntaryWork.address?.cityMunicipality?.code, + "_countryId": voluntaryWork.address!.country!.id, + "_oldPosId": oldPosId, + "_oldAgencyId": oldAgencyId, + "_oldFromDate": oldFromDate + }; + try { + http.Response response = await Request.instance + .putRequest(param: {}, path: path, body: body, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + responseData = data; + } else { + responseData.addAll({'success': false}); + } + } catch (e) { + throw e.toString(); + } + return responseData; + } + + ////delete + Future delete( + {required int agencyId, + required int positionId, + required String fromDate, + required String token, + required int profileId}) async { + bool success = false; + String authToken = "Token $token"; + String path = "${Url.instance.getVoluntaryWorks()}$profileId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + Map params = {"force_mode": "true"}; + Map body = { + "agency_id": agencyId, + "position_id": positionId, + "from_date": fromDate, + }; + try { + http.Response response = await Request.instance.deleteRequest( + path: path, headers: headers, body: body, param: params); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + success = data['success']; + } + } catch (e) { + throw (e.toString()); + } + return success; + } +} diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index de84310..7f3839d 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -6,8 +6,8 @@ class Url { // return '192.168.10.183:3000'; // return 'agusandelnorte.gov.ph'; // return "192.168.10.219:3000"; - // return "devweb.agusandelnorte.gov.ph"; - return 'devapi.agusandelnorte.gov.ph:3004'; + return "devweb.agusandelnorte.gov.ph"; + // return 'devapi.agusandelnorte.gov.ph:3004'; } String authentication() { diff --git a/pubspec.yaml b/pubspec.yaml index 943b2ce..5a652da 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -77,7 +77,8 @@ dependencies: hive_flutter: ^1.1.0 mask_text_input_formatter: ^2.4.0 location: ^4.3.0 - platform_device_id: ^1.0.1, + platform_device_id: ^1.0.1 + multi_dropdown: ^1.0.9 dev_dependencies: flutter_test: From bfe1ee538bee53bf84346e2ca3ace1a6d01fc30e Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Tue, 2 May 2023 16:42:15 +0800 Subject: [PATCH 67/86] Implemented Address API --- .../address/address_bloc.dart | 203 ++++++++++--- .../address/address_event.dart | 84 ++++-- .../address/address_state.dart | 73 +++-- .../basic_information/address/add_modal.dart | 91 ++++-- .../basic_information/address/edit_modal.dart | 272 +++++++++++++++--- .../basic_information/address_screen.dart | 177 +++++++++--- lib/sevices/profile/address_service.dart | 142 +++++++++ lib/utils/request.dart | 41 +++ lib/utils/urls.dart | 5 + 9 files changed, 925 insertions(+), 163 deletions(-) create mode 100644 lib/sevices/profile/address_service.dart diff --git a/lib/bloc/profile/primary_information/address/address_bloc.dart b/lib/bloc/profile/primary_information/address/address_bloc.dart index 75cafa4..c6022e1 100644 --- a/lib/bloc/profile/primary_information/address/address_bloc.dart +++ b/lib/bloc/profile/primary_information/address/address_bloc.dart @@ -2,7 +2,9 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:unit2/bloc/profile/eligibility/eligibility_bloc.dart'; import 'package:unit2/model/location/barangay.dart'; +import 'package:unit2/model/location/subdivision.dart'; import 'package:unit2/model/profile/voluntary_works.dart'; +import 'package:unit2/sevices/profile/address_service.dart'; import '../../../../model/location/city.dart'; import '../../../../model/location/country.dart'; @@ -16,55 +18,188 @@ part 'address_state.dart'; class AddressBloc extends Bloc { AddressBloc() : super(AddressInitial()) { - List globalCountries=[]; - List globalRegions=[]; - List addresses = []; + List globalCountries = []; + List globalRegions = []; + List addresses = []; + List provinces = []; + List cities = []; + List barangays = []; + Region? currentRegion; + Country currentCountry; + Province? currentProvince; + CityMunicipality? currentCity; + Barangay? currentBarangay; on((event, emit) { emit(AddressLoadingState()); - try{ - addresses = event.addresses; + try { + addresses = event.addresses; + emit(AddressLoadedState(addresses: addresses)); + } catch (e) { + emit(AddressErrorState(message: e.toString())); + } + }); + ////Load + on((event, emit) { emit(AddressLoadedState(addresses: addresses)); - }catch(e){ - emit(AddressErrorState(message: e.toString())); - } - //// show add form - });on((event,emit)async{ + }); + + //// show add form + on((event, emit) async { emit(AddressLoadingState()); - try{ - if (globalRegions.isEmpty) { - List regions = await LocationUtils.instance.getRegions(); - globalRegions = regions; - } - if (globalCountries.isEmpty) { - List countries = await LocationUtils.instance.getCountries(); - globalCountries = countries; - } - emit(AddAddressState(countries: globalCountries, regions: globalRegions)); - }catch(e){ + try { + if (globalRegions.isEmpty) { + List regions = await LocationUtils.instance.getRegions(); + globalRegions = regions; + } + if (globalCountries.isEmpty) { + List countries = await LocationUtils.instance.getCountries(); + globalCountries = countries; + } + + emit(AddAddressState( + countries: globalCountries, regions: globalRegions)); + } catch (e) { emit(AddressErrorState(message: e.toString())); } }); //// Show Edit Form - on((event,emit)async{ + on((event, emit) async { + try { + if (globalRegions.isEmpty) { + List regions = await LocationUtils.instance.getRegions(); + globalRegions = regions; + } + if (globalCountries.isEmpty) { + List countries = await LocationUtils.instance.getCountries(); + globalCountries = countries; + } + currentCountry = globalCountries.firstWhere((Country country) => + event.address.address!.country!.code == country.code); - try{ - if (globalRegions.isEmpty) { - List regions = await LocationUtils.instance.getRegions(); - globalRegions = regions; - } - if (globalCountries.isEmpty) { - List countries = await LocationUtils.instance.getCountries(); - globalCountries = countries; - } - emit(EditAddressState(countries: globalCountries, regions: globalRegions,address: event.address)); - }catch(e){ + if (!event.overseas) { + //// if not overseas + currentRegion = globalRegions.firstWhere((Region region) => + event.address.address!.cityMunicipality!.province!.region!.code == + region.code); + provinces = await LocationUtils.instance + .getProvinces(regionCode: currentRegion!.code.toString()); + currentProvince = provinces.firstWhere((Province province) => + event.address.address!.cityMunicipality!.province!.code == + province.code); + + cities = await LocationUtils.instance + .getCities(code: currentProvince!.code.toString()); + + currentCity = cities.firstWhere((CityMunicipality cityMunicipality) => + event.address.address!.cityMunicipality!.code == + cityMunicipality.code); + barangays = await LocationUtils.instance + .getBarangay(code: currentCity!.code.toString()); + if (event.address.address?.barangay != null) { + currentBarangay = barangays.firstWhere((Barangay barangay) => + event.address.address?.barangay?.code == barangay.code); + } else { + currentBarangay = null; + } + } + + emit(EditAddressState( + countries: globalCountries, + regions: globalRegions, + address: event.address, + baragays: barangays, + cities: cities, + currentCity: currentCity, + currentCountry: currentCountry, + currentProvince: currentProvince, + currentRegion: currentRegion, + overseas: event.overseas, + provinces: provinces, + currentBarangay: currentBarangay)); + } catch (e) { emit(AddressErrorState(message: e.toString())); } }); + ////Add + on( + (event, emit) async { + try { + Map status = await AddressService.instance.add( + address: event.address, + categoryId: event.categoryId, + token: event.token, + details: event.details, + blockNumber: event.blockNumber, + lotNumber: event.lotNumber, + profileId: event.profileId); + if (status['success']) { + AddressClass addressClass = AddressClass.fromJson(status['address']); + Subdivision? subdivision = status['subdivision'] !=null? Subdivision.fromJson(status['subdivision']):null; + + MainAdress address = MainAdress(address: addressClass,subdivision: subdivision,id: status['id'],details: status['details']); + addresses.add(address); + emit(AddressAddedState(response: status)); + } else { + emit(AddressAddedState(response: status)); + } + } catch (e) { + emit(AddressErrorState(message: e.toString())); + } + }, + ); + ////update + on( + (event, emit) async { + // try { + Map status = await AddressService.instance.update( + address: event.address, + categoryId: event.categoryId, + token: event.token, + details: event.details, + blockNumber: event.blockNumber, + lotNumber: event.lotNumber, + profileId: event.profileId); + if (status['success']) { + AddressClass addressClass = AddressClass.fromJson(status['address']); + Subdivision? subdivision = status['subdivision'] !=null? Subdivision.fromJson(status['subdivision']):null; + MainAdress address = MainAdress(address: addressClass,subdivision: subdivision,id: status['id'],details: status['details']); + addresses.removeWhere((address)=>address.id == event.address.id); + addresses.add(address); + + emit(AddressUpdatedState(response: status)); + } else { + emit(AddressUpdatedState(response: status)); + } + // } catch (e) { + // emit(AddressErrorState(message: e.toString())); + // } + }, + ); + ////Update +////Delete + on((event, emit) async { + try { + final bool success = await AddressService.instance.delete( + addressId: event.id, + profileId: int.parse(event.profileId), + token: event.token); + if (success) { + addresses + .removeWhere(((MainAdress element) => element.id == event.id)); + emit(AddressDeletedState( + success: success, + )); + } else { + emit(AddressDeletedState(success: success)); + } + } catch (e) { + emit(AddressErrorState(message: e.toString())); + } + }); ////call error state - on((event, emit) { + on((event, emit) { emit(const AddressErrorState( message: "Something went wrong. Please try again")); }); diff --git a/lib/bloc/profile/primary_information/address/address_event.dart b/lib/bloc/profile/primary_information/address/address_event.dart index 9446ba2..193fc74 100644 --- a/lib/bloc/profile/primary_information/address/address_event.dart +++ b/lib/bloc/profile/primary_information/address/address_event.dart @@ -7,28 +7,78 @@ abstract class AddressEvent extends Equatable { List get props => []; } -class GetAddress extends AddressEvent{ - final List addresses; - const GetAddress({required this.addresses}); - @override +class GetAddress extends AddressEvent { + final List addresses; + const GetAddress({required this.addresses}); + @override List get props => [addresses]; } -class ShowAddAddressForm extends AddressEvent{ - -} -class ShowEditAddressForm extends AddressEvent{ + +class ShowAddAddressForm extends AddressEvent {} + +class ShowEditAddressForm extends AddressEvent { + final bool overseas; final MainAdress address; - const ShowEditAddressForm({required this.address}); -} -class CallErrorState extends AddressEvent{ - + const ShowEditAddressForm({required this.address, required this.overseas}); } -class AddAddress extends EligibilityEvent{ - final MainAdress address; +class CallErrorState extends AddressEvent {} + +class AddAddress extends AddressEvent { + final AddressClass address; + final int categoryId; + final String? details; + final int? blockNumber; + final int? lotNumber; final String token; final int profileId; - const AddAddress({required this.address, required this.profileId, required this.token}); - @override - List get props => [address,token,profileId]; + const AddAddress( + {required this.address, + required this.profileId, + required this.token, + required this.blockNumber, + required this.categoryId, + required this.details, + required this.lotNumber}); + @override + List get props => [address, token, profileId,categoryId]; } + +class UpdateAddress extends AddressEvent { + final AddressClass address; + final int categoryId; + final String? details; + final int? blockNumber; + final int? lotNumber; + final String token; + final int profileId; + const UpdateAddress( + {required this.address, + required this.profileId, + required this.token, + required this.blockNumber, + required this.categoryId, + required this.details, + required this.lotNumber}); + @override + List get props => [address, token, profileId,categoryId]; +} + + +class LoadAddress extends AddressEvent{ + +} + +class DeleteAddress extends AddressEvent { + final String profileId; + final int id; + final String token; + const DeleteAddress( + { + required this.id, + required this.profileId, + required this.token}); + @override + List get props => [ profileId, id, token]; +} + diff --git a/lib/bloc/profile/primary_information/address/address_state.dart b/lib/bloc/profile/primary_information/address/address_state.dart index 6c9e70d..efef785 100644 --- a/lib/bloc/profile/primary_information/address/address_state.dart +++ b/lib/bloc/profile/primary_information/address/address_state.dart @@ -8,6 +8,7 @@ abstract class AddressState extends Equatable { } class AddressInitial extends AddressState {} + //// LOADED STATE class AddressLoadedState extends AddressState { final List addresses; @@ -15,6 +16,7 @@ class AddressLoadedState extends AddressState { @override List get props => [addresses]; } + ////ERROR STATE class AddressErrorState extends AddressState { final String message; @@ -22,6 +24,7 @@ class AddressErrorState extends AddressState { @override List get props => [message]; } + //// LOADING STATE class AddressLoadingState extends AddressState {} @@ -29,28 +32,66 @@ class AddressLoadingState extends AddressState {} class AddAddressState extends AddressState { final List countries; final List regions; - const AddAddressState( - { - required this.countries, - required this.regions}); + const AddAddressState({required this.countries, required this.regions}); @override - List get props => [countries, regions,]; + List get props => [ + countries, + regions, + ]; +} + +//// DeletedState +class AddressDeletedState extends AddressState { + final bool success; + const AddressDeletedState({required this.success}); + @override + List get props => [success]; } ////AddedState -class AddressAddedState extends AddressState{ - final Map response; - const AddressAddedState({required this.response}); - @override +class AddressAddedState extends AddressState { + final Map response; + const AddressAddedState({required this.response}); + @override List get props => [response]; - } -class EditAddressState extends AddressState{ +////Edited State +class AddressUpdatedState extends AddressState { + final Map response; + const AddressUpdatedState({required this.response}); + @override + List get props => [response]; +} + +class EditAddressState extends AddressState { final MainAdress address; - final List countries; + final List countries; final List regions; - const EditAddressState({required this.address, required this.countries, required this.regions}); - @override - List get props => [countries, regions,address]; -} \ No newline at end of file + final List provinces; + final List cities; + final List baragays; + final Region? currentRegion; + final Country currentCountry; + final Province? currentProvince; + final CityMunicipality? currentCity; + final Barangay? currentBarangay; + final bool overseas; + + const EditAddressState( + {required this.address, + required this.countries, + required this.regions, + required this.baragays, + required this.cities, + required this.currentCity, + required this.currentCountry, + required this.currentProvince, + required this.currentRegion, + required this.overseas, + required this.provinces, + required this.currentBarangay + }); + @override + List get props => [countries, regions, address]; +} diff --git a/lib/screens/profile/components/basic_information/address/add_modal.dart b/lib/screens/profile/components/basic_information/address/add_modal.dart index af4e8af..21acda2 100644 --- a/lib/screens/profile/components/basic_information/address/add_modal.dart +++ b/lib/screens/profile/components/basic_information/address/add_modal.dart @@ -3,10 +3,13 @@ import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter/src/widgets/placeholder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; import 'package:unit2/bloc/profile/primary_information/address/address_bloc.dart'; import 'package:unit2/model/location/address_category.dart'; +import 'package:unit2/model/profile/basic_information/adress.dart'; +import 'package:unit2/model/profile/voluntary_works.dart'; import 'package:unit2/model/utils/category.dart'; import 'package:unit2/utils/global.dart'; @@ -20,6 +23,7 @@ import '../../../../../theme-data.dart/colors.dart'; import '../../../../../theme-data.dart/form-style.dart'; import '../../../../../utils/location_utilities.dart'; import '../../../../../utils/text_container.dart'; +import '../../../../../utils/validators.dart'; class AddAddressScreen extends StatefulWidget { final int profileId; @@ -57,7 +61,11 @@ class _AddAddressScreenState extends State { const Area(value: "Metropolis", group: 1), const Area(value: "Megacity", group: 1), ]; - final List category = [AddressCategory(id: 1, name: "Permanent", type: "home"), AddressCategory(id: 2, name: "Residential", type: "home"), AddressCategory(id: 3, name: "Birthplace", type: "home")]; + final List category = [ + AddressCategory(id: 1, name: "Permanent", type: "home"), + AddressCategory(id: 2, name: "Residential", type: "home"), + AddressCategory(id: 3, name: "Birthplace", type: "home") + ]; List? provinces; List? citymuns; List? barangays; @@ -84,9 +92,12 @@ class _AddAddressScreenState extends State { decoration: normalTextFieldStyle("Category*", "Category"), name: "category", - onChanged: (AddressCategory? category) {}, - items: category.map>( - (AddressCategory category) { + onChanged: (AddressCategory? category) { + selectedAddressCategory = category; + }, + items: category + .map>( + (AddressCategory category) { return DropdownMenuItem( value: category, child: Text(category.name!)); }).toList()), @@ -151,6 +162,8 @@ class _AddAddressScreenState extends State { Flexible( flex: 1, child: FormBuilderTextField( + validator: FormBuilderValidators.compose([numericRequired]), + keyboardType: TextInputType.number, name: "block_number", decoration: normalTextFieldStyle( @@ -163,7 +176,9 @@ class _AddAddressScreenState extends State { Flexible( flex: 1, child: FormBuilderTextField( + validator: FormBuilderValidators.compose([numericRequired]), name: "lot_number", + keyboardType: TextInputType.number, decoration: normalTextFieldStyle( "Lot #*", "Lot #"), @@ -180,6 +195,7 @@ class _AddAddressScreenState extends State { ), //// Address Line FormBuilderTextField( + name: "address_line", decoration: normalTextFieldStyle( "Address Line *", "Address Line"), @@ -391,31 +407,64 @@ class _AddAddressScreenState extends State { name: 'country', decoration: normalTextFieldStyle( "Country*", "Country"), - onChanged: (Country? value) {}, + onChanged: (Country? value) { + selectedCountry = value; + }, ), ), ), - ], ); }), - ////sumit button - const Expanded(child: SizedBox()), - SizedBox( - width: double.infinity, - height: 60, - child: ElevatedButton( - style: mainBtnStyle( - primary, Colors.transparent, second), - onPressed: () {}, - child: const Text(submit)), - ), - const SizedBox( - height: 20, - ), + ////sumit button + const Expanded(child: SizedBox()), + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState!.saveAndValidate()) { + String? lotNumber; + String? blockNumber; + String? addressLine; + Country country = selectedCountry ??= Country( + id: 175, name: 'Philippines', code: 'PH'); + AddressClass address = AddressClass( + barangay: selectedBarangay, + id: null, + category: null, + areaClass: selectedAreaClass, + cityMunicipality: selectedMunicipality, + country: country); + if (hasLotandBlock) { + lotNumber = formKey + .currentState!.value['lot_number']; + blockNumber = formKey + .currentState!.value['block_number']; + } + addressLine = formKey + .currentState?.value['address_line']; + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add(AddAddress( + address: address, + profileId: widget.profileId, + token: widget.token, + blockNumber: blockNumber != null?int.parse(blockNumber):null, + categoryId: selectedAddressCategory!.id!, + details: addressLine, + lotNumber: lotNumber != null?int.parse(lotNumber):null)); + } + }, + child: const Text(submit)), + ), + const SizedBox( + height: 20, + ), ], ), - )), ), ); diff --git a/lib/screens/profile/components/basic_information/address/edit_modal.dart b/lib/screens/profile/components/basic_information/address/edit_modal.dart index 5760b17..9eb828f 100644 --- a/lib/screens/profile/components/basic_information/address/edit_modal.dart +++ b/lib/screens/profile/components/basic_information/address/edit_modal.dart @@ -3,10 +3,15 @@ import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter/src/widgets/placeholder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; import 'package:unit2/bloc/profile/primary_information/address/address_bloc.dart'; +import 'package:unit2/model/location/address_category.dart'; +import 'package:unit2/model/profile/basic_information/adress.dart'; +import 'package:unit2/model/profile/voluntary_works.dart'; import 'package:unit2/model/utils/category.dart'; +import 'package:unit2/screens/profile/components/other_information/org_membership/add_modal.dart'; import 'package:unit2/utils/global.dart'; import '../../../../../model/location/barangay.dart'; @@ -19,6 +24,7 @@ import '../../../../../theme-data.dart/colors.dart'; import '../../../../../theme-data.dart/form-style.dart'; import '../../../../../utils/location_utilities.dart'; import '../../../../../utils/text_container.dart'; +import '../../../../../utils/validators.dart'; class EditAddressScreen extends StatefulWidget { final int profileId; @@ -38,6 +44,8 @@ class _EditAddressScreenState extends State { bool cityCall = false; bool barangayCall = false; ////selected + AddressCategory? selectedAddressCategory; + Area? selectedAreaClass; Region? selectedRegion; Province? selectedProvince; CityMunicipality? selectedMunicipality; @@ -54,7 +62,11 @@ class _EditAddressScreenState extends State { const Area(value: "Metropolis", group: 1), const Area(value: "Megacity", group: 1), ]; - final List category = ["Permanent", "Residential", "Birthplace"]; + final List category = [ + AddressCategory(id: 1, name: "Permanent", type: "home"), + AddressCategory(id: 2, name: "Residential", type: "home"), + AddressCategory(id: 3, name: "Birthplace", type: "home") + ]; List? provinces; List? citymuns; List? barangays; @@ -64,6 +76,33 @@ class _EditAddressScreenState extends State { return BlocBuilder( builder: (context, state) { if (state is EditAddressState) { + overseas = state.overseas; + if (!overseas) { + selectedRegion = state.currentRegion; + provinces = state.provinces; + selectedProvince = state.currentProvince; + citymuns = state.cities; + selectedMunicipality = state.currentCity; + barangays = state.baragays; + selectedBarangay = state.currentBarangay; + } else { + selectedCountry = state.currentCountry; + } + selectedAddressCategory = category.firstWhere( + (AddressCategory category) => + category.id == state.address.address!.category!.id); + if (state.address.address?.areaClass != null) { + selectedAreaClass = areaClass.firstWhere((Area area) => + area.value.toLowerCase() == + state.address.address!.areaClass!.toLowerCase()); + } else { + selectedAreaClass = null; + } + if (state.address.subdivision != null) { + hasLotandBlock = true; + } else { + hasLotandBlock = false; + } return SingleChildScrollView( child: SizedBox( height: screenHeight * .90, @@ -72,32 +111,39 @@ class _EditAddressScreenState extends State { child: Padding( padding: const EdgeInsets.symmetric( vertical: 25, horizontal: 18), - child: Column( + child: ListView( children: [ //// category - FormBuilderDropdown( + FormBuilderDropdown( + initialValue: selectedAddressCategory, validator: FormBuilderValidators.required( errorText: "This field is required"), decoration: normalTextFieldStyle("Category*", "Category"), name: "category", - onChanged: (String? category) {}, - items: category.map>( - (String category) { + onChanged: (AddressCategory? category) { + selectedAddressCategory = category; + }, + items: category + .map>( + (AddressCategory category) { return DropdownMenuItem( - value: category, child: Text(category)); + value: category, child: Text(category.name!)); }).toList()), const SizedBox( height: 12, ), ////Area Class FormBuilderDropdown( + initialValue: selectedAreaClass, validator: FormBuilderValidators.required( errorText: "This field is required"), decoration: normalTextFieldStyle( "Area class *", "Area class"), name: "area_class", - onChanged: (Area? area) {}, + onChanged: (Area? area) { + selectedAreaClass = area; + }, items: areaClass .map>((Area area) { return area.group == 0 @@ -146,6 +192,11 @@ class _EditAddressScreenState extends State { Flexible( flex: 1, child: FormBuilderTextField( + initialValue: state.address.subdivision?.blockNo?.toString(), + validator: FormBuilderValidators + .compose([numericRequired]), + keyboardType: + TextInputType.number, name: "block_number", decoration: normalTextFieldStyle( @@ -158,7 +209,12 @@ class _EditAddressScreenState extends State { Flexible( flex: 1, child: FormBuilderTextField( + initialValue: state.address.subdivision?.lotNo?.toString(), + validator: FormBuilderValidators + .compose([numericRequired]), name: "lot_number", + keyboardType: + TextInputType.number, decoration: normalTextFieldStyle( "Lot #*", "Lot #"), @@ -175,6 +231,7 @@ class _EditAddressScreenState extends State { ), //// Address Line FormBuilderTextField( + initialValue: state.address.details, name: "address_line", decoration: normalTextFieldStyle( "Address Line *", "Address Line"), @@ -209,7 +266,9 @@ class _EditAddressScreenState extends State { height: 12, ), ////REGION DROPDOWN - FormBuilderDropdown( + DropdownButtonFormField( + isExpanded: true, + value: selectedRegion, autovalidateMode: AutovalidateMode .onUserInteraction, validator: @@ -222,13 +281,71 @@ class _EditAddressScreenState extends State { provinceCall = true; }); selectedRegion = region; - getProvinces(); + //// GET PROVINCES + provinces = await LocationUtils + .instance + .getProvinces( + regionCode: + selectedRegion!.code + .toString()); + selectedProvince = + provinces![0]; + setState(() { + provinceCall = false; + cityCall = true; + }); + //// GET CITIES + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code!); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + barangayCall = true; + }); + //// GET BARANGAY + barangays = await LocationUtils + .instance + .getBarangay( + code: + selectedMunicipality! + .code!); + selectedBarangay = + barangays![0]; + setState(() { + barangayCall = false; + }); + ////GET CITY MUNICIPALITY + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code!); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + barangayCall = true; + }); + //// GET BARANGAYS + barangays = await LocationUtils + .instance + .getBarangay( + code: + selectedMunicipality! + .code!); + selectedBarangay = + barangays![0]; + setState(() { + barangayCall = false; + }); } }, - initialValue: null, decoration: normalTextFieldStyle( "Region*", "Region"), - name: 'region', items: state.regions .map>( (Region region) { @@ -258,16 +375,41 @@ class _EditAddressScreenState extends State { : null, isExpanded: true, value: selectedProvince, - onChanged: - (Province? province) { + onChanged: (Province? + province) async { if (selectedProvince != province) { + selectedProvince = + province; setState(() { cityCall = true; }); - selectedProvince = - province; - getCities(); + + //// GET CITIES + citymuns = await LocationUtils + .instance + .getCities( + code: + selectedProvince! + .code!); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + barangayCall = true; + }); + //// GET BARANGAY + barangays = await LocationUtils + .instance + .getBarangay( + code: + selectedMunicipality! + .code!); + selectedBarangay = + barangays![0]; + setState(() { + barangayCall = false; + }); } }, items: provinces == null @@ -302,15 +444,27 @@ class _EditAddressScreenState extends State { errorText: "This field is required"), isExpanded: true, - onChanged: - (CityMunicipality? city) { + onChanged: (CityMunicipality? + city) async { if (selectedMunicipality != city) { setState(() { barangayCall = true; }); selectedMunicipality = city; - getBarangays(); + selectedMunicipality = city; + //// GET BARANGAYS + barangays = await LocationUtils + .instance + .getBarangay( + code: + selectedMunicipality! + .code!); + selectedBarangay = + barangays![0]; + setState(() { + barangayCall = false; + }); } }, decoration: @@ -369,7 +523,7 @@ class _EditAddressScreenState extends State { : SizedBox( height: 60, child: FormBuilderDropdown( - initialValue: null, + initialValue: selectedCountry, validator: FormBuilderValidators.required( errorText: @@ -386,31 +540,73 @@ class _EditAddressScreenState extends State { name: 'country', decoration: normalTextFieldStyle( "Country*", "Country"), - onChanged: (Country? value) {}, + onChanged: (Country? value) { + selectedCountry = value; + }, ), ), ), - ], ); }), - ////sumit button - const Expanded(child: SizedBox()), - SizedBox( - width: double.infinity, - height: 60, - child: ElevatedButton( - style: mainBtnStyle( - primary, Colors.transparent, second), - onPressed: () {}, - child: const Text(submit)), - ), - const SizedBox( - height: 20, - ), + ////sumit button + const Expanded(child: SizedBox()), + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState!.saveAndValidate()) { + String? lotNumber; + String? blockNumber; + String? addressLine; + if(overseas){ + selectedCountry = selectedCountry; + }else{ + selectedCountry = Country( + id: 175, name: 'Philippines', code: 'PH'); + } + + AddressClass address = AddressClass( + barangay: overseas?null:selectedBarangay, + id: state.address.id, + category: null, + areaClass: selectedAreaClass!.value, + cityMunicipality: overseas?null:selectedMunicipality, + country: selectedCountry); + if (hasLotandBlock) { + lotNumber = formKey + .currentState!.value['lot_number']; + blockNumber = formKey + .currentState!.value['block_number']; + } + addressLine = formKey + .currentState?.value['address_line']; + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add(UpdateAddress( + address: address, + profileId: widget.profileId, + token: widget.token, + blockNumber: blockNumber != null + ? int.parse(blockNumber) + : null, + categoryId: selectedAddressCategory!.id!, + details: addressLine, + lotNumber: lotNumber != null + ? int.parse(lotNumber) + : null)); + } + }, + child: const Text(submit)), + ), + const SizedBox( + height: 20, + ), ], ), - )), ), ); diff --git a/lib/screens/profile/components/basic_information/address_screen.dart b/lib/screens/profile/components/basic_information/address_screen.dart index 9801069..f025239 100644 --- a/lib/screens/profile/components/basic_information/address_screen.dart +++ b/lib/screens/profile/components/basic_information/address_screen.dart @@ -39,11 +39,11 @@ class AddressScreen extends StatelessWidget { ], ), body: ProgressHUD( - padding: const EdgeInsets.all(24), - indicatorWidget: const SpinKitFadingCircle( - color: Colors.white, - ), - backgroundColor: Colors.black87, + padding: const EdgeInsets.all(24), + indicatorWidget: const SpinKitFadingCircle( + color: Colors.white, + ), + backgroundColor: Colors.black87, child: BlocBuilder( builder: (context, state) { if (state is UserLoggedIn) { @@ -61,13 +61,67 @@ class AddressScreen extends StatelessWidget { if (state is AddressLoadedState || state is AddressErrorState || state is AddAddressState || - state is EditAddressState) { + state is EditAddressState || + state is AddressAddedState || state is EditAddressState || state is AddressUpdatedState) { final progress = ProgressHUD.of(context); progress!.dismiss(); } + ////Added State + if (state is AddressAddedState) { + if (state.response['success']) { + successAlert(context, "Adding Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context.read().add(LoadAddress()); + }); + } else { + errorAlert(context, "Adding Failed", + state.response['message'], () { + Navigator.of(context).pop(); + context.read().add(LoadAddress()); + }); + } + } + ////updated State + if (state is AddressUpdatedState) { + if (state.response['success']) { + successAlert(context, "Update Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context.read().add(LoadAddress()); + }); + } else { + errorAlert(context, "Update Failed", + state.response['message'], () { + Navigator.of(context).pop(); + context.read().add(LoadAddress()); + }); + } + } + //// Deleted + if (state is AddressDeletedState) { + if (state.success) { + successAlert(context, "Deletion Successfull", + "Address has been deleted successfully", () { + Navigator.of(context).pop(); + context.read().add(LoadAddress()); + }); + } else { + errorAlert(context, "Deletion Failed", + "Error deleting Address", () { + Navigator.of(context).pop(); + context.read().add(LoadAddress()); + }); + } + } }, builder: (context, state) { if (state is AddressLoadedState) { + bool overseas; + String? cityMunicipality; + String? province; + String? region; + String? barangay; if (state.addresses.isNotEmpty) { return ListView.builder( padding: const EdgeInsets.symmetric( @@ -79,29 +133,37 @@ class AddressScreen extends StatelessWidget { state.addresses[index].details ?? ''; String category = state.addresses[index] .address!.category!.name!; - String? barangay = state.addresses[index] - .address!.barangay != - null - ? '${state.addresses[index].address!.barangay!.description!.toUpperCase()},' - : ''; - String cityMunicipality = state - .addresses[index] - .address! - .cityMunicipality! - .description!; - String province = state - .addresses[index] - .address! - .cityMunicipality! - .province! - .description!; - String region = state - .addresses[index] - .address! - .cityMunicipality! - .province! - .region! - .description!; + if (state.addresses[index].address!.country! + .id == + 175) { + barangay = state.addresses[index].address! + .barangay != + null + ? '${state.addresses[index].address!.barangay!.description!.toUpperCase()},' + : ''; + cityMunicipality = state + .addresses[index] + .address! + .cityMunicipality! + .description!; + province = state + .addresses[index] + .address! + .cityMunicipality! + .province! + .description!; + region = state + .addresses[index] + .address! + .cityMunicipality! + .province! + .region! + .description!; + overseas = false; + } else { + overseas = true; + } + return Column( children: [ Column( @@ -115,6 +177,10 @@ class AddressScreen extends StatelessWidget { child: Row(children: [ Expanded( child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, children: [ Row( children: [ @@ -141,12 +207,25 @@ class AddressScreen extends StatelessWidget { const SizedBox( height: 5, ), - Text( - "$barangay $cityMunicipality, $province, $region", - style: Theme.of(context) - .textTheme - .labelLarge, - ), + Container( + child: overseas + ? Text( + "COUNTRY: ${state.addresses[index].address!.country!.name!.toUpperCase()}", + textAlign: + TextAlign + .start, + style: Theme.of( + context) + .textTheme + .labelLarge, + ) + : Text( + "$barangay $cityMunicipality, $province, $region", + style: Theme.of( + context) + .textTheme + .labelLarge, + )), ], )), AppPopupMenu( @@ -164,8 +243,19 @@ class AddressScreen extends StatelessWidget { "Loading..."); }, "Delete?", "Confirm Delete?"); + context + .read() + .add(DeleteAddress( + id: state + .addresses[ + index] + .id!, + profileId: profileId + .toString(), + token: token!)); } if (value == 1) { + bool overseas; ////edit eligibilty-= = = = = = = = =>> final progress = ProgressHUD.of( @@ -173,9 +263,20 @@ class AddressScreen extends StatelessWidget { progress!.showWithText( "Loading..."); + if (state + .addresses[index] + .address + ?.cityMunicipality == + null) { + overseas = true; + } else { + overseas = false; + } context .read() .add(ShowEditAddressForm( + overseas: + overseas, address: state .addresses[ index])); @@ -225,8 +326,10 @@ class AddressScreen extends StatelessWidget { if (state is AddAddressState) { return AddAddressScreen( profileId: profileId!, token: token!); - }if(state is EditAddressState){ - return EditAddressScreen(profileId: profileId!, token: token!); + } + if (state is EditAddressState) { + return EditAddressScreen( + profileId: profileId!, token: token!); } return Container(); }, diff --git a/lib/sevices/profile/address_service.dart b/lib/sevices/profile/address_service.dart new file mode 100644 index 0000000..5458c98 --- /dev/null +++ b/lib/sevices/profile/address_service.dart @@ -0,0 +1,142 @@ +import 'dart:convert'; + +import 'package:unit2/theme-data.dart/colors.dart'; + +import '../../model/profile/basic_information/adress.dart'; +import '../../utils/request.dart'; +import '../../utils/urls.dart'; +import 'package:http/http.dart' as http; + +class AddressService { + static final AddressService _instance = AddressService(); + static AddressService get instance => _instance; +////add + Future> add( + {required AddressClass address, + required int categoryId, + required String? details, + required int? blockNumber, + required int? lotNumber, + required String token, + required int profileId}) async { + Map? _response = {}; + String authtoken = "Token $token"; + String path = '${Url.instance.addressPath()}$profileId/'; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authtoken + }; + Map body = { + "id": address.id, + "details": details, + "_addressCatId": categoryId, + "_areaClass": address.areaClass, + "_blockNo": blockNumber, + "_lotNo": lotNumber, + "_citymunCode": address.cityMunicipality?.code, + "_brgyCode": address.barangay?.code, + "_countryId": address.country!.id + }; + try { + http.Response response = await Request.instance + .postRequest(path: path, body: body, headers: headers, param: {}); + if (response.statusCode == 201) { + Map data = jsonDecode(response.body); + _response = data['response']['data']; + _response!.addAll({'success': true}); + } else { + _response.addAll({'success': false}); + } + + return _response; + } catch (e) { + throw e.toString(); + } + } + + ////delete + Future delete( + {required int addressId, + required int profileId, + required String token}) async { + bool? success; + String authtoken = "Token $token"; + String path = "${Url.instance.addressPath()}$profileId/"; + Map body = {"id": addressId}; + Map params = {"force_mode": "true"}; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authtoken + }; + try { + http.Response response = await Request.instance.deleteRequest( + path: path, headers: headers, body: body, param: params); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + success = data['success']; + } else { + success = false; + } + } catch (e) { + throw (e.toString()); + } + return success!; + } + + ////update + Future> update( + {required AddressClass address, + required int categoryId, + required String? details, + required int? blockNumber, + required int? lotNumber, + required String token, + required int profileId}) async { + Map? _response = {}; + String authtoken = "Token $token"; + String path = '${Url.instance.addressPath()}$profileId/'; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authtoken + }; + Map body = { + "id": address.id, + "details": details, + "_addressCatId": categoryId, + "_areaClass": address.areaClass, + "_blockNo": blockNumber, + "_lotNo": lotNumber, + "_citymunCode": address.cityMunicipality?.code, + "_brgyCode": address.barangay?.code, + "_countryId": address.country!.id + }; + try { + http.Response response = await http.patch( + Uri.parse( + 'http://${Url.instance.host()}${Url.instance.addressPath()}$profileId/'), + headers: headers, + body: jsonEncode({ + "id": address.id, + "details": details, + "_addressCatId": categoryId, + "_areaClass": address.areaClass, + "_blockNo": blockNumber, + "_lotNo": lotNumber, + "_citymunCode": address.cityMunicipality?.code, + "_brgyCode": address.barangay?.code, + "_countryId": address.country!.id + })); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + _response = data['response']['data']; + _response!.addAll({'success': true}); + } else { + _response.addAll({'success': false}); + } + + return _response; + } catch (e) { + throw e.toString(); + } + } +} diff --git a/lib/utils/request.dart b/lib/utils/request.dart index c224af0..329b3c4 100644 --- a/lib/utils/request.dart +++ b/lib/utils/request.dart @@ -137,6 +137,47 @@ class Request { } return response; } + Future patch( + {required String path, + required Map? headers, + required Map? body, + required Map? param}) async { + Response response; + try { + response =await patch(path: host+path, headers: headers,body: body,param: param); + } on TimeoutException catch (_) { + Fluttertoast.showToast( + msg: timeoutError, + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.black, + ); + throw (timeoutError); + } on SocketException catch (_) { + Fluttertoast.showToast( + msg: timeoutError, + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.black, + ); + throw (timeoutError); + } on FormatException catch (_) { + throw const FormatException(formatError); + } on HttpException catch (_) { + throw const HttpException(httpError); + } on Error catch (e) { + debugPrint("post request error: $e"); + Fluttertoast.showToast( + msg: onError, + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.black, + ); + throw (e.toString()); + } + return response; + } + Future deleteRequest( {required String path, diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index 7f3839d..3d755e1 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -125,6 +125,11 @@ String getServiceTypes(){ return "/api/jobnet_app/comm_service_type/"; } +//// address path +String addressPath(){ + return "/api/jobnet_app/profile/pds/basic/address/"; +} + String contactPath(){ return "/api/jobnet_app/profile/pds/basic/contact/"; } From d8efeffd8c592f1333f6f6e2a23bdd8897cb7516 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Thu, 11 May 2023 08:04:53 +0800 Subject: [PATCH 68/86] Implement Basic Information Identification API --- lib/bloc/profile/family/family_bloc.dart | 106 +- lib/bloc/profile/family/family_event.dart | 49 + lib/bloc/profile/family/family_state.dart | 30 +- .../identification/identification_bloc.dart | 196 +- .../identification/identification_event.dart | 55 +- .../identification/identification_state.dart | 46 + lib/model/profile/family_backround.dart | 309 ++- .../contact_information/add_modal.dart | 11 +- .../family/add_mobile_modal.dart | 57 + .../family/child_add_modal.dart | 339 +++ .../family/child_edit_modal.dart | 362 +++ .../family/father_add_modal.dart | 344 +++ .../family/father_edit_modal.dart | 362 +++ .../family/mother_add_modal.dart | 372 +++ .../family/mother_edit_modal.dart | 390 +++ .../family/related_add_modal.dart | 381 +++ .../family/related_edit_modal.dart | 421 ++++ .../family/spouse_add_modal.dart | 722 ++++++ .../family/spouse_edit_modal.dart | 800 ++++++ .../identification/add_modal.dart | 826 ++++++ .../identification/edit_modal.dart | 528 ++++ .../identification_information_screen.dart | 439 +++- .../components/eligibility/add_modal.dart | 6 +- .../components/eligibility/edit_modal.dart | 13 +- .../components/family_background_screen.dart | 2221 ++++++++++++++--- .../unit2/homepage.dart/module-screen.dart | 3 + lib/sevices/profile/family_services.dart | 213 +- .../profile/identification_services.dart | 125 + lib/utils/alerts.dart | 2 +- lib/utils/formatters.dart | 13 + lib/utils/profile_utilities.dart | 30 + lib/utils/urls.dart | 13 +- 32 files changed, 9129 insertions(+), 655 deletions(-) create mode 100644 lib/screens/profile/components/basic_information/family/add_mobile_modal.dart create mode 100644 lib/screens/profile/components/basic_information/family/child_add_modal.dart create mode 100644 lib/screens/profile/components/basic_information/family/child_edit_modal.dart create mode 100644 lib/screens/profile/components/basic_information/family/father_add_modal.dart create mode 100644 lib/screens/profile/components/basic_information/family/father_edit_modal.dart create mode 100644 lib/screens/profile/components/basic_information/family/mother_add_modal.dart create mode 100644 lib/screens/profile/components/basic_information/family/mother_edit_modal.dart create mode 100644 lib/screens/profile/components/basic_information/family/related_add_modal.dart create mode 100644 lib/screens/profile/components/basic_information/family/related_edit_modal.dart create mode 100644 lib/screens/profile/components/basic_information/family/spouse_add_modal.dart create mode 100644 lib/screens/profile/components/basic_information/family/spouse_edit_modal.dart create mode 100644 lib/screens/profile/components/basic_information/identification/add_modal.dart create mode 100644 lib/screens/profile/components/basic_information/identification/edit_modal.dart create mode 100644 lib/sevices/profile/identification_services.dart create mode 100644 lib/utils/formatters.dart diff --git a/lib/bloc/profile/family/family_bloc.dart b/lib/bloc/profile/family/family_bloc.dart index 30db4da..e49737c 100644 --- a/lib/bloc/profile/family/family_bloc.dart +++ b/lib/bloc/profile/family/family_bloc.dart @@ -9,14 +9,110 @@ part 'family_state.dart'; class FamilyBloc extends Bloc { FamilyBloc() : super(FamilyInitial()) { - List families = []; - on((event, emit) async{ + List families = []; + on((event, emit) async { emit(FamilyLoadingState()); - try{ - List family = await FamilyService.instance.getFamilies(event.profileId, event.token); + try { + emit(FamilyLoadingState()); + List family = await FamilyService.instance + .getFamilies(event.profileId, event.token); families = family; emit(FamilyLoaded(families: families)); - }catch(e){ + } catch (e) { + emit(FamilyErrorState(message: e.toString())); + } + ////Load + on((event, emit) { + emit(FamilyLoaded(families: families)); + }); + ////Add Family + }); + on((event, emit) async { + try { + emit(FamilyLoadingState()); + Map status = await FamilyService.instance.add( + family: event.familyBackground, + relationshipId: event.relationshipId, + profileId: event.profileId, + token: event.token); + if (status['success']) { + FamilyBackground familyBackground = + FamilyBackground.fromJson(status['data']); + families.add(familyBackground); + emit(FamilyAddedState(response: status)); + } else { + emit(FamilyAddedState(response: status)); + } + } catch (e) { + emit(FamilyErrorState(message: e.toString())); + } + }); + //// Add Emergency + on((event, emit) async { + try { + emit(FamilyLoadingState()); + Map status = await FamilyService.instance + .addEmergency( + requestType: event.requestType, + relatedPersonId: event.relatedPersonId, + numberMail: event.numberMail, + contactInfoId: event.contactInfoId, + profileId: event.profileId, + token: event.token); + if (status['success']) { + families.removeWhere( + (element) => element.relatedPerson!.id == event.relatedPersonId); + FamilyBackground familyBackground = + FamilyBackground.fromJson(status['data']); + families.add(familyBackground); + emit(EmergencyContactEditedState(response: status)); + } else { + emit(EmergencyContactEditedState(response: status)); + } + } catch (e) { + emit(FamilyErrorState(message: e.toString())); + } + }); + ////update + on((event, emit) async { + try { + emit(FamilyLoadingState()); + Map status = await FamilyService.instance.update( + family: event.familyBackground, + relationshipId: event.relationshipId, + profileId: event.profileId, + token: event.token); + if (status['success']) { + families.removeWhere((element) => + element.relatedPerson!.id == + event.familyBackground.relatedPerson!.id); + FamilyBackground familyBackground = + FamilyBackground.fromJson(status['data']); + families.add(familyBackground); + emit(FamilyEditedState(response: status)); + } else { + emit(FamilyEditedState(response: status)); + } + } catch (e) { + emit(FamilyErrorState(message: e.toString())); + } + }); + + ////Delete + on((event, emit) async { + try { + final bool success = await FamilyService.instance.delete( + personRelatedId: event.id, + profileId: event.profileId, + token: event.token); + if (success) { + families + .removeWhere((element) => element.relatedPerson!.id == event.id); + emit(DeletedState(success: success)); + } else { + emit(DeletedState(success: success)); + } + } catch (e) { emit(FamilyErrorState(message: e.toString())); } }); diff --git a/lib/bloc/profile/family/family_event.dart b/lib/bloc/profile/family/family_event.dart index 366091c..ba6ff31 100644 --- a/lib/bloc/profile/family/family_event.dart +++ b/lib/bloc/profile/family/family_event.dart @@ -15,3 +15,52 @@ class GetFamilies extends FamilyEvent{ @override List get props => [profileId,token]; } + +class LoadFamily extends FamilyEvent{ + +} + +class ShowEditFamilyForm extends FamilyEvent{ + final FamilyBackground familyBackground; + const ShowEditFamilyForm({required this.familyBackground}); + @override + List get props => [familyBackground]; + +} +class AddFamily extends FamilyEvent{ + final int profileId; + final String token; + final int relationshipId; + final FamilyBackground familyBackground; + const AddFamily({required this.familyBackground, required this.profileId, required this.token, required this.relationshipId}); + @override + List get props => [profileId,token,familyBackground]; +} +class Updatefamily extends FamilyEvent{ + final int profileId; + final String token; + final int relationshipId; + + final FamilyBackground familyBackground; + const Updatefamily({required this.familyBackground, required this.profileId, required this.token, required this.relationshipId,}); + @override + List get props => [profileId,token,familyBackground]; +} + +class DeleteFamily extends FamilyEvent{ + final int id; + final int profileId; + final String token; + const DeleteFamily({required this.id, required this.profileId, required this.token}); +} +class AddEmergencyEvent extends FamilyEvent{ + final int profileId; + final int relatedPersonId; + final int? contactInfoId; + final String token; + final String? numberMail; + final String requestType; + const AddEmergencyEvent({required this.contactInfoId, required this.numberMail, required this.profileId, required this.relatedPersonId, required this.token, required this.requestType}); + @override + List get props => [profileId,token,relatedPersonId]; +} diff --git a/lib/bloc/profile/family/family_state.dart b/lib/bloc/profile/family/family_state.dart index 7ac4003..e7f3df3 100644 --- a/lib/bloc/profile/family/family_state.dart +++ b/lib/bloc/profile/family/family_state.dart @@ -10,13 +10,35 @@ abstract class FamilyState extends Equatable { class FamilyInitial extends FamilyState {} class FamilyLoaded extends FamilyState{ - final List families; + final List? families; const FamilyLoaded({required this.families}); - @override - List get props => [families]; -} +} +class DeletedState extends FamilyState { + final bool success; + const DeletedState({required this.success}); + @override + List get props => [success]; +} +class FamilyAddedState extends FamilyState{ + final Map response; + const FamilyAddedState({required this.response}); + @override + List get props => [response]; +} +class EmergencyContactEditedState extends FamilyState{ + final Map response; + const EmergencyContactEditedState({required this.response}); + @override + List get props => [response]; +} +class FamilyEditedState extends FamilyState{ + final Map response; + const FamilyEditedState({required this.response}); + @override + List get props => [response]; +} class FamilyErrorState extends FamilyState{ final String message; const FamilyErrorState({required this.message}); diff --git a/lib/bloc/profile/primary_information/identification/identification_bloc.dart b/lib/bloc/profile/primary_information/identification/identification_bloc.dart index 2ea178d..d4ee0b8 100644 --- a/lib/bloc/profile/primary_information/identification/identification_bloc.dart +++ b/lib/bloc/profile/primary_information/identification/identification_bloc.dart @@ -1,21 +1,207 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:unit2/bloc/profile/primary_information/contact/contact_bloc.dart'; +import 'package:unit2/screens/profile/components/basic_information/identification/edit_modal.dart'; +import 'package:unit2/sevices/profile/identification_services.dart'; +import '../../../../model/location/city.dart'; +import '../../../../model/location/country.dart'; +import '../../../../model/location/provinces.dart'; +import '../../../../model/location/region.dart'; import '../../../../model/profile/basic_information/identification_information.dart'; +import '../../../../model/utils/agency.dart'; +import '../../../../model/utils/category.dart'; +import '../../../../utils/location_utilities.dart'; +import '../../../../utils/profile_utilities.dart'; part 'identification_event.dart'; part 'identification_state.dart'; -class IdentificationBloc extends Bloc { +class IdentificationBloc + extends Bloc { IdentificationBloc() : super(IdentificationInitial()) { - List identificationInformations = []; + List identificationInformations = []; + List agencies = []; + List addedAgencies = []; + List agencyCategory = []; + List globalCountries = []; + List globalRegions = []; + List provinces = []; + List cities = []; + Region? currentRegion; + Country currentCountry; + Province? currentProvince; + CityMunicipality? currentCity; + ////get on((event, emit) { - try{ + try { identificationInformations = event.identificationInformation; - emit(IdentificationLoadedState(identificationInformation: identificationInformations)); - }catch(e){ + emit(IdentificationLoadedState( + identificationInformation: identificationInformations)); + } catch (e) { emit(IdenficationErrorState(message: e.toString())); } }); + ////load + on((event, emit) { + emit(IdentificationLoadedState( + identificationInformation: identificationInformations)); + }); + //// show add form + on((event, emit) async { + addedAgencies.clear(); + try { + emit(IdentificationLoadingState()); + if (identificationInformations.isNotEmpty) { + for (var element in identificationInformations) { + addedAgencies.add(element.agency!); + } + } + /////AGENCIES------------------------------------------ + if (agencies.isEmpty) { + List newAgencies = + await ProfileUtilities.instance.getAgecies(); + agencies = newAgencies; + } + /////Category Agency------------------------------------------ + if (agencyCategory.isEmpty) { + List categoryAgencies = + await ProfileUtilities.instance.agencyCategory(); + agencyCategory = categoryAgencies; + } + ////regions + if (globalRegions.isEmpty) { + List regions = await LocationUtils.instance.getRegions(); + globalRegions = regions; + } + //// country + if (globalCountries.isEmpty) { + List countries = await LocationUtils.instance.getCountries(); + globalCountries = countries; + } + emit(IdentificationAddingState( + addedAgencies: addedAgencies, + agencyCategory: agencyCategory, + agencies: agencies, + countries: globalCountries, + regions: globalRegions)); + } catch (e) { + emit(IdenficationErrorState(message: e.toString())); + } + }); + ////show edit form + on((event, emit) async { + try { + ////regions + if (globalRegions.isEmpty) { + List regions = await LocationUtils.instance.getRegions(); + globalRegions = regions; + } + //// country + if (globalCountries.isEmpty) { + List countries = await LocationUtils.instance.getCountries(); + globalCountries = countries; + } + currentCountry = globalCountries.firstWhere((Country country) => + event.identification.issuedAt!.country!.code == country.code); + if (!event.overseas) { + currentRegion = globalRegions.firstWhere((Region region) => + event.identification.issuedAt!.cityMunicipality!.province!.region! + .code == + region.code); + provinces = await LocationUtils.instance + .getProvinces(regionCode: currentRegion!.code.toString()); + currentProvince = provinces.firstWhere((Province province) => + event.identification.issuedAt!.cityMunicipality!.province!.code == + province.code); + + cities = await LocationUtils.instance + .getCities(code: currentProvince!.code.toString()); + + currentCity = cities.firstWhere((CityMunicipality cityMunicipality) => + event.identification.issuedAt!.cityMunicipality!.code == + cityMunicipality.code); + } + emit(IdentificationEditingState( + cities: cities, + countries: globalCountries, + currentCity: currentCity, + currentCountry: currentCountry, + currentProvince: currentProvince, + currentRegion: currentRegion, + identification: event.identification, + overseas: event.overseas, + provinces: provinces, + regions: globalRegions)); + } catch (e) { + emit(IdenficationErrorState(message: e.toString())); + } + }); + ////add + on((event, emit) async { + try { + emit(IdentificationLoadingState()); + Map status = await IdentificationServices.instance + .add( + identification: event.identification, + profileId: event.profileId, + token: event.token); + if (status['success']) { + Identification identification = + Identification.fromJson(status['data']); + identificationInformations.add(identification); + emit(IdentificationAddedState(response: status)); + } else { + emit(IdentificationAddedState(response: status)); + } + } catch (e) { + emit(IdenficationErrorState(message: e.toString())); + } + }); + ////update + on((event, emit) async { + try { + emit(IdentificationLoadingState()); + Map status = await IdentificationServices.instance + .update( + identification: event.identification, + profileId: event.profileId, + token: event.token); + if (status['success']) { + identificationInformations + .removeWhere((element) => element.id == event.identification.id); + Identification identification = + Identification.fromJson(status['data']); + identificationInformations.add(identification); + emit(IdentificationEditedState(response: status)); + } else { + emit(IdentificationEditedState(response: status)); + } + } catch (e) { + emit(IdenficationErrorState(message: e.toString())); + } + }); + ////delete + on((event, emit) async { + try { + final bool success = await IdentificationServices.instance.delete( + identificationId: event.identificationId, + token: event.token, + profileId: event.profileId); + if (success) { + identificationInformations.removeWhere( + (Identification element) => element.id == event.identificationId); + emit(IdentificationDeletedState(success: success)); + } else { + emit(IdentificationDeletedState(success: success)); + } + } catch (e) { + emit(IdenficationErrorState(message: e.toString())); + } + }); + ////show error state + on((event, emit) { + emit(IdenficationErrorState(message: event.message)); + }); } } diff --git a/lib/bloc/profile/primary_information/identification/identification_event.dart b/lib/bloc/profile/primary_information/identification/identification_event.dart index 03bbc09..97bfefc 100644 --- a/lib/bloc/profile/primary_information/identification/identification_event.dart +++ b/lib/bloc/profile/primary_information/identification/identification_event.dart @@ -6,10 +6,63 @@ abstract class IdentificationEvent extends Equatable { @override List get props => []; } - +////get class GetIdentifications extends IdentificationEvent{ final List identificationInformation; const GetIdentifications({required this.identificationInformation}); @override List get props => [identificationInformation]; } +////load +class LoadIdentifications extends IdentificationEvent{ + @override + List get props => []; +} + +////show add form +class ShowAddIdentificationForm extends IdentificationEvent{ + +} +//// show edit form +class ShowEditIdentificationForm extends IdentificationEvent{ + final bool overseas; + final Identification identification; + final int profileId; + final String token; + const ShowEditIdentificationForm({required this.identification, required this.profileId, required this.token, required this.overseas}); + @override + List get props => [identification,profileId,token,overseas]; +} +class DeleteIdentification extends IdentificationEvent{ + final int identificationId; + final int profileId; + final String token; + const DeleteIdentification({required this.identificationId, required this.profileId, required this.token}); + +} + +//// add +class AddIdentification extends IdentificationEvent{ + final Identification identification; + final int profileId; + final String token; + const AddIdentification({required this.identification, required this.profileId, required this.token}); + @override + List get props => [identification,profileId,token]; +} + +//// update +class UpdateIdentifaction extends IdentificationEvent{ + final Identification identification; + final int profileId; + final String token; + const UpdateIdentifaction({required this.identification, required this.profileId, required this.token}); + @override + List get props => [identification,profileId,token]; +} + +class ShowErrorState extends IdentificationEvent { + final String message; + const ShowErrorState({required this.message}); +} + diff --git a/lib/bloc/profile/primary_information/identification/identification_state.dart b/lib/bloc/profile/primary_information/identification/identification_state.dart index 0cae35d..71ca194 100644 --- a/lib/bloc/profile/primary_information/identification/identification_state.dart +++ b/lib/bloc/profile/primary_information/identification/identification_state.dart @@ -26,3 +26,49 @@ class IdenficationErrorState extends IdentificationState{ class IdentificationLoadingState extends IdentificationState{ } +class IdentificationDeletedState extends IdentificationState{ + final bool success; + const IdentificationDeletedState({required this.success}); + @override + List get props => [success]; +} + +class IdentificationAddedState extends IdentificationState{ + final Map response; + const IdentificationAddedState({required this.response}); + @override + List get props => [response]; +} + +class IdentificationEditedState extends IdentificationState{ + final Map response; + const IdentificationEditedState({required this.response}); + @override + List get props => [response]; +} + +class IdentificationAddingState extends IdentificationState{ + final List agencies; + final List addedAgencies; + final List countries; + final List regions; + final List agencyCategory; + const IdentificationAddingState({required this.agencies, required this.countries, required this.regions,required this.agencyCategory, required this.addedAgencies}); + @override + List get props => [agencies,countries,regions,agencyCategory]; +} + +class IdentificationEditingState extends IdentificationState{ + final Identification identification; + final List countries; + final List regions; + final List provinces; + final List cities; + final Region? currentRegion; + final Country currentCountry; + final Province? currentProvince; + final CityMunicipality? currentCity; + final bool overseas; + const IdentificationEditingState({required this.cities,required this.countries, required this.currentCity, required this.currentCountry, required this.currentProvince, required this.currentRegion, required this.identification, required this.overseas, required this.provinces, required this.regions}); + +} diff --git a/lib/model/profile/family_backround.dart b/lib/model/profile/family_backround.dart index 68275a1..9d0d443 100644 --- a/lib/model/profile/family_backround.dart +++ b/lib/model/profile/family_backround.dart @@ -8,105 +8,122 @@ import 'dart:ffi'; import '../utils/category.dart'; import '../utils/position.dart'; -FamilyBackground familyBackgroundFromJson(String str) => FamilyBackground.fromJson(json.decode(str)); +FamilyBackground familyBackgroundFromJson(String str) => + FamilyBackground.fromJson(json.decode(str)); -String familyBackgroundToJson(FamilyBackground data) => json.encode(data.toJson()); +String familyBackgroundToJson(FamilyBackground data) => + json.encode(data.toJson()); class FamilyBackground { - FamilyBackground({ - this.company, - this.position, - this.relationship, - this.relatedPerson, - this.companyAddress, - this.emergencyContact, - this.incaseOfEmergency, - this.companyContactNumber, - }); + FamilyBackground({ + this.company, + this.position, + this.relationship, + this.relatedPerson, + this.companyAddress, + this.emergencyContact, + this.incaseOfEmergency, + this.companyContactNumber, + }); - final Company? company; - final Position? position; - final Relationship? relationship; - final RelatedPerson? relatedPerson; - final String? companyAddress; - final List? emergencyContact; - final bool? incaseOfEmergency; - final String? companyContactNumber; + final Company? company; + final Position? position; + final Relationship? relationship; + final RelatedPerson? relatedPerson; + final String? companyAddress; + final List? emergencyContact; + final bool? incaseOfEmergency; + final String? companyContactNumber; - factory FamilyBackground.fromJson(Map json) => FamilyBackground( - company: json["company"] == null ? null : Company.fromJson(json["company"]), - position: json["position"] == null ? null : Position.fromJson(json["position"]), - relationship: json["relationship"] == null ? null : Relationship.fromJson(json["relationship"]), - relatedPerson: json["related_person"] == null ? null : RelatedPerson.fromJson(json["related_person"]), + factory FamilyBackground.fromJson(Map json) => + FamilyBackground( + company: + json["company"] == null ? null : Company.fromJson(json["company"]), + position: json["position"] == null + ? null + : Position.fromJson(json["position"]), + relationship: json["relationship"] == null + ? null + : Relationship.fromJson(json["relationship"]), + relatedPerson: json["related_person"] == null + ? null + : RelatedPerson.fromJson(json["related_person"]), companyAddress: json["company_address"], - emergencyContact: json["emergency_contact"] == null ? [] : List.from(json["emergency_contact"]!.map((x) => EmergencyContact.fromJson(x))), + emergencyContact: json["emergency_contact"] == null + ? [] + : List.from(json["emergency_contact"]! + .map((x) => EmergencyContact.fromJson(x))), incaseOfEmergency: json["incase_of_emergency"], companyContactNumber: json["company_contact_number"], - ); + ); - Map toJson() => { + Map toJson() => { "company": company?.toJson(), "position": position?.toJson(), "relationship": relationship?.toJson(), "related_person": relatedPerson?.toJson(), "company_address": companyAddress, - "emergency_contact": emergencyContact == null ? [] : List.from(emergencyContact!.map((x) => x.toJson())), + "emergency_contact": emergencyContact == null + ? [] + : List.from(emergencyContact!.map((x) => x.toJson())), "incase_of_emergency": incaseOfEmergency, "company_contact_number": companyContactNumber, - }; + }; } class Company { - Company({ - this.id, - this.name, - this.category, - this.privateEntity, - }); + Company({ + this.id, + this.name, + this.category, + this.privateEntity, + }); - final int? id; - final String? name; - final Category? category; - final bool? privateEntity; + final int? id; + final String? name; + final Category? category; + final bool? privateEntity; - factory Company.fromJson(Map json) => Company( + factory Company.fromJson(Map json) => Company( id: json["id"], name: json["name"], - category: json["category"] == null ? null : Category.fromJson(json["category"]), + category: json["category"] == null + ? null + : Category.fromJson(json["category"]), privateEntity: json["private_entity"], - ); + ); - Map toJson() => { + Map toJson() => { "id": id, "name": name, "category": category?.toJson(), "private_entity": privateEntity, - }; + }; } - class EmergencyContact { - EmergencyContact({ - this.telco, - this.isactive, - this.provider, - this.isprimary, - this.numbermail, - this.serviceType, - this.contactinfoid, - this.commServiceId, - }); + EmergencyContact({ + this.telco, + this.isactive, + this.provider, + this.isprimary, + this.numbermail, + this.serviceType, + this.contactinfoid, + this.commServiceId, + }); - final String? telco; - final bool? isactive; - final int? provider; - final bool? isprimary; - final String? numbermail; - final int? serviceType; - final int? contactinfoid; - final int? commServiceId; + final String? telco; + final bool? isactive; + final int? provider; + final bool? isprimary; + final String? numbermail; + final int? serviceType; + final int? contactinfoid; + final int? commServiceId; - factory EmergencyContact.fromJson(Map json) => EmergencyContact( + factory EmergencyContact.fromJson(Map json) => + EmergencyContact( telco: json["telco"], isactive: json["isactive"], provider: json["provider"], @@ -115,9 +132,9 @@ class EmergencyContact { serviceType: json["service_type"], contactinfoid: json["contactinfoid"], commServiceId: json["comm_service_id"], - ); + ); - Map toJson() => { + Map toJson() => { "telco": telco, "isactive": isactive, "provider": provider, @@ -126,69 +143,76 @@ class EmergencyContact { "service_type": serviceType, "contactinfoid": contactinfoid, "comm_service_id": commServiceId, - }; + }; } - class RelatedPerson { - RelatedPerson({ - this.id, - this.sex, - this.gender, - this.deceased, - this.heightM, - this.birthdate, - this.esigPath, - this.lastName, - this.weightKg, - this.bloodType, - this.firstName, - this.photoPath, - this.maidenName, - this.middleName, - this.uuidQrcode, - this.civilStatus, - this.titlePrefix, - this.titleSuffix, - this.showTitleId, - this.nameExtension, - }); + RelatedPerson({ + required this.id, + required this.gender, + required this.sex, + required this.deceased, + required this.heightM, + required this.birthdate, + required this.esigPath, + required this.lastName, + required this.weightKg, + required this.bloodType, + required this.firstName, + required this.photoPath, + required this.maidenName, + required this.middleName, + required this.uuidQrcode, + required this.civilStatus, + required this.titlePrefix, + required this.titleSuffix, + required this.showTitleId, + required this.nameExtension, + }); - final int? id; - final String? sex; - final String? gender; - final bool? deceased; - final double? heightM; - final DateTime? birthdate; - final String? esigPath; - final String? lastName; - final double? weightKg; - final String? bloodType; - final String? firstName; - final String? photoPath; - final dynamic maidenName; - final String? middleName; - final String? uuidQrcode; - final String? civilStatus; - final String? titlePrefix; - final String? titleSuffix; - final bool? showTitleId; - final String? nameExtension; + final int? id; + final String? sex; + final bool? deceased; + final String? gender; + final double? heightM; + final DateTime? birthdate; + final String? esigPath; + final String? lastName; + final double? weightKg; + final String? bloodType; + final String? firstName; + final String? photoPath; + final MaidenName? maidenName; + final String? middleName; + final String? uuidQrcode; + final String? civilStatus; + final String? titlePrefix; + final String? titleSuffix; + final bool? showTitleId; + final String? nameExtension; - factory RelatedPerson.fromJson(Map json) => RelatedPerson( + factory RelatedPerson.fromJson(Map json) => RelatedPerson( id: json["id"], sex: json["sex"], gender: json["gender"], deceased: json["deceased"], - heightM: json["height_m"] == null?null:double.parse(json["height_m"].toString()), - birthdate: json["birthdate"] == null ? null : DateTime.parse(json["birthdate"]), + heightM: json["height_m"] == null + ? null + : double.parse(json["height_m"].toString()), + birthdate: json["birthdate"] == null + ? null + : DateTime.parse(json["birthdate"]), esigPath: json["esig_path"], lastName: json["last_name"], - weightKg: json["weight_kg"] == null? null:double.parse(json["weight_kg"].toString()) , + weightKg: json["weight_kg"] == null + ? null + : double.parse(json["weight_kg"].toString()), bloodType: json["blood_type"], firstName: json["first_name"], photoPath: json["photo_path"], - maidenName: json["maiden_name"], + maidenName: json["maiden_name"] == null + ? null + : MaidenName.fromJson(json['maiden_name']), middleName: json["middle_name"], uuidQrcode: json["uuid_qrcode"], civilStatus: json["civil_status"], @@ -196,15 +220,16 @@ class RelatedPerson { titleSuffix: json["title_suffix"], showTitleId: json["show_title_id"], nameExtension: json["name_extension"], - ); + ); - Map toJson() => { + Map toJson() => { "id": id, "sex": sex, "gender": gender, "deceased": deceased, "height_m": heightM, - "birthdate": "${birthdate!.year.toString().padLeft(4, '0')}-${birthdate!.month.toString().padLeft(2, '0')}-${birthdate!.day.toString().padLeft(2, '0')}", + "birthdate": + "${birthdate!.year.toString().padLeft(4, '0')}-${birthdate!.month.toString().padLeft(2, '0')}-${birthdate!.day.toString().padLeft(2, '0')}", "esig_path": esigPath, "last_name": lastName, "weight_kg": weightKg, @@ -219,29 +244,49 @@ class RelatedPerson { "title_suffix": titleSuffix, "show_title_id": showTitleId, "name_extension": nameExtension, - }; + }; } class Relationship { - Relationship({ - this.id, - this.type, - this.category, - }); + Relationship({ + this.id, + this.type, + this.category, + }); - final int? id; - final String? type; - final String? category; + final int? id; + final String? type; + final String? category; - factory Relationship.fromJson(Map json) => Relationship( + factory Relationship.fromJson(Map json) => Relationship( id: json["id"], type: json["type"], category: json["category"], - ); + ); - Map toJson() => { + Map toJson() => { "id": id, "type": type, "category": category, - }; + }; +} + +class MaidenName { + final String lastName; + final String middleName; + + MaidenName({ + required this.lastName, + required this.middleName, + }); + + factory MaidenName.fromJson(Map json) => MaidenName( + lastName: json["last_name"], + middleName: json["middle_name"], + ); + + Map toJson() => { + "last_name": lastName, + "middle_name": middleName, + }; } diff --git a/lib/screens/profile/components/basic_information/contact_information/add_modal.dart b/lib/screens/profile/components/basic_information/contact_information/add_modal.dart index 40c2494..7dc10ff 100644 --- a/lib/screens/profile/components/basic_information/contact_information/add_modal.dart +++ b/lib/screens/profile/components/basic_information/contact_information/add_modal.dart @@ -15,6 +15,7 @@ import 'package:unit2/theme-data.dart/form-style.dart'; import 'package:unit2/utils/text_container.dart'; import '../../../../../theme-data.dart/colors.dart'; +import '../../../../../utils/formatters.dart'; class AddContactInformationScreen extends StatefulWidget { final int profileId; @@ -37,17 +38,7 @@ class _AddContactInformationScreenState bool primaryaContact = false; bool active = false; String? numberMail; - var mobileFormatter = MaskTextInputFormatter( - mask: "+63 (###) ###-####", - filter: {"#": RegExp(r"^[1-9][0-9]*$")}, - type: MaskAutoCompletionType.lazy, - initialText: "0"); - var landLineFormatter = MaskTextInputFormatter( - mask: "(###) ###-###", - filter: {"#": RegExp(r"^[0-9]")}, - type: MaskAutoCompletionType.lazy, - initialText: "0"); @override Widget build(BuildContext context) { diff --git a/lib/screens/profile/components/basic_information/family/add_mobile_modal.dart b/lib/screens/profile/components/basic_information/family/add_mobile_modal.dart new file mode 100644 index 0000000..21b1560 --- /dev/null +++ b/lib/screens/profile/components/basic_information/family/add_mobile_modal.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; + +import '../../../../../bloc/profile/family/family_bloc.dart'; +import '../../../../../theme-data.dart/btn-style.dart'; +import '../../../../../theme-data.dart/colors.dart'; +import '../../../../../theme-data.dart/form-style.dart'; +import '../../../../../utils/formatters.dart'; + +class AddMobileNumber extends StatelessWidget { + final Function onPressed; + final GlobalKey formKey; + const AddMobileNumber({super.key, required this.onPressed,required this.formKey}); + + @override + + Widget build(BuildContext context) { + return AlertDialog( + title: const Text("Emergency Contact Information"), + content: FormBuilder( + key: formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + FormBuilderTextField( + name: 'number_mail', + inputFormatters: [mobileFormatter], + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: normalTextFieldStyle( + "Mobile number *", "+63 (9xx) xxx - xxxx"), + ), + const SizedBox( + height: 20, + ), + SizedBox( + width: 200, + height: 50, + child: ElevatedButton( + style: mainBtnStyle(primary, Colors.transparent, second), + child: const Text("Submit"), + onPressed: () { + if (formKey.currentState!.saveAndValidate()) { + onPressed(); + } + }, + ), + ) + ], + ), + ), + ); + } +} diff --git a/lib/screens/profile/components/basic_information/family/child_add_modal.dart b/lib/screens/profile/components/basic_information/family/child_add_modal.dart new file mode 100644 index 0000000..d54a375 --- /dev/null +++ b/lib/screens/profile/components/basic_information/family/child_add_modal.dart @@ -0,0 +1,339 @@ +import 'package:date_time_picker/date_time_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; + +import '../../../../../bloc/profile/family/family_bloc.dart'; +import '../../../../../model/profile/family_backround.dart'; +import '../../../../../model/utils/position.dart'; +import '../../../../../theme-data.dart/btn-style.dart'; +import '../../../../../theme-data.dart/colors.dart'; +import '../../../../../theme-data.dart/form-style.dart'; +import '../../../../../utils/global.dart'; +import '../../../../../utils/text_container.dart'; +import '../../../../../utils/validators.dart'; + +class ChildAlert extends StatefulWidget { + final List nameExtensions; + final List gender; + final List sexes; + final List bloodType; + final List civilStatus; + final String token; + final int profileId; + final FamilyBloc familyBloc; + const ChildAlert( + {super.key, + required this.bloodType, + required this.civilStatus, + required this.gender, + required this.nameExtensions, + required this.sexes,required this.familyBloc, required this.profileId, required this.token}); + + @override + State createState() => _ChildAlertState(); +} + +class _ChildAlertState extends State { + final _formKey = GlobalKey(); + final bdayController = TextEditingController(); + + ////selected + String? selectedExtension; + String? selectedGender; + String? selectedSex; + String? selectedBloodType; + String? selectedCivilStatus; + bool deceased = false; + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text("Family - Child"), + contentPadding: const EdgeInsets.all(24), + content: SingleChildScrollView( + child: FormBuilder( + key: _formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ////name + FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "lastname", + decoration: normalTextFieldStyle("Last name", ""), + ), + const SizedBox( + height: 8, + ), + //// firstname + FormBuilderTextField( + name: "firstname", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: normalTextFieldStyle("First name", ""), + ), + const SizedBox( + height: 8, + ), + ////middle name + FormBuilderTextField( + name: "middlename", + decoration: normalTextFieldStyle("Middle name", ""), + ), + + const SizedBox( + height: 8, + ), + //// extension + FormBuilderDropdown( + decoration: normalTextFieldStyle("Name extension", ""), + name: "extension", + items: widget.nameExtensions + .map((element) => DropdownMenuItem( + value: element, + child: Text(element), + )) + .toList(), + onChanged: (e) { + selectedExtension = e; + }, + ), + + const SizedBox( + height: 8, + ), + + ////Bday + DateTimePicker( + controller: bdayController, + use24HourFormat: false, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + timeHintText: "Birthdate", + decoration: + normalTextFieldStyle("Birthdate *", "*").copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + firstDate: DateTime(1970), + lastDate: DateTime(2100), + icon: const Icon(Icons.date_range), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + //// sex + child: FormBuilderDropdown( + decoration: normalTextFieldStyle("Sex", ""), + name: "sex", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + items: widget.sexes + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedSex = e; + }, + ), + ), + const SizedBox( + width: 8, + ), + ////gender + Flexible( + flex: 1, + child: FormBuilderDropdown( + decoration: normalTextFieldStyle("Gender", ""), + name: "gender", + items: widget.gender + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedGender = e; + }, + ), + ), + ]), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + ////Blood Type + Flexible( + flex: 1, + child: FormBuilderDropdown( + decoration: normalTextFieldStyle("Blood type", ""), + name: "bloodtype", + items: widget.bloodType + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedBloodType = e; + }, + ), + ), + const SizedBox( + width: 8, + ), + //// Civil Status + Flexible( + flex: 1, + child: FormBuilderDropdown( + decoration: normalTextFieldStyle("Civil status", ""), + name: "extension", + items: widget.civilStatus + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedCivilStatus = e; + }, + ), + ), + ]), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + ////height + child: FormBuilderTextField( + name: "height", + validator: integerAndNumeric, + keyboardType: TextInputType.number, + decoration: normalTextFieldStyle("Height", ""), + ), + ), + const SizedBox( + width: 8, + ), + Flexible( + flex: 1, + //// weight + child: FormBuilderTextField( + name: "weight", + validator: integerAndNumeric, + keyboardType: TextInputType.number, + decoration: normalTextFieldStyle("Weight", ""), + ), + ), + const SizedBox( + height: 8, + ), + ]), + ), + const SizedBox( + height: 8, + ), + ////Deceased + SizedBox( + width: screenWidth, + child: StatefulBuilder(builder: (context, setState) { + return FormBuilderSwitch( + initialValue: deceased, + title: Text(deceased ? "YES" : "NO"), + decoration: normalTextFieldStyle("Deceased?", ""), + ////onvhange private sector + onChanged: (value) { + setState(() { + deceased = value!; + }); + }, + + name: 'deceased', + validator: FormBuilderValidators.required(), + ); + }), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + height: 50, + child: ElevatedButton( + style: mainBtnStyle(second, Colors.transparent, primary), + onPressed: () { + if (_formKey.currentState!.saveAndValidate()) { + String fname = + _formKey.currentState!.value['firstname']; + String lastName = + _formKey.currentState!.value['lastname']; + String? mname = + _formKey.currentState?.value['middlename']; + String bday = bdayController.text; + String? gender = selectedGender =="NONE"?null:selectedGender; + String? extension = selectedExtension=="NONE"?null:selectedExtension; + String? blood = selectedBloodType =="NONE"?null:selectedBloodType; + String? civilStatus = selectedCivilStatus =="NONE"?null:selectedCivilStatus; + String? sex = selectedSex; + Company? company; + Position? position; + double? height = + _formKey.currentState?.value['height']==null? null: + double.parse( + _formKey.currentState?.value['height']); + double? weight = + _formKey.currentState?.value['weight']==null?null: + double.parse( + _formKey.currentState?.value['weight']); + Relationship relationship = Relationship( + id: 1, + type: "Paternal_Parent", + category: "Family"); + List? emergnecyContacts; + bool incaseOfEmergency = false; + String? companyAddress; + RelatedPerson person = RelatedPerson( + titlePrefix: null, + firstName: fname, + maidenName: null, + middleName: mname, + lastName: lastName, + birthdate: DateTime.parse(bday), + id: null, + sex: sex, + gender: gender, + deceased: false, + heightM: height, + weightKg: weight, + esigPath: null, + bloodType: blood, + photoPath: null, + uuidQrcode: null, + nameExtension: extension, + civilStatus: civilStatus, + titleSuffix: null, + showTitleId: false, + ); + FamilyBackground familyBackground = + FamilyBackground(company: company,position: position,relatedPerson: person,relationship: relationship,companyAddress: companyAddress,emergencyContact: emergnecyContacts,incaseOfEmergency: incaseOfEmergency,companyContactNumber: null); + Navigator.of(context).pop(); + + widget.familyBloc.add(AddFamily(familyBackground: familyBackground, profileId: widget.profileId, token: widget.token,relationshipId: 4)); + } + }, + child: const Text(submit)), + ), + ], + )), + )); + } +} \ No newline at end of file diff --git a/lib/screens/profile/components/basic_information/family/child_edit_modal.dart b/lib/screens/profile/components/basic_information/family/child_edit_modal.dart new file mode 100644 index 0000000..efd6bac --- /dev/null +++ b/lib/screens/profile/components/basic_information/family/child_edit_modal.dart @@ -0,0 +1,362 @@ +import 'package:date_time_picker/date_time_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:unit2/bloc/profile/family/family_bloc.dart'; +import 'package:unit2/model/profile/family_backround.dart'; + +import '../../../../../model/utils/position.dart'; +import '../../../../../theme-data.dart/btn-style.dart'; +import '../../../../../theme-data.dart/colors.dart'; +import '../../../../../theme-data.dart/form-style.dart'; +import '../../../../../utils/global.dart'; +import '../../../../../utils/text_container.dart'; +import '../../../../../utils/validators.dart'; + +class ChildEditAlert extends StatefulWidget { + final FamilyBackground familyBackground; + final List nameExtensions; + final List gender; + final List sexes; + final List bloodType; + final List civilStatus; + final String token; + final int profileId; + final FamilyBloc familyBloc; + + const ChildEditAlert( + {super.key, + required this.familyBackground, + required this.bloodType, + required this.civilStatus, + required this.gender, + required this.nameExtensions, + required this.sexes, required this.familyBloc, required this.profileId, required this.token}); + + @override + State createState() => _ChildEditAlertState(); +} + +class _ChildEditAlertState extends State { + final _formKey = GlobalKey(); + final bdayController = TextEditingController(); + + ////selected + String? selectedExtension; + String? selectedGender; + String? selectedSex; + String? selectedBloodType; + String? selectedCivilStatus; + bool deceased = false; + @override + Widget build(BuildContext context) { + selectedExtension = widget.familyBackground.relatedPerson?.nameExtension; + selectedGender = widget.familyBackground.relatedPerson?.gender; + selectedSex = widget.familyBackground.relatedPerson?.sex; + selectedBloodType = widget.familyBackground.relatedPerson?.bloodType; + selectedCivilStatus = widget.familyBackground.relatedPerson?.civilStatus; + bdayController.text = widget.familyBackground.relatedPerson!.birthdate!.toString(); + deceased = widget.familyBackground.relatedPerson!.deceased!; + return AlertDialog( + title: const Text("Family - Parental Parent"), + contentPadding: const EdgeInsets.all(24), + content: SingleChildScrollView( + child: FormBuilder( + key: _formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ////name + FormBuilderTextField( + initialValue: widget.familyBackground.relatedPerson!.lastName, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "lastname", + decoration: normalTextFieldStyle("Last name", ""), + ), + const SizedBox( + height: 8, + ), + //// firstname + FormBuilderTextField( + initialValue: widget.familyBackground.relatedPerson!.firstName, + name: "firstname", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: normalTextFieldStyle("First name", ""), + ), + const SizedBox( + height: 8, + ), + ////middle name + FormBuilderTextField( + initialValue: widget.familyBackground.relatedPerson?.middleName, + name: "middlename", + + decoration: normalTextFieldStyle("Middle name", ""), + ), + + const SizedBox( + height: 8, + ), + //// extension + DropdownButtonFormField( + value: selectedExtension, + decoration: normalTextFieldStyle("Name extension", ""), + + items: widget.nameExtensions + .map((element) => DropdownMenuItem( + value: element, + child: Text(element), + )) + .toList(), + onChanged: (e) { + selectedExtension = e; + }, + ), + + const SizedBox( + height: 8, + ), + ////Bday + DateTimePicker( + controller: bdayController, + use24HourFormat: false, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + timeHintText: "Birthdate", + decoration: + normalTextFieldStyle("Birthdate *", "*").copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + firstDate: DateTime(1970), + lastDate: DateTime(2100), + icon: const Icon(Icons.date_range), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + //// sex + child: DropdownButtonFormField( + decoration: normalTextFieldStyle("Sex", ""), + value: selectedSex, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + items: widget.sexes + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedSex = e; + }, + ), + ), + const SizedBox( + width: 8, + ), + ////gender + Flexible( + flex: 1, + child: DropdownButtonFormField( + isExpanded: true, + decoration: normalTextFieldStyle("Gender", ""), + value: selectedGender, + items: widget.gender + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedGender = e; + }, + ), + ), + ]), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + ////Blood Type + Flexible( + flex: 1, + child: DropdownButtonFormField( + isExpanded: true, + decoration: normalTextFieldStyle("Blood type", ""), + value: selectedBloodType, + items: widget.bloodType + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedBloodType = e; + }, + ), + ), + const SizedBox( + width: 8, + ), + //// Civil Status + Flexible( + flex: 1, + child: DropdownButtonFormField( + isExpanded: true, + decoration: normalTextFieldStyle("Civil status", ""), + value: selectedCivilStatus, + items: widget.civilStatus + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedCivilStatus = e; + }, + ), + ), + ]), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + ////height + child: FormBuilderTextField( + initialValue: widget.familyBackground.relatedPerson?.heightM == null? null:widget.familyBackground.relatedPerson?.heightM.toString(), + name: "height", + validator: integerAndNumeric, + keyboardType: TextInputType.number, + decoration: normalTextFieldStyle("Height", ""), + ), + ), + const SizedBox( + width: 8, + ), + Flexible( + flex: 1, + //// weight + child: FormBuilderTextField( + initialValue: widget.familyBackground.relatedPerson?.weightKg == null? null:widget.familyBackground.relatedPerson?.weightKg.toString(), + name: "weight", + validator: integerAndNumeric, + keyboardType: TextInputType.number, + decoration: normalTextFieldStyle("Weight", ""), + ), + ), + const SizedBox( + height: 8, + ), + ]), + ), + const SizedBox( + height: 8, + ), + ////Deceased + SizedBox( + width: screenWidth, + child: StatefulBuilder(builder: (context, setState) { + return FormBuilderSwitch( + initialValue: deceased, + title: Text(deceased ? "YES" : "NO"), + decoration: normalTextFieldStyle("Deceased?", ""), + + onChanged: (value) { + setState(() { + deceased = value!; + }); + }, + + name: 'deceased', + validator: FormBuilderValidators.required(), + ); + }), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + height: 50, + child: ElevatedButton( + style: + mainBtnStyle(second, Colors.transparent, primary), + onPressed: () { + if (_formKey.currentState!.saveAndValidate()) { + String fname = + _formKey.currentState!.value['firstname']; + String lastName = + _formKey.currentState!.value['lastname']; + String? mname = + _formKey.currentState?.value['middlename']; + String bday = bdayController.text; + String? gender = selectedGender =="NONE"?null:selectedGender; + String? extension = selectedExtension=="NONE"?null:selectedExtension; + String? blood = selectedBloodType =="NONE"?null:selectedBloodType; + String? civilStatus = selectedCivilStatus =="NONE"?null:selectedCivilStatus; + String? sex = selectedSex; + Company? company; + Position? position; + double? height = + _formKey.currentState?.value['height']==null? null: + double.parse( + _formKey.currentState?.value['height']); + double? weight = + _formKey.currentState?.value['weight']==null?null: + double.parse( + _formKey.currentState?.value['weight']); + Relationship relationship = Relationship( + id: 1, + type: "Paternal_Parent", + category: "Family"); + List? emergnecyContacts; + bool incaseOfEmergency = false; + String? companyAddress; + RelatedPerson person = RelatedPerson( + titlePrefix: null, + firstName: fname, + maidenName: null, + middleName: mname, + lastName: lastName, + birthdate: DateTime.parse(bday), + id: widget.familyBackground.relatedPerson!.id, + sex: sex, + gender: gender, + deceased: deceased, + heightM: height, + weightKg: weight, + esigPath: null, + bloodType: blood, + photoPath: null, + uuidQrcode: null, + nameExtension: extension, + civilStatus: civilStatus, + titleSuffix: null, + showTitleId: false, + ); + FamilyBackground familyBackground = + FamilyBackground(company: company,position: position,relatedPerson: person,relationship: relationship,companyAddress: companyAddress,emergencyContact: emergnecyContacts,incaseOfEmergency: incaseOfEmergency,companyContactNumber: null); + Navigator.of(context).pop(); + + widget.familyBloc.add(Updatefamily(familyBackground: familyBackground, profileId: widget.profileId, token: widget.token,relationshipId: 4)); + } + }, + child: const Text(submit)), + ), + ], + )), + )); + } +} diff --git a/lib/screens/profile/components/basic_information/family/father_add_modal.dart b/lib/screens/profile/components/basic_information/family/father_add_modal.dart new file mode 100644 index 0000000..da8c4e9 --- /dev/null +++ b/lib/screens/profile/components/basic_information/family/father_add_modal.dart @@ -0,0 +1,344 @@ +import 'package:date_time_picker/date_time_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:unit2/bloc/profile/family/family_bloc.dart'; +import 'package:unit2/model/profile/family_backround.dart'; + +import '../../../../../model/utils/position.dart'; +import '../../../../../theme-data.dart/btn-style.dart'; +import '../../../../../theme-data.dart/colors.dart'; +import '../../../../../theme-data.dart/form-style.dart'; +import '../../../../../utils/global.dart'; +import '../../../../../utils/text_container.dart'; +import '../../../../../utils/validators.dart'; + +class FatherAlert extends StatefulWidget { + final List nameExtensions; + final List gender; + final List sexes; + final List bloodType; + final List civilStatus; + final String token; + final int profileId; + final FamilyBloc familyBloc; + + const FatherAlert( + {super.key, + required this.bloodType, + required this.civilStatus, + required this.gender, + required this.nameExtensions, + required this.sexes, required this.familyBloc, required this.profileId, required this.token}); + + @override + State createState() => _FatherAlertState(); +} + +class _FatherAlertState extends State { + final _formKey = GlobalKey(); + final bdayController = TextEditingController(); + + ////selected + String? selectedExtension; + String? selectedGender; + String? selectedSex; + String? selectedBloodType; + String? selectedCivilStatus; + bool deceased = false; + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text("Family - Parental Parent"), + contentPadding: const EdgeInsets.all(24), + content: SingleChildScrollView( + child: FormBuilder( + key: _formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ////name + FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "lastname", + decoration: normalTextFieldStyle("Last name", ""), + ), + const SizedBox( + height: 8, + ), + //// firstname + FormBuilderTextField( + name: "firstname", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: normalTextFieldStyle("First name", ""), + ), + const SizedBox( + height: 8, + ), + ////middle name + FormBuilderTextField( + name: "middlename", + + decoration: normalTextFieldStyle("Middle name", ""), + ), + + const SizedBox( + height: 8, + ), + //// extension + FormBuilderDropdown( + decoration: normalTextFieldStyle("Name extension", ""), + name: "extension", + items: widget.nameExtensions + .map((element) => DropdownMenuItem( + value: element, + child: Text(element), + )) + .toList(), + onChanged: (e) { + selectedExtension = e; + }, + ), + + const SizedBox( + height: 8, + ), + ////Bday + DateTimePicker( + controller: bdayController, + use24HourFormat: false, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + timeHintText: "Birthdate", + decoration: + normalTextFieldStyle("Birthdate *", "*").copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + firstDate: DateTime(1970), + lastDate: DateTime(2100), + icon: const Icon(Icons.date_range), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + //// sex + child: FormBuilderDropdown( + decoration: normalTextFieldStyle("Sex", ""), + name: "sex", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + items: widget.sexes + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedSex = e; + }, + ), + ), + const SizedBox( + width: 8, + ), + ////gender + Flexible( + flex: 1, + child: FormBuilderDropdown( + decoration: normalTextFieldStyle("Gender", ""), + name: "gender", + items: widget.gender + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedGender = e; + }, + ), + ), + ]), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + ////Blood Type + Flexible( + flex: 1, + child: FormBuilderDropdown( + decoration: normalTextFieldStyle("Blood type", ""), + name: "bloodtype", + items: widget.bloodType + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedBloodType = e; + }, + ), + ), + const SizedBox( + width: 8, + ), + //// Civil Status + Flexible( + flex: 1, + child: FormBuilderDropdown( + decoration: normalTextFieldStyle("Civil status", ""), + name: "extension", + items: widget.civilStatus + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedCivilStatus = e; + }, + ), + ), + ]), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + ////height + child: FormBuilderTextField( + name: "height", + validator: integerAndNumeric, + keyboardType: TextInputType.number, + decoration: normalTextFieldStyle("Height", ""), + ), + ), + const SizedBox( + width: 8, + ), + Flexible( + flex: 1, + //// weight + child: FormBuilderTextField( + name: "weight", + validator: integerAndNumeric, + keyboardType: TextInputType.number, + decoration: normalTextFieldStyle("Weight", ""), + ), + ), + const SizedBox( + height: 8, + ), + ]), + ), + const SizedBox( + height: 8, + ), + ////Deceased + SizedBox( + width: screenWidth, + child: StatefulBuilder(builder: (context, setState) { + return FormBuilderSwitch( + initialValue: deceased, + title: Text(deceased ? "YES" : "NO"), + decoration: normalTextFieldStyle("Deceased?", ""), + ////onvhange private sector + onChanged: (value) { + setState(() { + deceased = value!; + }); + }, + + name: 'deceased', + validator: FormBuilderValidators.required(), + ); + }), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + height: 50, + child: ElevatedButton( + style: + mainBtnStyle(second, Colors.transparent, primary), + onPressed: () { + if (_formKey.currentState!.saveAndValidate()) { + String fname = + _formKey.currentState!.value['firstname']; + String lastName = + _formKey.currentState!.value['lastname']; + String? mname = + _formKey.currentState?.value['middlename']; + String bday = bdayController.text; + String? gender = selectedGender =="NONE"?null:selectedGender; + String? extension = selectedExtension=="NONE"?null:selectedExtension; + String? blood = selectedBloodType =="NONE"?null:selectedBloodType; + String? civilStatus = selectedCivilStatus =="NONE"?null:selectedCivilStatus; + String? sex = selectedSex; + Company? company; + Position? position; + double? height = + _formKey.currentState?.value['height']==null? null: + double.parse( + _formKey.currentState?.value['height']); + double? weight = + _formKey.currentState?.value['weight']==null?null: + double.parse( + _formKey.currentState?.value['weight']); + Relationship relationship = Relationship( + id: 1, + type: "Paternal_Parent", + category: "Family"); + List? emergnecyContacts; + bool incaseOfEmergency = false; + String? companyAddress; + RelatedPerson person = RelatedPerson( + titlePrefix: null, + firstName: fname, + maidenName: null, + middleName: mname, + lastName: lastName, + birthdate: DateTime.parse(bday), + id: null, + sex: sex, + gender: gender, + deceased: deceased, + heightM: height, + weightKg: weight, + esigPath: null, + bloodType: blood, + photoPath: null, + uuidQrcode: null, + nameExtension: extension, + civilStatus: civilStatus, + titleSuffix: null, + showTitleId: false, + ); + FamilyBackground familyBackground = + FamilyBackground(company: company,position: position,relatedPerson: person,relationship: relationship,companyAddress: companyAddress,emergencyContact: emergnecyContacts,incaseOfEmergency: incaseOfEmergency,companyContactNumber: null); + Navigator.of(context).pop(); + + widget.familyBloc.add(AddFamily(familyBackground: familyBackground, profileId: widget.profileId, token: widget.token,relationshipId: 1)); + } + }, + child: const Text(submit)), + ), + ], + )), + )); + } +} diff --git a/lib/screens/profile/components/basic_information/family/father_edit_modal.dart b/lib/screens/profile/components/basic_information/family/father_edit_modal.dart new file mode 100644 index 0000000..ff95fe3 --- /dev/null +++ b/lib/screens/profile/components/basic_information/family/father_edit_modal.dart @@ -0,0 +1,362 @@ +import 'package:date_time_picker/date_time_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:unit2/bloc/profile/family/family_bloc.dart'; +import 'package:unit2/model/profile/family_backround.dart'; + +import '../../../../../model/utils/position.dart'; +import '../../../../../theme-data.dart/btn-style.dart'; +import '../../../../../theme-data.dart/colors.dart'; +import '../../../../../theme-data.dart/form-style.dart'; +import '../../../../../utils/global.dart'; +import '../../../../../utils/text_container.dart'; +import '../../../../../utils/validators.dart'; + +class FatherEditAlert extends StatefulWidget { + final FamilyBackground familyBackground; + final List nameExtensions; + final List gender; + final List sexes; + final List bloodType; + final List civilStatus; + final String token; + final int profileId; + final FamilyBloc familyBloc; + + const FatherEditAlert( + {super.key, + required this.familyBackground, + required this.bloodType, + required this.civilStatus, + required this.gender, + required this.nameExtensions, + required this.sexes, required this.familyBloc, required this.profileId, required this.token}); + + @override + State createState() => _FatherEditAlertState(); +} + +class _FatherEditAlertState extends State { + final _formKey = GlobalKey(); + final bdayController = TextEditingController(); + + ////selected + String? selectedExtension; + String? selectedGender; + String? selectedSex; + String? selectedBloodType; + String? selectedCivilStatus; + bool deceased = false; + @override + Widget build(BuildContext context) { + selectedExtension = widget.familyBackground.relatedPerson?.nameExtension; + selectedGender = widget.familyBackground.relatedPerson?.gender; + selectedSex = widget.familyBackground.relatedPerson?.sex; + selectedBloodType = widget.familyBackground.relatedPerson?.bloodType; + selectedCivilStatus = widget.familyBackground.relatedPerson?.civilStatus; + bdayController.text = widget.familyBackground.relatedPerson!.birthdate!.toString(); + deceased = widget.familyBackground.relatedPerson!.deceased!; + return AlertDialog( + title: const Text("Family - Parental Parent"), + contentPadding: const EdgeInsets.all(24), + content: SingleChildScrollView( + child: FormBuilder( + key: _formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ////name + FormBuilderTextField( + initialValue: widget.familyBackground.relatedPerson!.lastName, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "lastname", + decoration: normalTextFieldStyle("Last name", ""), + ), + const SizedBox( + height: 8, + ), + //// firstname + FormBuilderTextField( + initialValue: widget.familyBackground.relatedPerson!.firstName, + name: "firstname", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: normalTextFieldStyle("First name", ""), + ), + const SizedBox( + height: 8, + ), + ////middle name + FormBuilderTextField( + initialValue: widget.familyBackground.relatedPerson?.middleName, + name: "middlename", + + decoration: normalTextFieldStyle("Middle name", ""), + ), + + const SizedBox( + height: 8, + ), + //// extension + DropdownButtonFormField( + value: selectedExtension, + decoration: normalTextFieldStyle("Name extension", ""), + + items: widget.nameExtensions + .map((element) => DropdownMenuItem( + value: element, + child: Text(element), + )) + .toList(), + onChanged: (e) { + selectedExtension = e; + }, + ), + + const SizedBox( + height: 8, + ), + ////Bday + DateTimePicker( + controller: bdayController, + use24HourFormat: false, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + timeHintText: "Birthdate", + decoration: + normalTextFieldStyle("Birthdate *", "*").copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + firstDate: DateTime(1970), + lastDate: DateTime(2100), + icon: const Icon(Icons.date_range), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + //// sex + child: DropdownButtonFormField( + decoration: normalTextFieldStyle("Sex", ""), + value: selectedSex, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + items: widget.sexes + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedSex = e; + }, + ), + ), + const SizedBox( + width: 8, + ), + ////gender + Flexible( + flex: 1, + child: DropdownButtonFormField( + isExpanded: true, + decoration: normalTextFieldStyle("Gender", ""), + value: selectedGender, + items: widget.gender + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedGender = e; + }, + ), + ), + ]), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + ////Blood Type + Flexible( + flex: 1, + child: DropdownButtonFormField( + isExpanded: true, + decoration: normalTextFieldStyle("Blood type", ""), + value: selectedBloodType, + items: widget.bloodType + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedBloodType = e; + }, + ), + ), + const SizedBox( + width: 8, + ), + //// Civil Status + Flexible( + flex: 1, + child: DropdownButtonFormField( + isExpanded: true, + decoration: normalTextFieldStyle("Civil status", ""), + value: selectedCivilStatus, + items: widget.civilStatus + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedCivilStatus = e; + }, + ), + ), + ]), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + ////height + child: FormBuilderTextField( + initialValue: widget.familyBackground.relatedPerson?.heightM == null? null:widget.familyBackground.relatedPerson?.heightM.toString(), + name: "height", + validator: integerAndNumeric, + keyboardType: TextInputType.number, + decoration: normalTextFieldStyle("Height", ""), + ), + ), + const SizedBox( + width: 8, + ), + Flexible( + flex: 1, + //// weight + child: FormBuilderTextField( + initialValue: widget.familyBackground.relatedPerson?.weightKg == null? null:widget.familyBackground.relatedPerson?.weightKg.toString(), + name: "weight", + validator: integerAndNumeric, + keyboardType: TextInputType.number, + decoration: normalTextFieldStyle("Weight", ""), + ), + ), + const SizedBox( + height: 8, + ), + ]), + ), + const SizedBox( + height: 8, + ), + ////Deceased + SizedBox( + width: screenWidth, + child: StatefulBuilder(builder: (context, setState) { + return FormBuilderSwitch( + initialValue: deceased, + title: Text(deceased ? "YES" : "NO"), + decoration: normalTextFieldStyle("Deceased?", ""), + + onChanged: (value) { + setState(() { + deceased = value!; + }); + }, + + name: 'deceased', + validator: FormBuilderValidators.required(), + ); + }), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + height: 50, + child: ElevatedButton( + style: + mainBtnStyle(second, Colors.transparent, primary), + onPressed: () { + if (_formKey.currentState!.saveAndValidate()) { + String fname = + _formKey.currentState!.value['firstname']; + String lastName = + _formKey.currentState!.value['lastname']; + String? mname = + _formKey.currentState?.value['middlename']; + String bday = bdayController.text; + String? gender = selectedGender =="NONE"?null:selectedGender; + String? extension = selectedExtension=="NONE"?null:selectedExtension; + String? blood = selectedBloodType =="NONE"?null:selectedBloodType; + String? civilStatus = selectedCivilStatus =="NONE"?null:selectedCivilStatus; + String? sex = selectedSex; + Company? company; + Position? position; + double? height = + _formKey.currentState?.value['height']==null? null: + double.parse( + _formKey.currentState?.value['height']); + double? weight = + _formKey.currentState?.value['weight']==null?null: + double.parse( + _formKey.currentState?.value['weight']); + Relationship relationship = Relationship( + id: 1, + type: "Paternal_Parent", + category: "Family"); + List? emergnecyContacts; + bool incaseOfEmergency = false; + String? companyAddress; + RelatedPerson person = RelatedPerson( + titlePrefix: null, + firstName: fname, + maidenName: null, + middleName: mname, + lastName: lastName, + birthdate: DateTime.parse(bday), + id: widget.familyBackground.relatedPerson!.id, + sex: sex, + gender: gender, + deceased: deceased, + heightM: height, + weightKg: weight, + esigPath: null, + bloodType: blood, + photoPath: null, + uuidQrcode: null, + nameExtension: extension, + civilStatus: civilStatus, + titleSuffix: null, + showTitleId: false, + ); + FamilyBackground familyBackground = + FamilyBackground(company: company,position: position,relatedPerson: person,relationship: relationship,companyAddress: companyAddress,emergencyContact: emergnecyContacts,incaseOfEmergency: incaseOfEmergency,companyContactNumber: null); + Navigator.of(context).pop(); + + widget.familyBloc.add(Updatefamily(familyBackground: familyBackground, profileId: widget.profileId, token: widget.token,relationshipId: 1)); + } + }, + child: const Text(submit)), + ), + ], + )), + )); + } +} diff --git a/lib/screens/profile/components/basic_information/family/mother_add_modal.dart b/lib/screens/profile/components/basic_information/family/mother_add_modal.dart new file mode 100644 index 0000000..36f9c85 --- /dev/null +++ b/lib/screens/profile/components/basic_information/family/mother_add_modal.dart @@ -0,0 +1,372 @@ +import 'package:date_time_picker/date_time_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; + +import '../../../../../bloc/profile/family/family_bloc.dart'; +import '../../../../../model/profile/family_backround.dart'; +import '../../../../../model/utils/position.dart'; +import '../../../../../theme-data.dart/btn-style.dart'; +import '../../../../../theme-data.dart/colors.dart'; +import '../../../../../theme-data.dart/form-style.dart'; +import '../../../../../utils/global.dart'; +import '../../../../../utils/text_container.dart'; +import '../../../../../utils/validators.dart'; + +class MotherAlert extends StatefulWidget { + final List nameExtensions; + final List gender; + final List sexes; + final List bloodType; + final List civilStatus; + final String token; + final int profileId; + final FamilyBloc familyBloc; + const MotherAlert( + {super.key, + required this.familyBloc, + required this.profileId, + required this.token, + required this.bloodType, + required this.civilStatus, + required this.gender, + required this.nameExtensions, + required this.sexes}); + + @override + State createState() => _MotherAlertState(); +} + +class _MotherAlertState extends State { + final _formKey = GlobalKey(); + final bdayController = TextEditingController(); + ////selected + String? selectedExtension; + String? selectedGender; + String? selectedSex; + String? selectedBloodType; + String? selectedCivilStatus; + bool deceased = false; + Widget build(BuildContext context) { + return AlertDialog( + title: const Text("Family - Maternal Parent"), + contentPadding: const EdgeInsets.all(24), + content: SingleChildScrollView( + child: FormBuilder( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + ////name + FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "lastname", + decoration: normalTextFieldStyle("Last name", ""), + ), + const SizedBox( + height: 8, + ), + //// firstname + FormBuilderTextField( + name: "firstname", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: normalTextFieldStyle("First name", ""), + ), + const SizedBox( + height: 8, + ), + ////middle name + FormBuilderTextField( + name: "middlename", + + decoration: normalTextFieldStyle("Middle name", ""), + ), + + const SizedBox( + height: 8, + ), + //// extension + FormBuilderDropdown( + decoration: normalTextFieldStyle("Name extension", ""), + name: "extension", + items: widget.nameExtensions + .map((element) => DropdownMenuItem( + value: element, + child: Text(element), + )) + .toList(), + onChanged: (e) { + selectedExtension = e; + }, + ), + + const SizedBox( + height: 8, + ), +////Bday + DateTimePicker( + controller: bdayController, + use24HourFormat: false, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + timeHintText: "Birthdate", + decoration: + normalTextFieldStyle("Birthdate *", "*").copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + firstDate: DateTime(1970), + lastDate: DateTime(2100), + icon: const Icon(Icons.date_range), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + //// sex + child: FormBuilderDropdown( + decoration: normalTextFieldStyle("Sex", ""), + name: "sex", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + items: widget.sexes + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedSex = e; + }, + ), + ), + const SizedBox( + width: 8, + ), + ////gender + Flexible( + flex: 1, + child: FormBuilderDropdown( + decoration: normalTextFieldStyle("Gender", ""), + name: "gender", + items: widget.gender + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedGender = e; + }, + ), + ), + ]), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + ////Blood Type + Flexible( + flex: 1, + child: FormBuilderDropdown( + decoration: normalTextFieldStyle("Blood type", ""), + name: "bloodtype", + items: widget.bloodType + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedBloodType = e; + }, + ), + ), + const SizedBox( + width: 8, + ), + //// Civil Status + Flexible( + flex: 1, + child: FormBuilderDropdown( + decoration: normalTextFieldStyle("Civil status", ""), + name: "extension", + items: widget.civilStatus + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedCivilStatus = e; + }, + ), + ), + ]), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + + ////height + child: FormBuilderTextField( + name: "height", + validator: integerAndNumeric, + keyboardType: TextInputType.number, + decoration: normalTextFieldStyle("Height", ""), + ), + ), + const SizedBox( + width: 8, + ), + Flexible( + flex: 1, + //// weight + child: FormBuilderTextField( + name: "weight", + validator: integerAndNumeric, + keyboardType: TextInputType.number, + decoration: normalTextFieldStyle("Weight", ""), + ), + ), + const SizedBox( + height: 8, + ), + ]), + ), + const SizedBox( + height: 8, + ), + ////Deceased + + SizedBox( + width: screenWidth, + child: StatefulBuilder(builder: (context, setState) { + return FormBuilderSwitch( + initialValue: deceased, + title: Text(deceased ? "YES" : "NO"), + decoration: normalTextFieldStyle("Deceased?", ""), + ////onvhange private sector + onChanged: (value) { + setState(() { + deceased = value!; + }); + }, + + name: 'deceased', + validator: FormBuilderValidators.required(), + ); + }), + ), + const SizedBox( + height: 8, + ), + Text( + "Maiden Name", + style: Theme.of(context).textTheme.labelLarge, + ), + const SizedBox( + height: 8, + ), + ////maiden lastname + FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "maiden_lastname", + decoration: normalTextFieldStyle("Last name", ""), + ), + + const SizedBox( + height: 8, + ), + ////maiden middle name + FormBuilderTextField( + name: "maiden_middlename", + decoration: normalTextFieldStyle("Middle name", ""), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + height: 50, + child: ElevatedButton( + style: + mainBtnStyle(second, Colors.transparent, primary), + onPressed: () { + if (_formKey.currentState!.saveAndValidate()) { + String fname = + _formKey.currentState!.value['firstname']; + String lastName = + _formKey.currentState!.value['lastname']; + String? mname = + _formKey.currentState?.value['middlename']; + String maidenMiddleName = _formKey.currentState!.value['maiden_middlename']; + String maidenLastName = _formKey.currentState!.value['maiden_lastname']; + String bday = bdayController.text; + String? gender = selectedGender =="NONE"?null:selectedGender; + String? extension = selectedExtension=="NONE"?null:selectedExtension; + String? blood = selectedBloodType =="NONE"?null:selectedBloodType; + String? civilStatus = selectedCivilStatus =="NONE"?null:selectedCivilStatus; + String? sex = selectedSex; + Company? company; + Position? position; + double? height = + _formKey.currentState?.value['height']==null? null: + double.parse( + _formKey.currentState?.value['height']); + double? weight = + _formKey.currentState?.value['weight']==null?null: + double.parse( + _formKey.currentState?.value['weight']); + Relationship relationship = Relationship( + id: 1, + type: "Paternal_Parent", + category: "Family"); + List? emergnecyContacts; + bool incaseOfEmergency = false; + String? companyAddress; + RelatedPerson person = RelatedPerson( + titlePrefix: null, + firstName: fname, + maidenName: MaidenName(lastName: maidenLastName, middleName: maidenMiddleName), + middleName: mname, + lastName: lastName, + birthdate: DateTime.parse(bday), + id: null, + sex: sex, + gender: gender, + deceased: deceased, + heightM: height, + weightKg: weight, + esigPath: null, + bloodType: blood, + photoPath: null, + uuidQrcode: null, + nameExtension: extension, + civilStatus: civilStatus, + titleSuffix: null, + showTitleId: false, + ); + FamilyBackground familyBackground = + FamilyBackground(company: company,position: position,relatedPerson: person,relationship: relationship,companyAddress: companyAddress,emergencyContact: emergnecyContacts,incaseOfEmergency: incaseOfEmergency,companyContactNumber: null); + Navigator.of(context).pop(); + + widget.familyBloc.add(AddFamily(familyBackground: familyBackground, profileId: widget.profileId, token: widget.token,relationshipId: 2)); + } + }, + child: const Text(submit)), + ), + ], + )), + )); + } +} diff --git a/lib/screens/profile/components/basic_information/family/mother_edit_modal.dart b/lib/screens/profile/components/basic_information/family/mother_edit_modal.dart new file mode 100644 index 0000000..7eb9351 --- /dev/null +++ b/lib/screens/profile/components/basic_information/family/mother_edit_modal.dart @@ -0,0 +1,390 @@ +import 'package:date_time_picker/date_time_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:unit2/bloc/profile/family/family_bloc.dart'; +import 'package:unit2/model/profile/family_backround.dart'; + +import '../../../../../model/utils/position.dart'; +import '../../../../../theme-data.dart/btn-style.dart'; +import '../../../../../theme-data.dart/colors.dart'; +import '../../../../../theme-data.dart/form-style.dart'; +import '../../../../../utils/global.dart'; +import '../../../../../utils/text_container.dart'; +import '../../../../../utils/validators.dart'; + +class MotherEditAlert extends StatefulWidget { + final FamilyBackground familyBackground; + final List nameExtensions; + final List gender; + final List sexes; + final List bloodType; + final List civilStatus; + final String token; + final int profileId; + final FamilyBloc familyBloc; + + const MotherEditAlert( + {super.key, + required this.familyBackground, + required this.bloodType, + required this.civilStatus, + required this.gender, + required this.nameExtensions, + required this.sexes, required this.familyBloc, required this.profileId, required this.token}); + + @override + State createState() => _MotherEditAlertState(); +} + +class _MotherEditAlertState extends State { + final _formKey = GlobalKey(); + final bdayController = TextEditingController(); + + ////selected + String? selectedExtension; + String? selectedGender; + String? selectedSex; + String? selectedBloodType; + String? selectedCivilStatus; + bool deceased = false; + @override + Widget build(BuildContext context) { + selectedExtension = widget.familyBackground.relatedPerson?.nameExtension; + selectedGender = widget.familyBackground.relatedPerson?.gender; + selectedSex = widget.familyBackground.relatedPerson?.sex; + selectedBloodType = widget.familyBackground.relatedPerson?.bloodType; + selectedCivilStatus = widget.familyBackground.relatedPerson?.civilStatus; + bdayController.text = widget.familyBackground.relatedPerson!.birthdate!.toString(); + deceased = widget.familyBackground.relatedPerson!.deceased!; + return AlertDialog( + title: const Text("Family - Parental Parent"), + contentPadding: const EdgeInsets.all(24), + content: SingleChildScrollView( + child: FormBuilder( + key: _formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ////name + FormBuilderTextField( + initialValue: widget.familyBackground.relatedPerson!.lastName, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "lastname", + decoration: normalTextFieldStyle("Last name", ""), + ), + const SizedBox( + height: 8, + ), + //// firstname + FormBuilderTextField( + initialValue: widget.familyBackground.relatedPerson!.firstName, + name: "firstname", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: normalTextFieldStyle("First name", ""), + ), + const SizedBox( + height: 8, + ), + ////middle name + FormBuilderTextField( + initialValue: widget.familyBackground.relatedPerson?.middleName, + name: "middlename", + + decoration: normalTextFieldStyle("Middle name", ""), + ), + + const SizedBox( + height: 8, + ), + //// extension + DropdownButtonFormField( + value: selectedExtension, + decoration: normalTextFieldStyle("Name extension", ""), + + items: widget.nameExtensions + .map((element) => DropdownMenuItem( + value: element, + child: Text(element), + )) + .toList(), + onChanged: (e) { + selectedExtension = e; + }, + ), + + const SizedBox( + height: 8, + ), + ////Bday + DateTimePicker( + controller: bdayController, + use24HourFormat: false, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + timeHintText: "Birthdate", + decoration: + normalTextFieldStyle("Birthdate *", "*").copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + firstDate: DateTime(1970), + lastDate: DateTime(2100), + icon: const Icon(Icons.date_range), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + //// sex + child: DropdownButtonFormField( + decoration: normalTextFieldStyle("Sex", ""), + value: selectedSex, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + items: widget.sexes + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedSex = e; + }, + ), + ), + const SizedBox( + width: 8, + ), + ////gender + Flexible( + flex: 1, + child: DropdownButtonFormField( + isExpanded: true, + decoration: normalTextFieldStyle("Gender", ""), + value: selectedGender, + items: widget.gender + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedGender = e; + }, + ), + ), + ]), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + ////Blood Type + Flexible( + flex: 1, + child: DropdownButtonFormField( + isExpanded: true, + decoration: normalTextFieldStyle("Blood type", ""), + value: selectedBloodType, + items: widget.bloodType + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedBloodType = e; + }, + ), + ), + const SizedBox( + width: 8, + ), + //// Civil Status + Flexible( + flex: 1, + child: DropdownButtonFormField( + isExpanded: true, + decoration: normalTextFieldStyle("Civil status", ""), + value: selectedCivilStatus, + items: widget.civilStatus + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedCivilStatus = e; + }, + ), + ), + ]), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + ////height + child: FormBuilderTextField( + initialValue: widget.familyBackground.relatedPerson?.heightM == null? null:widget.familyBackground.relatedPerson?.heightM.toString(), + name: "height", + validator: integerAndNumeric, + keyboardType: TextInputType.number, + decoration: normalTextFieldStyle("Height", ""), + ), + ), + const SizedBox( + width: 8, + ), + Flexible( + flex: 1, + //// weight + child: FormBuilderTextField( + initialValue: widget.familyBackground.relatedPerson?.weightKg == null? null:widget.familyBackground.relatedPerson?.weightKg.toString(), + name: "weight", + validator: integerAndNumeric, + keyboardType: TextInputType.number, + decoration: normalTextFieldStyle("Weight", ""), + ), + ), + + ]), + ), + const SizedBox( + height: 8, + ), + ////Deceased + SizedBox( + width: screenWidth, + child: StatefulBuilder(builder: (context, setState) { + return FormBuilderSwitch( + initialValue: deceased, + title: Text(deceased ? "YES" : "NO"), + decoration: normalTextFieldStyle("Deceased?", ""), + + onChanged: (value) { + setState(() { + deceased = value!; + }); + }, + + name: 'deceased', + validator: FormBuilderValidators.required(), + ); + }), + ), + const SizedBox( + height: 8, + ), + Text( + "Maiden Name", + style: Theme.of(context).textTheme.labelLarge, + ), + const SizedBox( + height: 8, + ), + ////maiden lastname + FormBuilderTextField( + initialValue: widget.familyBackground.relatedPerson?.maidenName?.lastName, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "maiden_lastname", + decoration: normalTextFieldStyle("Last name", ""), + ), + + const SizedBox( + height: 8, + ), + ////maiden middle name + FormBuilderTextField( + initialValue: widget.familyBackground.relatedPerson?.maidenName?.middleName, + name: "maiden_middlename", + decoration: normalTextFieldStyle("Middle name", ""), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + height: 50, + child: ElevatedButton( + style: + mainBtnStyle(second, Colors.transparent, primary), + onPressed: () { + if (_formKey.currentState!.saveAndValidate()) { + String fname = + _formKey.currentState!.value['firstname']; + String lastName = + _formKey.currentState!.value['lastname']; + String? mname = + _formKey.currentState?.value['middlename']; + String maidenMiddleName = _formKey.currentState!.value['maiden_middlename']; + String maidenLastName = _formKey.currentState!.value['maiden_lastname']; + String bday = bdayController.text; + String? gender = selectedGender =="NONE"?null:selectedGender; + String? extension = selectedExtension=="NONE"?null:selectedExtension; + String? blood = selectedBloodType =="NONE"?null:selectedBloodType; + String? civilStatus = selectedCivilStatus =="NONE"?null:selectedCivilStatus; + String? sex = selectedSex; + Company? company; + Position? position; + double? height = + _formKey.currentState?.value['height']==null? null: + double.parse( + _formKey.currentState?.value['height']); + double? weight = + _formKey.currentState?.value['weight']==null?null: + double.parse( + _formKey.currentState?.value['weight']); + Relationship relationship = Relationship( + id: 1, + type: "Paternal_Parent", + category: "Family"); + List? emergnecyContacts; + bool incaseOfEmergency = false; + String? companyAddress; + RelatedPerson person = RelatedPerson( + titlePrefix: null, + firstName: fname, + maidenName: MaidenName(middleName: maidenMiddleName,lastName: maidenLastName), + middleName: mname, + lastName: lastName, + birthdate: DateTime.parse(bday), + id: widget.familyBackground.relatedPerson!.id, + sex: sex, + gender: gender, + deceased: deceased, + heightM: height, + weightKg: weight, + esigPath: null, + bloodType: blood, + photoPath: null, + uuidQrcode: null, + nameExtension: extension, + civilStatus: civilStatus, + titleSuffix: null, + showTitleId: false, + ); + FamilyBackground familyBackground = + FamilyBackground(company: company,position: position,relatedPerson: person,relationship: relationship,companyAddress: companyAddress,emergencyContact: emergnecyContacts,incaseOfEmergency: incaseOfEmergency,companyContactNumber: null); + Navigator.of(context).pop(); + + widget.familyBloc.add(Updatefamily(familyBackground: familyBackground, profileId: widget.profileId, token: widget.token,relationshipId: 1)); + } + }, + child: const Text(submit)), + ), + ], + )), + )); + } +} diff --git a/lib/screens/profile/components/basic_information/family/related_add_modal.dart b/lib/screens/profile/components/basic_information/family/related_add_modal.dart new file mode 100644 index 0000000..0e4b22b --- /dev/null +++ b/lib/screens/profile/components/basic_information/family/related_add_modal.dart @@ -0,0 +1,381 @@ +import 'package:date_time_picker/date_time_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:unit2/model/utils/agency.dart'; +import 'package:unit2/model/utils/category.dart'; + +import '../../../../../bloc/profile/family/family_bloc.dart'; +import '../../../../../model/profile/family_backround.dart'; +import '../../../../../model/utils/position.dart'; +import '../../../../../theme-data.dart/btn-style.dart'; +import '../../../../../theme-data.dart/colors.dart'; +import '../../../../../theme-data.dart/form-style.dart'; +import '../../../../../utils/global.dart'; +import '../../../../../utils/text_container.dart'; +import '../../../../../utils/validators.dart'; + +class RelatedAlert extends StatefulWidget { + final List nameExtensions; + final List gender; + final List sexes; + final List bloodType; + final List civilStatus; + final List relationships; + final String token; + final int profileId; + final FamilyBloc familyBloc; + + + const RelatedAlert( + {super.key, + required this.bloodType, + required this.token, + required this.profileId, + required this.familyBloc, + required this.civilStatus, + required this.relationships, + required this.gender, + required this.nameExtensions, + required this.sexes, + + }); + + @override + State createState() => _RelatedAlertState(); +} + +class _RelatedAlertState extends State { +final _formKey = GlobalKey(); + final bdayController = TextEditingController(); + + ////selected + String? selectedExtension; + String? selectedGender; + String? selectedSex; + String? selectedBloodType; + String? selectedCivilStatus; + Relationship? selectedRelationship; + bool deceased = false; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text("Family - Other Related Person"), + contentPadding: const EdgeInsets.all(24), + content: SingleChildScrollView( + child: FormBuilder( + key: _formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ////Relationship + FormBuilderDropdown( + decoration: normalTextFieldStyle("Relationship", ""), + name: "extension", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + items: widget.relationships + .map((element){ + return DropdownMenuItem( + value: element, + child: Text(element.type!), + ); + + },) + .toList(), + onChanged: (e){ + selectedRelationship = e; + }, + ), + const SizedBox( + height: 8, + ), + ////name + FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "lastname", + decoration: normalTextFieldStyle("Last name", ""), + ), + const SizedBox( + height: 8, + ), + //// firstname + FormBuilderTextField( + name: "firstname", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: normalTextFieldStyle("First name", ""), + ), + const SizedBox( + height: 8, + ), + ////middle name + FormBuilderTextField( + name: "middlename", + + decoration: normalTextFieldStyle("Middle name", ""), + ), + + const SizedBox( + height: 8, + ), + //// extension + FormBuilderDropdown( + decoration: normalTextFieldStyle("Name extension", ""), + name: "extension", + items: widget.nameExtensions + .map((element) => DropdownMenuItem( + value: element, + child: Text(element), + )) + .toList(), + onChanged: (e) { + selectedExtension = e; + }, + ), + + const SizedBox( + height: 8, + ), + + ////Bday + DateTimePicker( + controller: bdayController, + use24HourFormat: false, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + timeHintText: "Birthdate", + decoration: + normalTextFieldStyle("Birthdate *", "*").copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + firstDate: DateTime(1970), + lastDate: DateTime(2100), + icon: const Icon(Icons.date_range), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + //// sex + child: FormBuilderDropdown( + decoration: normalTextFieldStyle("Sex", ""), + name: "sex", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + items: widget.sexes + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedSex = e; + }, + ), + ), + const SizedBox( + width: 8, + ), + ////gender + Flexible( + flex: 1, + child: FormBuilderDropdown( + decoration: normalTextFieldStyle("Gender", ""), + name: "gender", + items: widget.gender + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedGender = e; + }, + ), + ), + ]), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + ////Blood Type + Flexible( + flex: 1, + child: FormBuilderDropdown( + decoration: normalTextFieldStyle("Blood type", ""), + name: "bloodtype", + items: widget.bloodType + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedBloodType = e; + }, + ), + ), + const SizedBox( + width: 8, + ), + //// Civil Status + Flexible( + flex: 1, + child: FormBuilderDropdown( + decoration: normalTextFieldStyle("Civil status", ""), + name: "extension", + items: widget.civilStatus + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedCivilStatus = e; + }, + ), + ), + ]), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + ////height + child: FormBuilderTextField( + name: "height", + validator: integerAndNumeric, + keyboardType: TextInputType.number, + decoration: normalTextFieldStyle("Height", ""), + ), + ), + const SizedBox( + width: 8, + ), + Flexible( + flex: 1, + //// weight + child: FormBuilderTextField( + name: "weight", + validator: integerAndNumeric, + keyboardType: TextInputType.number, + decoration: normalTextFieldStyle("Weight", ""), + ), + ), + const SizedBox( + height: 8, + ), + ]), + ), + const SizedBox( + height: 8, + ), + ////Deceased + SizedBox( + width: screenWidth, + child: StatefulBuilder(builder: (context, setState) { + return FormBuilderSwitch( + initialValue: deceased, + title: Text(deceased ? "YES" : "NO"), + decoration: normalTextFieldStyle("Deceased?", ""), + ////onvhange private sector + onChanged: (value) { + setState(() { + deceased = value!; + }); + }, + + name: 'deceased', + validator: FormBuilderValidators.required(), + ); + }), + ), + const SizedBox( + height: 8, + ), + + + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + height: 50, + child: ElevatedButton( + style: + mainBtnStyle(second, Colors.transparent, primary), + onPressed: () { + if (_formKey.currentState!.saveAndValidate()) { + String fname = + _formKey.currentState!.value['firstname']; + String lastName = + _formKey.currentState!.value['lastname']; + String? mname = + _formKey.currentState?.value['middlename']; + String bday = bdayController.text; + String? gender = selectedGender =="NONE"?null:selectedGender; + String? extension = selectedExtension=="NONE"?null:selectedExtension; + String? blood = selectedBloodType =="NONE"?null:selectedBloodType; + String? civilStatus = selectedCivilStatus =="NONE"?null:selectedCivilStatus; + String? sex = selectedSex; + Company? company; + Position? position; + double? height = + _formKey.currentState?.value['height']==null? null: + double.parse( + _formKey.currentState?.value['height']); + double? weight = + _formKey.currentState?.value['weight']==null?null: + double.parse( + _formKey.currentState?.value['weight']); + Relationship relationship = Relationship( + id: 1, + type: "Paternal_Parent", + category: "Family"); + List? emergnecyContacts; + bool incaseOfEmergency = false; + String? companyAddress; + RelatedPerson person = RelatedPerson( + titlePrefix: null, + firstName: fname, + maidenName: null, + middleName: mname, + lastName: lastName, + birthdate: DateTime.parse(bday), + id: null, + sex: sex, + gender: gender, + deceased: deceased, + heightM: height, + weightKg: weight, + esigPath: null, + bloodType: blood, + photoPath: null, + uuidQrcode: null, + nameExtension: extension, + civilStatus: civilStatus, + titleSuffix: null, + showTitleId: false, + ); + FamilyBackground familyBackground = + FamilyBackground(company: company,position: position,relatedPerson: person,relationship: relationship,companyAddress: companyAddress,emergencyContact: emergnecyContacts,incaseOfEmergency: incaseOfEmergency,companyContactNumber: null); + Navigator.of(context).pop(); + + widget.familyBloc.add(AddFamily(familyBackground: familyBackground, profileId: widget.profileId, token: widget.token,relationshipId: selectedRelationship!.id!)); + } + }, + child: const Text(submit)), + ), + ], + )), + )); + } +} diff --git a/lib/screens/profile/components/basic_information/family/related_edit_modal.dart b/lib/screens/profile/components/basic_information/family/related_edit_modal.dart new file mode 100644 index 0000000..febcae4 --- /dev/null +++ b/lib/screens/profile/components/basic_information/family/related_edit_modal.dart @@ -0,0 +1,421 @@ +import 'package:date_time_picker/date_time_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:unit2/bloc/profile/family/family_bloc.dart'; +import 'package:unit2/model/profile/family_backround.dart'; + +import '../../../../../model/utils/position.dart'; +import '../../../../../theme-data.dart/btn-style.dart'; +import '../../../../../theme-data.dart/colors.dart'; +import '../../../../../theme-data.dart/form-style.dart'; +import '../../../../../utils/global.dart'; +import '../../../../../utils/text_container.dart'; +import '../../../../../utils/validators.dart'; + +class RelatedEditAlert extends StatefulWidget { + final FamilyBackground familyBackground; + final List nameExtensions; + final List gender; + final List sexes; + final List bloodType; + final List civilStatus; + final String token; + final int profileId; + final FamilyBloc familyBloc; + final List relationships; + + const RelatedEditAlert( + {super.key, + required this.familyBackground, + required this.bloodType, + required this.civilStatus, + required this.gender, + required this.nameExtensions, + required this.relationships, + required this.sexes, + required this.familyBloc, + required this.profileId, + required this.token}); + + @override + State createState() => _RelatedEditAlertState(); +} + +class _RelatedEditAlertState extends State { + final _formKey = GlobalKey(); + final bdayController = TextEditingController(); + + ////selected + String? selectedExtension; + String? selectedGender; + String? selectedSex; + String? selectedBloodType; + String? selectedCivilStatus; + Relationship? selectedRelationship; + bool deceased = false; + @override + Widget build(BuildContext context) { + selectedExtension = widget.familyBackground.relatedPerson?.nameExtension; + selectedGender = widget.familyBackground.relatedPerson?.gender; + selectedSex = widget.familyBackground.relatedPerson?.sex; + selectedBloodType = widget.familyBackground.relatedPerson?.bloodType; + selectedCivilStatus = widget.familyBackground.relatedPerson?.civilStatus; + selectedRelationship = widget.relationships.firstWhere((element) => element.id == widget.familyBackground.relationship!.id); + bdayController.text = + widget.familyBackground.relatedPerson!.birthdate!.toString(); + deceased = widget.familyBackground.relatedPerson!.deceased!; + return AlertDialog( + title: const Text("Family - Other Related Person"), + contentPadding: const EdgeInsets.all(24), + content: SingleChildScrollView( + child: FormBuilder( + key: _formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ////Relationship + DropdownButtonFormField( + decoration: normalTextFieldStyle("Relationship", ""), + value: selectedRelationship, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + items: widget.relationships.map( + (element) { + return DropdownMenuItem( + value: element, + child: Text(element.type!), + ); + }, + ).toList(), + onChanged: (e) { + selectedRelationship = e; + }, + ), + const SizedBox(height: 8,), + ////name + FormBuilderTextField( + initialValue: + widget.familyBackground.relatedPerson!.lastName, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "lastname", + decoration: normalTextFieldStyle("Last name", ""), + ), + const SizedBox( + height: 8, + ), + //// firstname + FormBuilderTextField( + initialValue: + widget.familyBackground.relatedPerson!.firstName, + name: "firstname", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: normalTextFieldStyle("First name", ""), + ), + const SizedBox( + height: 8, + ), + ////middle name + FormBuilderTextField( + initialValue: + widget.familyBackground.relatedPerson?.middleName, + name: "middlename", + + decoration: normalTextFieldStyle("Middle name", ""), + ), + + const SizedBox( + height: 8, + ), + //// extension + DropdownButtonFormField( + value: selectedExtension, + decoration: normalTextFieldStyle("Name extension", ""), + items: widget.nameExtensions + .map((element) => DropdownMenuItem( + value: element, + child: Text(element), + )) + .toList(), + onChanged: (e) { + selectedExtension = e; + }, + ), + + const SizedBox( + height: 8, + ), + ////Bday + DateTimePicker( + controller: bdayController, + use24HourFormat: false, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + timeHintText: "Birthdate", + decoration: + normalTextFieldStyle("Birthdate *", "*").copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + firstDate: DateTime(1970), + lastDate: DateTime(2100), + icon: const Icon(Icons.date_range), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + //// sex + child: DropdownButtonFormField( + decoration: normalTextFieldStyle("Sex", ""), + value: selectedSex, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + items: widget.sexes + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedSex = e; + }, + ), + ), + const SizedBox( + width: 8, + ), + ////gender + Flexible( + flex: 1, + child: DropdownButtonFormField( + isExpanded: true, + decoration: normalTextFieldStyle("Gender", ""), + value: selectedGender, + items: widget.gender + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedGender = e; + }, + ), + ), + ]), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + ////Blood Type + Flexible( + flex: 1, + child: DropdownButtonFormField( + isExpanded: true, + decoration: normalTextFieldStyle("Blood type", ""), + value: selectedBloodType, + items: widget.bloodType + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedBloodType = e; + }, + ), + ), + const SizedBox( + width: 8, + ), + //// Civil Status + Flexible( + flex: 1, + child: DropdownButtonFormField( + isExpanded: true, + decoration: normalTextFieldStyle("Civil status", ""), + value: selectedCivilStatus, + items: widget.civilStatus + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedCivilStatus = e; + }, + ), + ), + ]), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + ////height + child: FormBuilderTextField( + initialValue: widget.familyBackground.relatedPerson + ?.heightM == + null + ? null + : widget.familyBackground.relatedPerson?.heightM + .toString(), + name: "height", + validator: integerAndNumeric, + keyboardType: TextInputType.number, + decoration: normalTextFieldStyle("Height", ""), + ), + ), + const SizedBox( + width: 8, + ), + Flexible( + flex: 1, + //// weight + child: FormBuilderTextField( + initialValue: widget.familyBackground.relatedPerson + ?.weightKg == + null + ? null + : widget.familyBackground.relatedPerson?.weightKg + .toString(), + name: "weight", + validator: integerAndNumeric, + keyboardType: TextInputType.number, + decoration: normalTextFieldStyle("Weight", ""), + ), + ), + const SizedBox( + height: 8, + ), + ]), + ), + const SizedBox( + height: 8, + ), + ////Deceased + SizedBox( + width: screenWidth, + child: StatefulBuilder(builder: (context, setState) { + return FormBuilderSwitch( + initialValue: deceased, + title: Text(deceased ? "YES" : "NO"), + decoration: normalTextFieldStyle("Deceased?", ""), + onChanged: (value) { + setState(() { + deceased = value!; + }); + }, + name: 'deceased', + validator: FormBuilderValidators.required(), + ); + }), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + height: 50, + child: ElevatedButton( + style: + mainBtnStyle(second, Colors.transparent, primary), + onPressed: () { + if (_formKey.currentState!.saveAndValidate()) { + String fname = + _formKey.currentState!.value['firstname']; + String lastName = + _formKey.currentState!.value['lastname']; + String? mname = + _formKey.currentState?.value['middlename']; + String bday = bdayController.text; + String? gender = selectedGender == "NONE" + ? null + : selectedGender; + String? extension = selectedExtension == "NONE" + ? null + : selectedExtension; + String? blood = selectedBloodType == "NONE" + ? null + : selectedBloodType; + String? civilStatus = selectedCivilStatus == "NONE" + ? null + : selectedCivilStatus; + String? sex = selectedSex; + Company? company; + Position? position; + double? height = + _formKey.currentState?.value['height'] == null + ? null + : double.parse( + _formKey.currentState?.value['height']); + double? weight = + _formKey.currentState?.value['weight'] == null + ? null + : double.parse( + _formKey.currentState?.value['weight']); + Relationship relationship = Relationship( + id: 1, + type: "Paternal_Parent", + category: "Family"); + List? emergnecyContacts; + bool incaseOfEmergency = false; + String? companyAddress; + RelatedPerson person = RelatedPerson( + titlePrefix: null, + firstName: fname, + maidenName: null, + middleName: mname, + lastName: lastName, + birthdate: DateTime.parse(bday), + id: widget.familyBackground.relatedPerson!.id, + sex: sex, + gender: gender, + deceased: deceased, + heightM: height, + weightKg: weight, + esigPath: null, + bloodType: blood, + photoPath: null, + uuidQrcode: null, + nameExtension: extension, + civilStatus: civilStatus, + titleSuffix: null, + showTitleId: false, + ); + FamilyBackground familyBackground = + FamilyBackground( + company: company, + position: position, + relatedPerson: person, + relationship: relationship, + companyAddress: companyAddress, + emergencyContact: emergnecyContacts, + incaseOfEmergency: incaseOfEmergency, + companyContactNumber: null); + Navigator.of(context).pop(); + + widget.familyBloc.add(Updatefamily( + familyBackground: familyBackground, + profileId: widget.profileId, + token: widget.token, + relationshipId: selectedRelationship!.id!)); + } + }, + child: const Text(submit)), + ), + ], + )), + )); + } +} diff --git a/lib/screens/profile/components/basic_information/family/spouse_add_modal.dart b/lib/screens/profile/components/basic_information/family/spouse_add_modal.dart new file mode 100644 index 0000000..0ea373b --- /dev/null +++ b/lib/screens/profile/components/basic_information/family/spouse_add_modal.dart @@ -0,0 +1,722 @@ +import 'package:date_time_picker/date_time_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:searchfield/searchfield.dart'; + +import '../../../../../bloc/profile/family/family_bloc.dart'; +import '../../../../../model/profile/family_backround.dart'; +import '../../../../../model/utils/agency.dart'; +import '../../../../../model/utils/category.dart'; +import '../../../../../model/utils/position.dart'; +import '../../../../../theme-data.dart/box_shadow.dart'; +import '../../../../../theme-data.dart/btn-style.dart'; +import '../../../../../theme-data.dart/colors.dart'; +import '../../../../../theme-data.dart/form-style.dart'; +import '../../../../../utils/global.dart'; +import '../../../../../utils/text_container.dart'; +import '../../../../../utils/validators.dart'; +import '../../../shared/add_for_empty_search.dart'; + +class SpouseAlert extends StatefulWidget { + final List nameExtensions; + final List gender; + final List sexes; + final List bloodType; + final List civilStatus; + final List positions; + final List agencies; + final List category; + final FamilyBloc familyBloc; + final String token; + final int profileId; + + const SpouseAlert( + {super.key, + required this.token, + required this.profileId, + required this.familyBloc, + required this.bloodType, + required this.civilStatus, + required this.agencies, + required this.category, + required this.positions, + required this.gender, + required this.nameExtensions, + required this.sexes}); + + @override + State createState() => _SpouseAlertState(); +} + +class _SpouseAlertState extends State { + final _formKey = GlobalKey(); + final bdayController = TextEditingController(); + + ////selected + String? selectedExtension; + String? selectedGender; + String? selectedSex; + String? selectedBloodType; + String? selectedCivilStatus; Position? selectedPosition; + Category? selectedAgencyCategory; + Agency? selectedAgency; + bool deceased = false; + + final agencyFocusNode = FocusNode(); + final positionFocusNode = FocusNode(); + final appointmentStatusNode = FocusNode(); + final agencyCategoryFocusNode = FocusNode(); + + final addAgencyController = TextEditingController(); + final addPositionController = TextEditingController(); + + bool hasOccupation = false; + bool showAgency = false; + bool? isPrivate = false; + bool showIsPrivateRadio = false; + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text("Family - Spouse"), + contentPadding: const EdgeInsets.all(24), + content: SingleChildScrollView( + child: FormBuilder( + key: _formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ////name + FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "lastname", + decoration: normalTextFieldStyle("Last name", ""), + ), + const SizedBox( + height: 8, + ), + //// firstname + FormBuilderTextField( + name: "firstname", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: normalTextFieldStyle("First name", ""), + ), + const SizedBox( + height: 8, + ), + ////middle name + FormBuilderTextField( + name: "middlename", + decoration: normalTextFieldStyle("Middle name", ""), + ), + + const SizedBox( + height: 8, + ), + //// extension + FormBuilderDropdown( + decoration: normalTextFieldStyle("Name extension", ""), + name: "extension", + + items: widget.nameExtensions + .map((element) => DropdownMenuItem( + value: element, + child: Text(element), + )) + .toList()), + + const SizedBox( + height: 8, + ), + + ////Bday + DateTimePicker( + controller: bdayController, + use24HourFormat: false, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + timeHintText: "Birthdate", + decoration: + normalTextFieldStyle("Birthdate *", "*").copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + firstDate: DateTime(1970), + lastDate: DateTime(2100), + icon: const Icon(Icons.date_range), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + //// sex + child: FormBuilderDropdown( + decoration: normalTextFieldStyle("Sex", ""), + name: "sex", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + items: widget.sexes + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedSex = e; + }, + ), + ), + const SizedBox( + width: 8, + ), + ////gender + Flexible( + flex: 1, + child: FormBuilderDropdown( + decoration: normalTextFieldStyle("Gender", ""), + name: "gender", + items: widget.gender + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedGender = e; + }, + ), + ), + ]), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + ////Blood Type + Flexible( + flex: 1, + child: FormBuilderDropdown( + decoration: normalTextFieldStyle("Blood type", ""), + name: "bloodtype", + items: widget.bloodType + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedBloodType = e; + }, + ), + ), + const SizedBox( + width: 8, + ), + //// Civil Status + Flexible( + flex: 1, + child: FormBuilderDropdown( + decoration: normalTextFieldStyle("Civil status", ""), + name: "civil_status", + items: widget.civilStatus + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedCivilStatus = e; + }, + ), + ), + ]), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + ////height + child: FormBuilderTextField( + name: "height", + validator: integerAndNumeric, + keyboardType: TextInputType.number, + decoration: normalTextFieldStyle("Height", ""), + ), + ), + const SizedBox( + width: 8, + ), + Flexible( + flex: 1, + //// weight + child: FormBuilderTextField( + name: "weight", + validator: integerAndNumeric, + keyboardType: TextInputType.number, + decoration: normalTextFieldStyle("Weight", ""), + ), + ), + const SizedBox( + height: 8, + ), + ]), + ), + const SizedBox( + height: 8, + ), + ////Deceased + SizedBox( + width: screenWidth, + child: StatefulBuilder(builder: (context, setState) { + return FormBuilderSwitch( + initialValue: deceased, + title: Text(deceased ? "YES" : "NO"), + decoration: normalTextFieldStyle("Deceased?", ""), + ////onvhange private sector + onChanged: (value) { + setState(() { + deceased = value!; + }); + }, + + name: 'deceased', + validator: FormBuilderValidators.required(), + ); + }), + ), + const SizedBox( + height: 8, + ), + ////Has Occupation + SizedBox( + width: screenWidth, + child: StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: hasOccupation, + title: Text(hasOccupation ? "YES" : "NO"), + decoration: + normalTextFieldStyle("Has Occupation?", ""), + ////onvhange private sector + onChanged: (value) { + setState(() { + hasOccupation = value!; + }); + }, + + name: 'hasOccupation', + validator: FormBuilderValidators.required(), + ), + Container( + child: hasOccupation + ? Column( + children: [ + const SizedBox( + height: 8, + ), + StatefulBuilder( + builder: (context, setState) { + ////Position + return SearchField( + itemHeight: 50, + suggestionsDecoration: box1(), + suggestions: widget.positions + .map((Position position) => + SearchFieldListItem( + position.title!, + item: position, + child: Padding( + padding: + const EdgeInsets + .symmetric( + horizontal: + 10), + child: ListTile( + title: Text(position + .title!), + )))) + .toList(), + focusNode: positionFocusNode, + searchInputDecoration: + normalTextFieldStyle( + "Position *", "") + .copyWith( + suffixIcon: IconButton( + icon: const Icon( + Icons.arrow_drop_down), + onPressed: () { + positionFocusNode.unfocus(); + }, + )), + onSuggestionTap: (position) { + setState(() { + selectedPosition = position.item; + positionFocusNode.unfocus(); + }); + }, + ////EMPTY WIDGET + emptyWidget: EmptyWidget( + title: "Add Position", + controller: addPositionController, + onpressed: () { + setState(() { + Position newAgencyPosition = + Position( + id: null, + title: + addPositionController + .text + .toUpperCase()); + + widget.positions.insert( + 0, newAgencyPosition); + + addPositionController.text = + ""; + Navigator.pop(context); + }); + }), + validator: (position) { + if (position!.isEmpty) { + return "This field is required"; + } + return null; + }, + ); + }), + const SizedBox( + height: 8, + ), + StatefulBuilder( + builder: (context, setState) { + return Column( + children: [ + SearchField( + itemHeight: 70, + focusNode: agencyFocusNode, + suggestions: widget.agencies + .map((Agency agency) => + SearchFieldListItem( + agency.name!, + item: agency, + child: ListTile( + title: Text( + agency.name!, + overflow: + TextOverflow + .ellipsis, + ), + subtitle: Text(agency + .privateEntity == + true + ? "Private" + : agency.privateEntity == + false + ? "Government" + : ""), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle( + "Agency *", "") + .copyWith( + suffixIcon: + const Icon(Icons + .arrow_drop_down)), + onSuggestionTap: (agency) { + setState(() { + selectedAgency = + agency.item; + + if (selectedAgency! + .privateEntity == + null) { + showIsPrivateRadio = true; + } else { + showIsPrivateRadio = + false; + } + + agencyFocusNode.unfocus(); + }); + }, + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + emptyWidget: EmptyWidget( + controller: + addAgencyController, + onpressed: () { + setState(() { + Agency newAgency = Agency( + id: null, + name: addAgencyController + .text + .toUpperCase(), + category: null, + privateEntity: + null); + + widget.agencies.insert( + 0, newAgency); + selectedAgency = + newAgency; + addAgencyController + .text = ""; + showAgency = true; + + showIsPrivateRadio = + true; + + Navigator.pop(context); + }); + }, + title: "Add Agency")), + + SizedBox( + height: showAgency ? 12 : 0, + ), + ////SHOW CATEGORY AGENCY + SizedBox( + child: showAgency + ? SearchField( + focusNode: + agencyCategoryFocusNode, + itemHeight: 70, + suggestions: widget + .category + .map((Category + category) => + SearchFieldListItem( + category + .name!, + item: + category, + child: + ListTile( + title: Text( + category + .name!), + subtitle: Text(category + .industryClass! + .name!), + ))) + .toList(), + emptyWidget: Container( + height: 100, + decoration: box1(), + child: const Center( + child: Text( + "No result found ...")), + ), + onSuggestionTap: + (agencyCategory) { + setState(() { + selectedAgencyCategory = + agencyCategory + .item; + agencyCategoryFocusNode + .unfocus(); + selectedAgency = Agency( + id: null, + name: + selectedAgency! + .name, + category: + selectedAgencyCategory, + privateEntity: + null); + }); + }, + searchInputDecoration: + normalTextFieldStyle( + "Category *", + "") + .copyWith( + suffixIcon: + const Icon( + Icons + .arrow_drop_down)), + validator: (value) { + if (value!.isEmpty) { + return "This field is required"; + } + return null; + }, + ) + : const SizedBox(), + ), + const SizedBox( + height: 8, + ), + ////PRVIATE SECTOR + SizedBox( + width: screenWidth, + child: showIsPrivateRadio + ? FormBuilderSwitch( + initialValue: isPrivate, + title: Text(isPrivate! + ? "YES" + : "NO"), + decoration: + normalTextFieldStyle( + "Private Entity?", + ""), + ////onvhange private sector + onChanged: (value) { + setState(() { + isPrivate = value; + selectedAgency = Agency( + id: null, + name: + selectedAgency! + .name, + category: + selectedAgencyCategory, + privateEntity: + isPrivate); + agencyFocusNode + .unfocus(); + agencyCategoryFocusNode + .unfocus(); + }); + }, + + name: 'isPrivate', + validator: + FormBuilderValidators + .required(), + ) + : const SizedBox()), + + const SizedBox( + height: 8, + ), + ////Company Address + FormBuilderTextField( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + name: "company_address", + decoration: normalTextFieldStyle( + "Company Address/Business Address *", + "Company Address/Business Address *"), + ), + const SizedBox( + height: 8, + ), + ////Company Tel Number + FormBuilderTextField( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + name: "company_tel", + decoration: normalTextFieldStyle( + "Company /Business Tel./Mobile # *", + "Company /Business Tel./Mobile # *"), + ), + ], + ); + }), + ], + ) + : const SizedBox(), + ), + ], + ); + }), + ), + + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + height: 50, + child: ElevatedButton( + style: + mainBtnStyle(second, Colors.transparent, primary), + onPressed: () { + if (_formKey.currentState!.saveAndValidate()) { + + if (_formKey.currentState!.saveAndValidate()) { + String fname = + _formKey.currentState!.value['firstname']; + String lastName = + _formKey.currentState!.value['lastname']; + String? mname = + _formKey.currentState?.value['middlename']; + String bday = bdayController.text; + String? gender = selectedGender =="NONE"?null:selectedGender; + String? extension = selectedExtension=="NONE"?null:selectedExtension; + String? blood = selectedBloodType =="NONE"?null:selectedBloodType; + String? civilStatus = selectedCivilStatus =="NONE"?null:selectedCivilStatus; + String? sex = selectedSex; + String? companyAddress = _formKey.currentState?.value['company_address']; + String? companyContactNumber= _formKey.currentState?.value['company_tel']; + Company? company = selectedAgency == null? null:Company(id: selectedAgency?.id,name: selectedAgency?.name,category: selectedAgencyCategory,privateEntity: isPrivate); + Position? position = selectedPosition; + double? height = + _formKey.currentState?.value['height']==null? null: + double.parse( + _formKey.currentState?.value['height']); + double? weight = + _formKey.currentState?.value['weight']==null?null: + double.parse( + _formKey.currentState?.value['weight']); + Relationship relationship = Relationship( + id: 1, + type: "Paternal_Parent", + category: "Family"); + List? emergnecyContacts; + bool incaseOfEmergency = false; + RelatedPerson person = RelatedPerson( + titlePrefix: null, + firstName: fname, + maidenName: null, + middleName: mname, + lastName: lastName, + birthdate: DateTime.parse(bday), + id: null, + sex: sex, + gender: gender, + deceased: deceased, + heightM: height, + weightKg: weight, + esigPath: null, + bloodType: blood, + photoPath: null, + uuidQrcode: null, + nameExtension: extension, + civilStatus: civilStatus, + titleSuffix: null, + showTitleId: false, + ); + FamilyBackground familyBackground = + FamilyBackground(company: company,position: position,relatedPerson: person,relationship: relationship,companyAddress: companyAddress,emergencyContact: emergnecyContacts,incaseOfEmergency: incaseOfEmergency,companyContactNumber: companyContactNumber); + Navigator.of(context).pop(); + + widget.familyBloc.add(AddFamily(familyBackground: familyBackground, profileId: widget.profileId, token: widget.token,relationshipId: 3)); + } + + } + }, + child: const Text(submit)), + ), + ], + )), + )); + } +} diff --git a/lib/screens/profile/components/basic_information/family/spouse_edit_modal.dart b/lib/screens/profile/components/basic_information/family/spouse_edit_modal.dart new file mode 100644 index 0000000..56f8575 --- /dev/null +++ b/lib/screens/profile/components/basic_information/family/spouse_edit_modal.dart @@ -0,0 +1,800 @@ +import 'package:date_time_picker/date_time_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:searchfield/searchfield.dart'; + +import '../../../../../bloc/profile/family/family_bloc.dart'; +import '../../../../../model/profile/family_backround.dart'; +import '../../../../../model/utils/agency.dart'; +import '../../../../../model/utils/category.dart'; +import '../../../../../model/utils/position.dart'; +import '../../../../../theme-data.dart/box_shadow.dart'; +import '../../../../../theme-data.dart/btn-style.dart'; +import '../../../../../theme-data.dart/colors.dart'; +import '../../../../../theme-data.dart/form-style.dart'; +import '../../../../../utils/global.dart'; +import '../../../../../utils/text_container.dart'; +import '../../../../../utils/validators.dart'; +import '../../../shared/add_for_empty_search.dart'; + +class SpouseEditAlert extends StatefulWidget { + final List nameExtensions; + final List gender; + final List sexes; + final List bloodType; + final List civilStatus; + final List positions; + final List agencies; + final List category; + final FamilyBloc familyBloc; + final String token; + final int profileId; + final FamilyBackground familyBackground; + + const SpouseEditAlert( + {super.key, + required this.familyBackground, + required this.token, + required this.profileId, + required this.familyBloc, + required this.bloodType, + required this.civilStatus, + required this.agencies, + required this.category, + required this.positions, + required this.gender, + required this.nameExtensions, + required this.sexes}); + + @override + State createState() => _SpouseEditAlertState(); +} + +class _SpouseEditAlertState extends State { + final _formKey = GlobalKey(); + final bdayController = TextEditingController(); + + ////selected + String? selectedExtension; + String? selectedGender; + String? selectedSex; + String? selectedBloodType; + String? selectedCivilStatus; + Position? selectedPosition; + Category? selectedAgencyCategory; + Agency? selectedAgency; + bool deceased = false; + + final agencyFocusNode = FocusNode(); + final positionFocusNode = FocusNode(); + final appointmentStatusNode = FocusNode(); + final agencyCategoryFocusNode = FocusNode(); + + final addAgencyController = TextEditingController(); + final addPositionController = TextEditingController(); + final oldPositionController = TextEditingController(); + final oldAppointmentStatusController = TextEditingController(); + final oldAgencyController = TextEditingController(); + bool hasOccupation = false; + bool showAgency = false; + bool? isPrivate = false; + bool showIsPrivateRadio = false; + @override + Widget build(BuildContext context) { + selectedExtension = widget.familyBackground.relatedPerson?.nameExtension; + selectedGender = widget.familyBackground.relatedPerson?.gender; + selectedSex = widget.familyBackground.relatedPerson?.sex; + selectedBloodType = widget.familyBackground.relatedPerson?.bloodType; + selectedCivilStatus = widget.familyBackground.relatedPerson?.civilStatus; + bdayController.text = + widget.familyBackground.relatedPerson!.birthdate!.toString(); + deceased = widget.familyBackground.relatedPerson!.deceased!; + hasOccupation = widget.familyBackground.position != null; + oldPositionController.text = widget.familyBackground.position == null?"":widget.familyBackground.position!.title!; + oldAgencyController.text = widget.familyBackground.company == null?"":widget.familyBackground.company!.name!; + selectedAgency = widget.familyBackground.company==null?null:Agency(id: widget.familyBackground.company!.id, name: widget.familyBackground.company!.name,category: widget.familyBackground.company!.category,privateEntity: widget.familyBackground.company!.privateEntity); + return AlertDialog( + title: const Text("Family - Spouse"), + contentPadding: const EdgeInsets.all(24), + content: SingleChildScrollView( + child: FormBuilder( + key: _formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ////name + FormBuilderTextField( + initialValue: + widget.familyBackground.relatedPerson!.lastName, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "lastname", + decoration: normalTextFieldStyle("Last name", ""), + ), + const SizedBox( + height: 8, + ), + //// firstname + FormBuilderTextField( + initialValue: + widget.familyBackground.relatedPerson!.firstName, + name: "firstname", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: normalTextFieldStyle("First name", ""), + ), + const SizedBox( + height: 8, + ), + ////middle name + FormBuilderTextField( + initialValue: + widget.familyBackground.relatedPerson?.middleName, + name: "middlename", + decoration: normalTextFieldStyle("Middle name", ""), + ), + + const SizedBox( + height: 8, + ), + //// extension + DropdownButtonFormField( + value: selectedExtension, + decoration: normalTextFieldStyle("Name extension", ""), + items: widget.nameExtensions + .map((element) => DropdownMenuItem( + value: element, + child: Text(element), + )) + .toList(), + onChanged: (e) { + selectedExtension = e; + }, + ), + + const SizedBox( + height: 8, + ), + ////Bday + DateTimePicker( + controller: bdayController, + use24HourFormat: false, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + timeHintText: "Birthdate", + decoration: + normalTextFieldStyle("Birthdate *", "*").copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + firstDate: DateTime(1970), + lastDate: DateTime(2100), + icon: const Icon(Icons.date_range), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + //// sex + child: DropdownButtonFormField( + decoration: normalTextFieldStyle("Sex", ""), + value: selectedSex, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + items: widget.sexes + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedSex = e; + }, + ), + ), + const SizedBox( + width: 8, + ), + ////gender + Flexible( + flex: 1, + child: DropdownButtonFormField( + isExpanded: true, + decoration: normalTextFieldStyle("Gender", ""), + value: selectedGender, + items: widget.gender + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedGender = e; + }, + ), + ), + ]), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + ////Blood Type + Flexible( + flex: 1, + child: DropdownButtonFormField( + isExpanded: true, + decoration: normalTextFieldStyle("Blood type", ""), + value: selectedBloodType, + items: widget.bloodType + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedBloodType = e; + }, + ), + ), + const SizedBox( + width: 8, + ), + //// Civil Status + Flexible( + flex: 1, + child: DropdownButtonFormField( + isExpanded: true, + decoration: normalTextFieldStyle("Civil status", ""), + value: selectedCivilStatus, + items: widget.civilStatus + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedCivilStatus = e; + }, + ), + ), + ]), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + ////height + child: FormBuilderTextField( + initialValue: widget.familyBackground.relatedPerson + ?.heightM == + null + ? null + : widget.familyBackground.relatedPerson?.heightM + .toString(), + name: "height", + validator: integerAndNumeric, + keyboardType: TextInputType.number, + decoration: normalTextFieldStyle("Height", ""), + ), + ), + const SizedBox( + width: 8, + ), + Flexible( + flex: 1, + //// weight + child: FormBuilderTextField( + initialValue: widget.familyBackground.relatedPerson + ?.weightKg == + null + ? null + : widget.familyBackground.relatedPerson?.weightKg + .toString(), + name: "weight", + validator: integerAndNumeric, + keyboardType: TextInputType.number, + decoration: normalTextFieldStyle("Weight", ""), + ), + ), + const SizedBox( + height: 8, + ), + ]), + ), + const SizedBox( + height: 8, + ), + ////Deceased + SizedBox( + width: screenWidth, + child: StatefulBuilder(builder: (context, setState) { + return FormBuilderSwitch( + initialValue: deceased, + title: Text(deceased ? "YES" : "NO"), + decoration: normalTextFieldStyle("Deceased?", ""), + ////onvhange private sector + onChanged: (value) { + setState(() { + deceased = value!; + }); + }, + + name: 'deceased', + validator: FormBuilderValidators.required(), + ); + }), + ), + const SizedBox( + height: 8, + ), + ////Has Occupation + SizedBox( + width: screenWidth, + child: StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: hasOccupation, + title: Text(hasOccupation ? "YES" : "NO"), + decoration: + normalTextFieldStyle("Has Occupation?", ""), + ////onvhange private sector + onChanged: (value) { + setState(() { + hasOccupation = value!; + }); + }, + + name: 'hasOccupation', + validator: FormBuilderValidators.required(), + ), + Container( + child: hasOccupation + ? Column( + children: [ + const SizedBox( + height: 8, + ), + StatefulBuilder( + builder: (context, setState) { + ////Position + return SearchField( + controller: oldPositionController, + itemHeight: 50, + suggestionsDecoration: box1(), + suggestions: widget.positions + .map((Position position) => + SearchFieldListItem( + position.title!, + item: position, + child: Padding( + padding: + const EdgeInsets + .symmetric( + horizontal: + 10), + child: ListTile( + title: Text(position + .title!), + )))) + .toList(), + focusNode: positionFocusNode, + searchInputDecoration: + normalTextFieldStyle( + "Position *", "") + .copyWith( + suffixIcon: IconButton( + icon: const Icon( + Icons.arrow_drop_down), + onPressed: () { + positionFocusNode.unfocus(); + }, + )), + onSuggestionTap: (position) { + setState(() { + selectedPosition = position.item; + positionFocusNode.unfocus(); + }); + }, + ////EMPTY WIDGET + emptyWidget: EmptyWidget( + title: "Add Position", + controller: addPositionController, + onpressed: () { + setState(() { + Position newAgencyPosition = + Position( + id: null, + title: + addPositionController + .text + .toUpperCase()); + + widget.positions.insert( + 0, newAgencyPosition); + + addPositionController.text = + ""; + Navigator.pop(context); + }); + }), + validator: (position) { + if (position!.isEmpty) { + return "This field is required"; + } + return null; + }, + ); + }), + const SizedBox( + height: 8, + ), + StatefulBuilder( + builder: (context, setState) { + return Column( + children: [ + ////Company + SearchField( + controller: oldAgencyController, + itemHeight: 70, + focusNode: agencyFocusNode, + suggestions: widget.agencies + .map((Agency agency) => + SearchFieldListItem( + agency.name!, + item: agency, + child: ListTile( + title: Text( + agency.name!, + overflow: + TextOverflow + .ellipsis, + ), + subtitle: Text(agency + .privateEntity == + true + ? "Private" + : agency.privateEntity == + false + ? "Government" + : ""), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle( + "Agency *", "") + .copyWith( + suffixIcon: + const Icon(Icons + .arrow_drop_down)), + onSuggestionTap: (agency) { + setState(() { + selectedAgency = + agency.item; + + if (selectedAgency! + .privateEntity == + null) { + showIsPrivateRadio = true; + } else { + showIsPrivateRadio = + false; + } + + agencyFocusNode.unfocus(); + }); + }, + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + emptyWidget: EmptyWidget( + controller: + addAgencyController, + onpressed: () { + setState(() { + Agency newAgency = Agency( + id: null, + name: addAgencyController + .text + .toUpperCase(), + category: null, + privateEntity: + null); + + widget.agencies.insert( + 0, newAgency); + selectedAgency = + newAgency; + addAgencyController + .text = ""; + showAgency = true; + + showIsPrivateRadio = + true; + + Navigator.pop(context); + }); + }, + title: "Add Agency")), + + SizedBox( + height: showAgency ? 12 : 0, + ), + ////SHOW CATEGORY AGENCY + SizedBox( + child: showAgency + ? SearchField( + focusNode: + agencyCategoryFocusNode, + itemHeight: 70, + suggestions: widget + .category + .map((Category + category) => + SearchFieldListItem( + category + .name!, + item: + category, + child: + ListTile( + title: Text( + category + .name!), + subtitle: Text(category + .industryClass! + .name!), + ))) + .toList(), + emptyWidget: Container( + height: 100, + decoration: box1(), + child: const Center( + child: Text( + "No result found ...")), + ), + onSuggestionTap: + (agencyCategory) { + setState(() { + selectedAgencyCategory = + agencyCategory + .item; + agencyCategoryFocusNode + .unfocus(); + selectedAgency = Agency( + id: null, + name: + selectedAgency! + .name, + category: + selectedAgencyCategory, + privateEntity: + null); + }); + }, + searchInputDecoration: + normalTextFieldStyle( + "Category *", + "") + .copyWith( + suffixIcon: + const Icon( + Icons + .arrow_drop_down)), + validator: (value) { + if (value!.isEmpty) { + return "This field is required"; + } + return null; + }, + ) + : const SizedBox(), + ), + const SizedBox( + height: 8, + ), + ////PRVIATE SECTOR + SizedBox( + width: screenWidth, + child: showIsPrivateRadio + ? FormBuilderSwitch( + initialValue: isPrivate, + title: Text(isPrivate! + ? "YES" + : "NO"), + decoration: + normalTextFieldStyle( + "Private Entity?", + ""), + ////onvhange private sector + onChanged: (value) { + setState(() { + isPrivate = value; + selectedAgency = Agency( + id: null, + name: + selectedAgency! + .name, + category: + selectedAgencyCategory, + privateEntity: + isPrivate); + agencyFocusNode + .unfocus(); + agencyCategoryFocusNode + .unfocus(); + }); + }, + + name: 'isPrivate', + validator: + FormBuilderValidators + .required(), + ) + : const SizedBox()), + + const SizedBox( + height: 8, + ), + ////Company Address + FormBuilderTextField( + initialValue: widget.familyBackground.companyAddress, + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + name: "company_address", + decoration: normalTextFieldStyle( + "Company Address/Business Address *", + "Company Address/Business Address *"), + ), + const SizedBox( + height: 8, + ), + ////Company Tel Number + FormBuilderTextField( + initialValue: + widget.familyBackground.companyContactNumber, + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + name: "company_tel", + decoration: normalTextFieldStyle( + "Company /Business Tel./Mobile # *", + "Company /Business Tel./Mobile # *"), + ), + ], + ); + }), + ], + ) + : const SizedBox(), + ), + ], + ); + }), + ), + + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + height: 50, + child: ElevatedButton( + style: + mainBtnStyle(second, Colors.transparent, primary), + onPressed: () { + if (_formKey.currentState!.saveAndValidate()) { + if (_formKey.currentState!.saveAndValidate()) { + String fname = + _formKey.currentState!.value['firstname']; + String lastName = + _formKey.currentState!.value['lastname']; + String? mname = + _formKey.currentState?.value['middlename']; + String bday = bdayController.text; + String? gender = selectedGender == "NONE" + ? null + : selectedGender; + String? extension = selectedExtension == "NONE" + ? null + : selectedExtension; + String? blood = selectedBloodType == "NONE" + ? null + : selectedBloodType; + String? civilStatus = + selectedCivilStatus == "NONE" + ? null + : selectedCivilStatus; + String? sex = selectedSex; + String? companyAddress = _formKey + .currentState?.value['company_address']; + String? companyContactNumber = + _formKey.currentState?.value['company_tel']; + Company? company = selectedAgency == null + ? null + : Company( + id: selectedAgency?.id, + name: selectedAgency?.name, + category: selectedAgencyCategory, + privateEntity: isPrivate); + Position? position = selectedPosition; + double? height = _formKey + .currentState?.value['height'] == + null + ? null + : double.parse( + _formKey.currentState?.value['height']); + double? weight = _formKey + .currentState?.value['weight'] == + null + ? null + : double.parse( + _formKey.currentState?.value['weight']); + Relationship relationship = Relationship( + id: 1, + type: "Paternal_Parent", + category: "Family"); + List? emergnecyContacts; + bool incaseOfEmergency = false; + RelatedPerson person = RelatedPerson( + titlePrefix: null, + firstName: fname, + maidenName: null, + middleName: mname, + lastName: lastName, + birthdate: DateTime.parse(bday), + id: widget.familyBackground.relatedPerson!.id, + sex: sex, + gender: gender, + deceased: deceased, + heightM: height, + weightKg: weight, + esigPath: null, + bloodType: blood, + photoPath: null, + uuidQrcode: null, + nameExtension: extension, + civilStatus: civilStatus, + titleSuffix: null, + showTitleId: false, + ); + FamilyBackground familyBackground = + FamilyBackground( + company: hasOccupation?company:null, + position: hasOccupation? position:null, + relatedPerson: person, + relationship: relationship, + companyAddress: hasOccupation? companyAddress:null, + emergencyContact: emergnecyContacts, + incaseOfEmergency: incaseOfEmergency, + companyContactNumber:hasOccupation? + companyContactNumber:null); + Navigator.of(context).pop(); + + widget.familyBloc.add(Updatefamily( + familyBackground: familyBackground, + profileId: widget.profileId, + token: widget.token, + relationshipId: 3)); + } + } + }, + child: const Text(submit)), + ), + ], + )), + )); + } +} diff --git a/lib/screens/profile/components/basic_information/identification/add_modal.dart b/lib/screens/profile/components/basic_information/identification/add_modal.dart new file mode 100644 index 0000000..fef97a8 --- /dev/null +++ b/lib/screens/profile/components/basic_information/identification/add_modal.dart @@ -0,0 +1,826 @@ +import 'package:date_time_picker/date_time_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; +import 'package:searchfield/searchfield.dart'; +import 'package:unit2/bloc/profile/primary_information/identification/identification_bloc.dart'; +import 'package:unit2/model/utils/industry_class.dart'; +import 'package:unit2/theme-data.dart/btn-style.dart'; +import 'package:unit2/utils/text_container.dart'; + +import '../../../../../model/location/city.dart'; +import '../../../../../model/location/country.dart'; +import '../../../../../model/location/provinces.dart'; +import '../../../../../model/location/region.dart'; +import '../../../../../model/profile/basic_information/identification_information.dart'; +import '../../../../../model/profile/voluntary_works.dart'; +import '../../../../../model/utils/agency.dart'; +import '../../../../../model/utils/category.dart'; +import '../../../../../theme-data.dart/box_shadow.dart'; +import '../../../../../theme-data.dart/colors.dart'; +import '../../../../../theme-data.dart/form-style.dart'; +import '../../../../../utils/global.dart'; +import '../../../../../utils/global_context.dart'; +import '../../../../../utils/location_utilities.dart'; +import '../../../shared/add_for_empty_search.dart'; + +class AddIdentificationScreen extends StatefulWidget { + final int profileId; + final String token; + const AddIdentificationScreen( + {super.key, required this.profileId, required this.token}); + + @override + State createState() => + _AddIdentificationScreenState(); +} + +class _AddIdentificationScreenState extends State { + final addAgencyController = TextEditingController(); + final dateIssuedController = TextEditingController(); + final expirationController = TextEditingController(); + final agencyFocusNode = FocusNode(); + final agencyCategoryFocusNode = FocusNode(); + bool showAgency = false; + bool showIsPrivateRadio = false; + bool asPdfReference = false; + bool isPrivate = false; + bool overseas = false; + bool provinceCall = false; + bool cityCall = false; + bool otherAgency = false; + ////selected + Agency? selectedAgency; + Category? selectedCategoty; + Region? selectedRegion; + Province? selectedProvince; + CityMunicipality? selectedMunicipality; + Country? selectedCountry; + List? provinces; + List? citymuns; + + final formKey = GlobalKey(); + List? requiredAgency = [ + Agency( + id: 173, + name: "GOVERNMENT SERVICE INSURANCE SYSTEM (GSIS)", + category: Category( + id: 16, + name: "Government-Owned and Controlled Corporations (GOCC)", + industryClass: IndustryClass( + description: null, name: "Public Governance", id: 16)), + privateEntity: false), + Agency( + id: 2, + name: "SOCIAL SECURITY SYSTEM (SSS)", + category: Category( + id: 202, + name: "Government-Owned and Controlled Corporations (GOCC)", + industryClass: IndustryClass( + id: 16, name: "Public Governance", description: null)), + privateEntity: false), + Agency( + id: 3, + name: "HOME DEVELOPMENT MUTUAL FUND (PAG-IBIG FUND)", + category: Category( + id: 202, + name: "Government-Owned and Controlled Corporations (GOCC)", + industryClass: IndustryClass( + name: "Public Governance", id: 16, description: null)), + privateEntity: false), + Agency( + id: 4, + name: "BUREAU OF INTERNAL REVENUE (BIR)", + category: Category( + id: 201, + name: "National Government Agency", + industryClass: IndustryClass( + id: 16, name: "Public Governance", description: null), + ), + privateEntity: false), + Agency( + id: 165, + name: "PHILIPPINE HEALTH INSURANCE CORPORATION (PHILHEALTH)", + category: Category( + id: 202, + name: "Government-Owned and Controlled Corporations (GOCC)", + industryClass: IndustryClass( + id: 16, name: "Public Governance", description: null)), + privateEntity: false), + Agency(id: 0, name: "OTHERS"), + ]; + List requiredIds = [173, 2, 3, 4, 165]; + + @override + Widget build(BuildContext context) { + return BlocBuilder( + buildWhen: (previous, current) { + return false; + }, + builder: (context, state) { + + if (state is IdentificationAddingState) { + + for (var agency in state.addedAgencies) { + if (requiredIds.contains(agency.id)) { + Agency? newAgency = requiredAgency + ?.firstWhere((element) => element.id == agency.id); + if (newAgency != null) { + requiredAgency!.remove(newAgency); + } + } + } + return Padding( + padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 24), + child: FormBuilder( + key: formKey, + child: SizedBox( + height: screenHeight * 90, + child: ListView( + children: [ + Column( + children: [ + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderDropdown( + isExpanded: true, + decoration: normalTextFieldStyle( + "Select Agency", "Select Agency"), + name: "requiredAgency", + items: requiredAgency! + .map>( + (Agency agency) { + return DropdownMenuItem( + value: agency, + child: Container( + width: double.infinity, + decoration: + box1().copyWith(boxShadow: []), + child: Text(agency.name!)), + ); + }).toList(), + onChanged: (value) { + selectedAgency = value; + + if (value!.id == 0) { + setState(() { + otherAgency = true; + }); + } else { + isPrivate = value.privateEntity!; + setState(() { + otherAgency = false; + }); + } + }, + ), + ////Other agency + SizedBox( + child: otherAgency + ? StatefulBuilder( + builder: (context, setState) { + return Column( + children: [ + ////Company + SizedBox( + child: SearchField( + itemHeight: 70, + focusNode: agencyFocusNode, + suggestions: state.agencies + .map((Agency agency) => + SearchFieldListItem( + agency.name!, + item: agency, + child: ListTile( + title: Text( + agency + .name!, + overflow: + TextOverflow + .ellipsis, + ), + subtitle: Text(agency + .privateEntity == + true + ? "Private" + : agency.privateEntity == + false + ? "Government" + : ""), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle( + "Agency *", "") + .copyWith( + suffixIcon: + const Icon(Icons + .arrow_drop_down)), + onSuggestionTap: (agency) { + setState(() { + selectedAgency = + agency.item; + + if (selectedAgency! + .privateEntity == + null) { + showIsPrivateRadio = + true; + } else { + showIsPrivateRadio = + false; + } + + agencyFocusNode + .unfocus(); + }); + }, + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + emptyWidget: EmptyWidget( + controller: + addAgencyController, + onpressed: () { + setState(() { + Agency newAgency = Agency( + id: null, + name: addAgencyController + .text + .toUpperCase(), + category: null, + privateEntity: + null); + + state.agencies + .insert(0, + newAgency); + selectedAgency = + newAgency; + addAgencyController + .text = ""; + showAgency = true; + + showIsPrivateRadio = + true; + + Navigator.pop( + context); + }); + }, + title: "Add Agency")), + ), + + SizedBox( + height: showAgency ? 8 : 0, + ), + ////SHOW CATEGORY AGENCY + SizedBox( + child: showAgency + ? SearchField( + focusNode: + agencyCategoryFocusNode, + itemHeight: 70, + suggestions: state + .agencyCategory + .map((Category + category) => + SearchFieldListItem( + category + .name!, + item: + category, + child: + ListTile( + title: Text( + category + .name!), + subtitle: Text(category + .industryClass! + .name!), + ))) + .toList(), + emptyWidget: Container( + height: 100, + decoration: box1(), + child: const Center( + child: Text( + "No result found ...")), + ), + onSuggestionTap: + (agencyCategory) { + setState(() { + selectedCategoty = + agencyCategory + .item; + agencyCategoryFocusNode + .unfocus(); + selectedAgency = Agency( + id: null, + name: + selectedAgency! + .name, + category: + selectedCategoty, + privateEntity: + null); + }); + }, + searchInputDecoration: + normalTextFieldStyle( + "Category *", + "") + .copyWith( + suffixIcon: + const Icon( + Icons.arrow_drop_down)), + validator: (value) { + if (value!.isEmpty) { + return "This field is required"; + } + return null; + }, + ) + : const SizedBox(), + ), + const SizedBox( + height: 8, + ), + ////PRVIATE SECTOR + SizedBox( + width: screenWidth, + child: showIsPrivateRadio + ? FormBuilderSwitch( + initialValue: + isPrivate, + title: Text(isPrivate + ? "YES" + : "NO"), + decoration: + normalTextFieldStyle( + "Private Entity?", + ""), + ////onvhange private sector + onChanged: (value) { + setState(() { + isPrivate = + value!; + selectedAgency = Agency( + id: null, + name: + selectedAgency! + .name, + category: + selectedCategoty, + privateEntity: + isPrivate); + agencyFocusNode + .unfocus(); + agencyCategoryFocusNode + .unfocus(); + }); + }, + + name: 'isPrivate', + validator: + FormBuilderValidators + .required(), + ) + : const SizedBox()), + ], + ); + }) + : const SizedBox(), + ), + ], + ); + }), + SizedBox( + height: otherAgency ? 8 : 0, + ), + + const SizedBox( + height: 8, + ), + //// Identification numner + FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "identification_number", + decoration: normalTextFieldStyle( + "Identification Number *", ""), + ), + const SizedBox( + height: 8, + ), + Row( + children: [ + //// Date Issued + Flexible( + flex: 1, + child: DateTimePicker( + controller: dateIssuedController, + use24HourFormat: false, + icon: const Icon(Icons.date_range), + firstDate: DateTime(1970), + lastDate: DateTime(2100), + timeHintText: "Date Issued", + decoration: normalTextFieldStyle( + "Date Issued *", "Date Issued *") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + initialValue: null, + )), + const SizedBox( + width: 8, + ), + //// Expiration Date + Flexible( + flex: 1, + child: DateTimePicker( + controller: expirationController, + use24HourFormat: false, + icon: const Icon(Icons.date_range), + firstDate: DateTime(1970), + lastDate: DateTime(2100), + timeHintText: "Expiration date", + decoration: normalTextFieldStyle( + "Expiration Date *", + "Expiration Date *") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + initialValue: null, + )), + ], + ), + const SizedBox( + height: 8, + ), + //// as pdf reference + StatefulBuilder(builder: (context, setState) { + return FormBuilderSwitch( + initialValue: asPdfReference, + activeColor: second, + onChanged: (value) { + setState(() { + asPdfReference = value!; + }); + }, + decoration: + normalTextFieldStyle("As PDF Reference?", ''), + name: 'pdf_reference', + title: Text(asPdfReference ? "YES" : "NO"), + ); + }), + const SizedBox( + height: 8, + ), + //// OVERSEAS + //// OVERSEAS + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: normalTextFieldStyle( + "Overseas Address?", ''), + name: 'overseas', + title: Text(overseas ? "YES" : "NO"), + ), + SizedBox( + height: overseas == true ? 8 : 0, + ), + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + DropdownButtonFormField( + isExpanded: true, + autovalidateMode: AutovalidateMode + .onUserInteraction, + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + onChanged: + (Region? region) async { + if (selectedRegion != region) { + setState(() { + provinceCall = true; + }); + selectedRegion = region; + try { + provinces = await LocationUtils + .instance + .getProvinces( + regionCode: + selectedRegion! + .code + .toString()); + selectedProvince = + provinces![0]; + setState(() { + provinceCall = false; + cityCall = true; + }); + try { + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code + .toString()); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + }); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read< + IdentificationBloc>() + .add(ShowErrorState( + message: e + .toString())); + } + } catch (e) { + context + .read< + IdentificationBloc>() + .add(ShowErrorState( + message: + e.toString())); + } + } + }, + value: selectedRegion, + decoration: normalTextFieldStyle( + "Region*", "Region"), + items: state.regions.map< + DropdownMenuItem>( + (Region region) { + return DropdownMenuItem( + value: region, + child: Text( + region.description!)); + }).toList(), + ), + const SizedBox( + height: 8, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + value: selectedProvince, + onChanged: (Province? + province) async { + if (selectedProvince != + province) { + setState(() { + cityCall = true; + }); + selectedProvince = + province; + try { + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code + .toString()); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + }); + } catch (e) { + context + .read< + IdentificationBloc>() + .add(ShowErrorState( + message: e + .toString())); + } + } + }, + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province + province) { + return DropdownMenuItem( + value: province, + child: + FittedBox( + child: Text( + province + .description!), + )); + }).toList(), + decoration: + normalTextFieldStyle( + "Province*", + "Province")), + ), + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: DropdownButtonFormField< + CityMunicipality>( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + isExpanded: true, + onChanged: + (CityMunicipality? city) { + if (selectedMunicipality != + city) { + selectedMunicipality = + city; + } + }, + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), + ), + ), + ), + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( + height: 60, + child: + DropdownButtonFormField( + isExpanded: true, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + items: state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox( + child: + Text(country.name!))); + }).toList(), + value: selectedCountry, + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) { + selectedCountry = value; + }, + ), + ), + ), + ], + ); + }), + ], + ), + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState! + .saveAndValidate()) { + Country country = selectedCountry ??= + Country( + id: 175, + name: 'Philippines', + code: 'PH'); + IssuedAt issuedAt = IssuedAt( + id:null, + barangay: null, + + addressCategory: null, + issuedAtClass: null, + cityMunicipality: overseas?null:selectedMunicipality + , + country: country); + if (selectedCategoty != null) { + selectedAgency = Agency( + id: selectedAgency?.id, + name: selectedAgency!.name, + category: selectedCategoty, + privateEntity: isPrivate); + } + Identification identification = Identification(id: null, agency: selectedAgency, issuedAt: issuedAt, asPdfReference: asPdfReference, identificationNumber: formKey.currentState!.value['identification_number'],dateIssued: dateIssuedController.text.isEmpty?null:DateTime.parse(dateIssuedController.text),expirationDate: expirationController.text.isEmpty?null:DateTime.tryParse(expirationController.text)); + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add(AddIdentification(identification: identification, profileId: widget.profileId, token: widget.token)); + } + + }, + child: const Text(submit)), + ) + + ], + ), + )), + ); + } + return Container(); + }, + ); + } + + Future getProvinces() async { + try { + List newProvinces = await LocationUtils.instance + .getProvinces(regionCode: selectedRegion!.code.toString()); + setState(() { + provinces = newProvinces; + selectedProvince = provinces![0]; + provinceCall = false; + cityCall = true; + getCities(); + }); + } catch (e) { + context + .read() + .add(ShowErrorState(message: e.toString())); + } + } + + Future getCities() async { + try { + List newCities = await LocationUtils.instance + .getCities(code: selectedProvince!.code.toString()); + citymuns = newCities; + setState(() { + selectedMunicipality = newCities[0]; + cityCall = false; + }); + } catch (e) { + context + .read() + .add(ShowErrorState(message: e.toString())); + } + } +} diff --git a/lib/screens/profile/components/basic_information/identification/edit_modal.dart b/lib/screens/profile/components/basic_information/identification/edit_modal.dart new file mode 100644 index 0000000..31fee18 --- /dev/null +++ b/lib/screens/profile/components/basic_information/identification/edit_modal.dart @@ -0,0 +1,528 @@ +import 'package:date_time_picker/date_time_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; +import 'package:searchfield/searchfield.dart'; +import 'package:unit2/bloc/profile/primary_information/identification/identification_bloc.dart'; +import 'package:unit2/model/utils/industry_class.dart'; +import 'package:unit2/theme-data.dart/btn-style.dart'; +import 'package:unit2/utils/text_container.dart'; + +import '../../../../../model/location/city.dart'; +import '../../../../../model/location/country.dart'; +import '../../../../../model/location/provinces.dart'; +import '../../../../../model/location/region.dart'; +import '../../../../../model/profile/basic_information/identification_information.dart'; +import '../../../../../model/profile/voluntary_works.dart'; +import '../../../../../model/utils/agency.dart'; +import '../../../../../model/utils/category.dart'; +import '../../../../../theme-data.dart/box_shadow.dart'; +import '../../../../../theme-data.dart/colors.dart'; +import '../../../../../theme-data.dart/form-style.dart'; +import '../../../../../utils/global.dart'; +import '../../../../../utils/global_context.dart'; +import '../../../../../utils/location_utilities.dart'; +import '../../../shared/add_for_empty_search.dart'; + +class EditIdentificationScreen extends StatefulWidget { + final int profileId; + final String token; + const EditIdentificationScreen( + {super.key, required this.profileId, required this.token}); + + @override + State createState() => + _EditIdentificationScreenState(); +} + +class _EditIdentificationScreenState extends State { + final addAgencyController = TextEditingController(); + final dateIssuedController = TextEditingController(); + final expirationController = TextEditingController(); + final agencyFocusNode = FocusNode(); + final agencyCategoryFocusNode = FocusNode(); + bool asPdfReference = false; + bool overseas = false; + bool provinceCall = false; + bool cityCall = false; + bool otherAgency = false; + ////selected + Agency? selectedAgency; + Category? selectedCategoty; + Region? selectedRegion; + Province? selectedProvince; + CityMunicipality? selectedMunicipality; + Country? selectedCountry; + List? provinces; + List? citymuns; + + final formKey = GlobalKey(); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is IdentificationEditingState) { + String? issuedDate = state.identification.dateIssued.toString(); + String? expDate = state.identification.expirationDate.toString(); + provinces = state.provinces; + citymuns = state.cities; + selectedCountry = state.currentCountry; + dateIssuedController.text = + issuedDate.isEmpty || issuedDate == "null" ? "" : issuedDate; + expirationController.text = + expDate.isEmpty || expDate == "null" ? "" : expDate; + selectedRegion = state.currentRegion; + selectedProvince = state.currentProvince; + selectedMunicipality = state.currentCity; + overseas = state.overseas; + asPdfReference = state.identification.asPdfReference!; + return Padding( + padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 24), + child: FormBuilder( + key: formKey, + child: SizedBox( + height: screenHeight * 90, + child: ListView( + children: [ + Column( + children: [ + FormBuilderTextField( + initialValue: state.identification.agency!.name, + enabled: false, + name: "", + decoration: normalTextFieldStyle("", "").copyWith( + filled: true, fillColor: Colors.black12), + ), + SizedBox( + height: otherAgency ? 8 : 0, + ), + + const SizedBox( + height: 12, + ), + //// Identification numner + FormBuilderTextField( + initialValue: + state.identification.identificationNumber, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "identification_number", + decoration: normalTextFieldStyle( + "Identification Number *", ""), + ), + const SizedBox( + height: 12, + ), + Row( + children: [ + //// Date Issued + Flexible( + flex: 1, + child: DateTimePicker( + controller: dateIssuedController, + use24HourFormat: false, + icon: const Icon(Icons.date_range), + firstDate: DateTime(1970), + lastDate: DateTime(2100), + timeHintText: "Date Issued", + decoration: normalTextFieldStyle( + "Date Issued *", "Date Issued *") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + initialValue: null, + )), + const SizedBox( + width: 12, + ), + //// Expiration Date + Flexible( + flex: 1, + child: DateTimePicker( + controller: expirationController, + use24HourFormat: false, + icon: const Icon(Icons.date_range), + firstDate: DateTime(1970), + lastDate: DateTime(2100), + timeHintText: "Expiration date", + decoration: normalTextFieldStyle( + "Expiration Date *", + "Expiration Date *") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + initialValue: null, + )), + ], + ), + const SizedBox( + height: 12, + ), + + //// OVERSEAS + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: normalTextFieldStyle( + "Overseas Address?", ''), + name: 'overseas', + title: Text(overseas ? "YES" : "NO"), + ), + SizedBox( + height: overseas == true ? 8 : 0, + ), + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + DropdownButtonFormField( + isExpanded: true, + autovalidateMode: AutovalidateMode + .onUserInteraction, + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + onChanged: + (Region? region) async { + if (selectedRegion != region) { + setState(() { + provinceCall = true; + }); + selectedRegion = region; + try { + provinces = await LocationUtils + .instance + .getProvinces( + regionCode: + selectedRegion! + .code + .toString()); + selectedProvince = + provinces![0]; + setState(() { + provinceCall = false; + cityCall = true; + }); + try { + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code + .toString()); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + }); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read< + IdentificationBloc>() + .add(ShowErrorState( + message: e + .toString())); + } + } catch (e) { + context + .read< + IdentificationBloc>() + .add(ShowErrorState( + message: + e.toString())); + } + } + }, + value: selectedRegion, + decoration: normalTextFieldStyle( + "Region*", "Region"), + items: state.regions.map< + DropdownMenuItem>( + (Region region) { + return DropdownMenuItem( + value: region, + child: Text( + region.description!)); + }).toList(), + ), + const SizedBox( + height: 8, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( + value: selectedProvince, + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + onChanged: (Province? + province) async { + if (selectedProvince != + province) { + setState(() { + cityCall = true; + }); + selectedProvince = + province; + try { + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code + .toString()); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + }); + } catch (e) { + context + .read< + IdentificationBloc>() + .add(ShowErrorState( + message: e + .toString())); + } + } + }, + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province + province) { + return DropdownMenuItem( + value: province, + child: + FittedBox( + child: Text( + province + .description!), + )); + }).toList(), + decoration: + normalTextFieldStyle( + "Province*", + "Province")), + ), + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: DropdownButtonFormField< + CityMunicipality>( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + isExpanded: true, + onChanged: + (CityMunicipality? city) { + if (selectedMunicipality != + city) { + selectedMunicipality = + city; + } + }, + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), + ), + ), + ), + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( + height: 60, + child: + DropdownButtonFormField( + isExpanded: true, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + items: state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox( + child: + Text(country.name!))); + }).toList(), + value: selectedCountry, + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) { + selectedCountry = value; + }, + ), + ), + ), + ], + ); + }), + const SizedBox( + height: 12, + ), + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState!.saveAndValidate()) { + IssuedAt? issuedAt; + if (!overseas) { + issuedAt = IssuedAt( + id: state.identification.issuedAt!.id, + barangay: null, + addressCategory: state.identification.issuedAt?.addressCategory, + issuedAtClass: null, + cityMunicipality: selectedMunicipality, + country: selectedCountry); + }else{ + issuedAt = IssuedAt( + id: state.identification.issuedAt!.id, + barangay: null, + addressCategory: state.identification.issuedAt?.addressCategory, + issuedAtClass: null, + cityMunicipality: null, + country: selectedCountry); + } + + Identification identification = + Identification( + id: state.identification.id, + agency: state.identification.agency, + issuedAt: issuedAt, + asPdfReference: asPdfReference, + identificationNumber: + formKey + .currentState!.value[ + 'identification_number'], + dateIssued: + dateIssuedController + .text.isEmpty + ? null + : DateTime + .parse( + dateIssuedController + .text), + expirationDate: expirationController + .text.isEmpty + ? null + : DateTime.tryParse( + expirationController.text)); + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add( + UpdateIdentifaction( + identification: identification, + profileId: widget.profileId, + token: widget.token)); + } + }, + child: const Text(submit)), + ) + ], + ), + ], + ), + )), + ); + } + return Container(); + }, + ); + } + + Future getProvinces() async { + try { + List newProvinces = await LocationUtils.instance + .getProvinces(regionCode: selectedRegion!.code.toString()); + setState(() { + provinces = newProvinces; + selectedProvince = provinces![0]; + provinceCall = false; + cityCall = true; + getCities(); + }); + } catch (e) { + context + .read() + .add(ShowErrorState(message: e.toString())); + } + } + + Future getCities() async { + try { + List newCities = await LocationUtils.instance + .getCities(code: selectedProvince!.code.toString()); + citymuns = newCities; + setState(() { + selectedMunicipality = newCities[0]; + cityCall = false; + }); + } catch (e) { + context + .read() + .add(ShowErrorState(message: e.toString())); + } + } +} diff --git a/lib/screens/profile/components/basic_information/identification_information_screen.dart b/lib/screens/profile/components/basic_information/identification_information_screen.dart index 2d59569..3cd6cab 100644 --- a/lib/screens/profile/components/basic_information/identification_information_screen.dart +++ b/lib/screens/profile/components/basic_information/identification_information_screen.dart @@ -1,149 +1,366 @@ +import 'package:app_popup_menu/app_popup_menu.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; import 'package:unit2/bloc/profile/primary_information/identification/identification_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/model/profile/basic_information/identification_information.dart'; +import 'package:unit2/screens/profile/components/basic_information/identification/add_modal.dart'; +import 'package:unit2/screens/profile/components/basic_information/identification/edit_modal.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; +import 'package:unit2/widgets/error_state.dart'; import '../../../../bloc/user/user_bloc.dart'; +import '../../../../utils/alerts.dart'; class IdentificationsScreen extends StatelessWidget { const IdentificationsScreen({super.key}); @override Widget build(BuildContext context) { + String? token; + int? profileId; return Scaffold( appBar: AppBar( title: const Text(identificationScreenTitle), centerTitle: true, backgroundColor: primary, - actions: [AddLeading(onPressed: () {})], + actions: [ + AddLeading(onPressed: () { + context + .read() + .add(ShowAddIdentificationForm()); + }) + ], ), - body: BlocBuilder( - builder: (context, state) { - if (state is UserLoggedIn) { - return BlocBuilder( - builder: (context, state) { - if (state is ProfileLoaded) { - return BlocConsumer( - listener: (context, state) {}, - builder: (context, state) { - if (state is IdentificationLoadedState) { - if (state.identificationInformation.isNotEmpty) { - return ListView.builder( - padding: const EdgeInsets.symmetric( - vertical: 8, horizontal: 10), - itemCount: state.identificationInformation.length, - itemBuilder: (BuildContext context, int index) { - String agency = - state.identificationInformation[index].agency!.name!; - String idNumber = - state.identificationInformation[index].identificationNumber!; - bool government = - state.identificationInformation[index].agency!.privateEntity!; - String issuedAt = - "${state.identificationInformation[index].issuedAt!.cityMunicipality!.description!} ${state.identificationInformation[index].issuedAt!.cityMunicipality!.province!.description}"; - return Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Container( - decoration: box1(), - padding: const EdgeInsets.symmetric( - horizontal: 12, vertical: 8), - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text(agency, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith( - fontWeight: - FontWeight - .w400)), - const Divider(), - const SizedBox( - height: 5, - ), - Row(children: [ - Expanded( - child: Text( - "$idNumberText : $idNumber", - style: - Theme.of(context) - .textTheme - .titleSmall, - ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocBuilder( + builder: (context, state) { + if (state is UserLoggedIn) { + token = state.userData!.user!.login!.token; + profileId = state.userData!.user!.login!.user!.profileId; + return BlocBuilder( + builder: (context, state) { + if (state is ProfileLoaded) { + return BlocConsumer( + listener: (context, state) { + if (state is IdentificationLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is IdentificationLoadedState || + state is IdentificationAddingState || + state is IdentificationEditingState || + state is IdenficationErrorState || + state is IdentificationAddedState || + state is IdentificationDeletedState || + state is IdentificationEditedState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + //// Added State + if (state is IdentificationAddedState) { + if (state.response['success']) { + successAlert(context, "Adding Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context + .read() + .add(LoadIdentifications()); + }); + } else { + errorAlert(context, "Adding Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context + .read() + .add(LoadIdentifications()); + }); + } + } + //// Updated State + if (state is IdentificationEditedState) { + if (state.response['success']) { + successAlert(context, "Update Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context + .read() + .add(LoadIdentifications()); + }); + } else { + errorAlert(context, "Update Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context + .read() + .add(LoadIdentifications()); + }); + } + } + + ////Deleted State + if (state is IdentificationDeletedState) { + if (state.success) { + successAlert(context, "Deletion Successfull!", + "Deleted Successfully", () { + Navigator.of(context).pop(); + context + .read() + .add(LoadIdentifications()); + }); + } else { + errorAlert(context, "Deletion Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context + .read() + .add(LoadIdentifications()); + }); + } + } + }, + builder: (context, state) { + if (state is IdentificationLoadedState) { + if (state.identificationInformation.isNotEmpty) { + return ListView.builder( + padding: const EdgeInsets.symmetric( + vertical: 8, horizontal: 10), + itemCount: + state.identificationInformation.length, + itemBuilder: + (BuildContext context, int index) { + String agency = state + .identificationInformation[index] + .agency! + .name!; + String idNumber = state + .identificationInformation[index] + .identificationNumber!; + bool government = state + .identificationInformation[index] + .agency! + .privateEntity!; + String issuedAt = + "${state.identificationInformation[index].issuedAt!.cityMunicipality?.description!} ${state.identificationInformation[index].issuedAt!.cityMunicipality?.province!.description}"; + return Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Container( + decoration: box1(), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + Text(agency, + style: Theme.of( + context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight + .w400)), + const Divider(), + const SizedBox( + height: 5, ), - Badge( - backgroundColor: - success2, - label: Text( - government == true - ? privateText - .toUpperCase() - : governmentText - .toUpperCase(), + Row(children: [ + Expanded( + child: Text( + "$idNumberText : $idNumber", style: Theme.of( context) .textTheme - .bodySmall! - .copyWith( - color: Colors - .white), - )) + .titleSmall, + ), + ), + Badge( + backgroundColor: + success2, + label: Text( + government == true + ? privateText + .toUpperCase() + : governmentText + .toUpperCase(), + style: Theme.of( + context) + .textTheme + .bodySmall! + .copyWith( + color: Colors + .white), + )) + ]), + const SizedBox( + height: 5, + ), + Text(issuedAt), ]), - const SizedBox( - height: 5, - ), - Text(issuedAt), - ]), - ), - IconButton( - onPressed: () {}, + ), + AppPopupMenu( + offset: const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + ////delete identification-= = = = = = = = =>> + if (value == 2) { + confirmAlert(context, () { + final progress = + ProgressHUD.of( + context); + progress!.showWithText( + "Loading..."); + BlocProvider.of( + context) + .add(DeleteIdentification( + identificationId: + state + .identificationInformation[ + index] + .id!, + profileId: + profileId!, + token: token!)); + }, "Delete?", + "Confirm Delete?"); + } + if (value == 1) { + bool isOverseas; + ////edit voluntary work-= = = = = = = = =>> + final progress = + ProgressHUD.of(context); + progress!.showWithText( + "Loading..."); + + if (state + .identificationInformation[ + index] + .issuedAt + ?.country!.id == 175) { + isOverseas = false; + } else { + isOverseas = true; + } + context + .read< + IdentificationBloc>() + .add(ShowEditIdentificationForm( + identification: + state.identificationInformation[ + index], + profileId: + profileId!, + token: token!, + overseas: + isOverseas)); + } + }, + menuItems: [ + popMenuItem( + text: "Edit", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Delete", + value: 2, + icon: Icons.delete), + popMenuItem( + text: "Attachment", + value: 3, + icon: FontAwesome.attach) + ], icon: const Icon( Icons.more_vert, color: Colors.grey, - )) - ], + ), + tooltip: "Options", + ) + ], + ), ), - ), - const SizedBox( - height: 5, - ), - ], - ); - }); - } else { - const EmptyData( - message: - "You don't have identifications added. Please click + to add."); + const SizedBox( + height: 5, + ), + ], + ); + }); + } else { + return const EmptyData( + message: + "You don't have identifications added. Please click + to add."); + } } - } - return Container(); - }, - ); - } - return Container(); - }, - ); - } - return Container(); - }, + if (state is IdentificationAddingState) { + return AddIdentificationScreen( + token: token!, + profileId: profileId!, + ); + } + if (state is IdenficationErrorState) { + return SomethingWentWrong( + message: state.message, onpressed: () {}); + } + if (state is IdentificationEditingState) { + return EditIdentificationScreen( + profileId: profileId!, token: token!); + } + + return Container(); + }, + ); + } + return Container(); + }, + ); + } + return Container(); + }, + ), )); } + + PopupMenuItem popMenuItem({String? text, int? value, IconData? icon}) { + return PopupMenuItem( + value: value, + child: Row( + children: [ + Icon( + icon, + ), + const SizedBox( + width: 10, + ), + Text( + text!, + ), + ], + ), + ); + } } diff --git a/lib/screens/profile/components/eligibility/add_modal.dart b/lib/screens/profile/components/eligibility/add_modal.dart index f5a71a6..55d6612 100644 --- a/lib/screens/profile/components/eligibility/add_modal.dart +++ b/lib/screens/profile/components/eligibility/add_modal.dart @@ -63,11 +63,11 @@ class _AddEligibilityScreenState extends State { if (state is AddEligibilityState) { return SingleChildScrollView( child: SizedBox( - height: screenHeight * .95, + height: screenHeight * .90, child: Center( child: Padding( padding: const EdgeInsets.symmetric( - vertical: 25, horizontal: 18), + vertical: 25, horizontal: 25), child: FormBuilder( key: formKey, child: Column( @@ -291,7 +291,7 @@ class _AddEligibilityScreenState extends State { ), ////PROVINCE DROPDOWN SizedBox( - height: 70, + height: 60, child: ModalProgressHUD( color: Colors.transparent, inAsyncCall: provinceCall, diff --git a/lib/screens/profile/components/eligibility/edit_modal.dart b/lib/screens/profile/components/eligibility/edit_modal.dart index b2ffb29..d77dec6 100644 --- a/lib/screens/profile/components/eligibility/edit_modal.dart +++ b/lib/screens/profile/components/eligibility/edit_modal.dart @@ -84,13 +84,14 @@ class _EditEligibilityScreenState extends State { return Center( child: Padding( padding: const EdgeInsets.symmetric( - vertical: 25, horizontal: 18), + vertical: 25, horizontal: 24), child: FormBuilder( key: formKey, child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ + const SizedBox(height: 24,), ////ELIGIBILITIES DROPDOWN DropdownButtonFormField( validator: (value) => @@ -110,7 +111,7 @@ class _EditEligibilityScreenState extends State { decoration: normalTextFieldStyle( "Eligibility", "")), const SizedBox( - height: 20, + height: 12, ), SizedBox( @@ -155,7 +156,7 @@ class _EditEligibilityScreenState extends State { ), ), const SizedBox( - height: 20, + height: 12, ), SizedBox( width: screenWidth, @@ -236,7 +237,7 @@ class _EditEligibilityScreenState extends State { title: const Text("Overseas Address?"), ), const SizedBox( - height: 20, + height: 12, ), //COUNTRY DROPDOWN SizedBox( @@ -328,7 +329,7 @@ class _EditEligibilityScreenState extends State { }).toList(), ), const SizedBox( - height: 20, + height: 12, ), ////PROVINCE DROPDOWN SizedBox( @@ -392,7 +393,7 @@ class _EditEligibilityScreenState extends State { //// City municipality SizedBox( - height: 70, + height: 60, child: ModalProgressHUD( color: Colors.transparent, diff --git a/lib/screens/profile/components/family_background_screen.dart b/lib/screens/profile/components/family_background_screen.dart index f179041..b7b9109 100644 --- a/lib/screens/profile/components/family_background_screen.dart +++ b/lib/screens/profile/components/family_background_screen.dart @@ -1,16 +1,35 @@ +import 'package:app_popup_menu/app_popup_menu.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:fluttericon/entypo_icons.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/family_backround.dart'; +import 'package:unit2/screens/profile/components/basic_information/family/add_mobile_modal.dart'; +import 'package:unit2/screens/profile/components/basic_information/family/child_add_modal.dart'; +import 'package:unit2/screens/profile/components/basic_information/family/child_edit_modal.dart'; +import 'package:unit2/screens/profile/components/basic_information/family/father_edit_modal.dart'; +import 'package:unit2/screens/profile/components/basic_information/family/mother_add_modal.dart'; +import 'package:unit2/screens/profile/components/basic_information/family/mother_edit_modal.dart'; +import 'package:unit2/screens/profile/components/basic_information/family/related_add_modal.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; +import 'package:unit2/utils/global_context.dart'; import 'package:unit2/utils/text_container.dart'; - import '../../../bloc/profile/family/family_bloc.dart'; +import '../../../model/utils/agency.dart'; +import '../../../model/utils/category.dart'; +import '../../../model/utils/position.dart'; +import '../../../utils/alerts.dart'; +import '../../../utils/profile_utilities.dart'; +import 'basic_information/family/father_add_modal.dart'; +import 'basic_information/family/related_edit_modal.dart'; +import 'basic_information/family/spouse_add_modal.dart'; +import 'basic_information/family/spouse_edit_modal.dart'; class FamilyBackgroundScreen extends StatefulWidget { const FamilyBackgroundScreen({ @@ -27,404 +46,1898 @@ class _FamilyBackgroundScreenState extends State { FamilyBackground? spouse; List children = []; List otherRelated = []; + List bloodType = [ + "NONE", + "A+", + "B+", + "A-", + "B-", + "AB+", + "AB-", + "0+", + "0-" + ]; + List nameExtensions = [ + "NONE", + "N/A", + "SR.", + "JR.", + "I", + "II", + "III", + "IV", + "V", + "VI", + "VII", + "VIII", + "IX", + "X" + ]; + List sexes = ["MALE", "FEMALE"]; + List civilStatus = [ + "NONE", + "SINGLE", + "MARRIED", + "SEPARATED", + "WIDOWED" + ]; + List gender = [ + "NONE", + "AGENDER", + "ANATOMAL SEX", + "CISGENDER", + "CISHET", + "GENDER NON-CONFORMING", + "GENDER-EXPANSIVE", + "GENDER-FLUID", + "GENDERQUEER", + "GENDERVOID", + "INTERSEX", + "NON-BINARY", + "TRANSGENDER", + "OTHERS" + ]; + List positions = []; + List agencices = []; + List categories = []; + bool fatherIncaseOfEmergency = false; + List? relationshipTypes; + final formKey = GlobalKey(); + final GlobalKey _scaffoldKey = GlobalKey(); @override Widget build(BuildContext context) { + final familyBloc = BlocProvider.of(context); + int? profileId; + String? token; + return Scaffold( + key: _scaffoldKey, + backgroundColor: Colors.white, appBar: AppBar( title: const Text(familyBackgroundScreenTitle), centerTitle: true, backgroundColor: primary, ), body: ProgressHUD( - padding: const EdgeInsets.all(24), - backgroundColor: Colors.black87, - indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), child: BlocBuilder( + buildWhen: (previous, current) { + return false; + }, builder: (context, state) { - return BlocBuilder( - builder: (context, state) { - if (state is ProfileLoaded) { - return BlocConsumer( - listener: (context, state) { - if (state is FamilyLoadingState) { - final progress = ProgressHUD.of(context); - progress!.showWithText("Please wait..."); - } - if (state is FamilyLoaded || state is FamilyErrorState) { - final progress = ProgressHUD.of(context); - progress!.dismiss(); - } - }, - builder: (context, state) { - if (state is FamilyLoaded) { - father = state.families.firstWhere( - (element) => element.relationship!.id == 1); - mother = state.families.firstWhere( - (element) => element.relationship!.id == 2); - spouse = state.families.firstWhere( - (element) => element.relationship!.id == 3); - - // get all children - var childs = state.families - .where((element) => element.relationship!.id == 4); - if (childs.isNotEmpty) { - for (var element in childs) { - children.add(element); + if (state is UserLoggedIn) { + token = state.userData!.user!.login!.token; + profileId = state.userData!.user!.login!.user!.profileId; + return BlocBuilder( + builder: (context, state) { + if (state is ProfileLoaded) { + return BlocConsumer( + listener: (context, state) { + if (state is FamilyLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is FamilyLoaded || + state is FamilyErrorState || + state is FamilyAddedState || + state is DeletedState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + ////AddedState + if (state is FamilyAddedState) { + if (state.response['success']) { + successAlert(context, "Adding Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context.read().add(LoadFamily()); + }); + } else { + errorAlert(context, "Adding Failed", + "Something went wrong. Please try again.", () { + Navigator.of(context).pop(); + context.read().add(LoadFamily()); + }); + } + } + if (state is EmergencyContactEditedState) { + if (state.response['success']) { + successAlert(context, state.response['message'], + state.response[''], () { + Navigator.of(context).pop(); + context.read().add(LoadFamily()); + }); + } else { + errorAlert(context, state.response['message'], + "Something went wrong. Please try again.", () { + Navigator.of(context).pop(); + context.read().add(LoadFamily()); + }); + } + } + ////Edited State + if (state is FamilyEditedState) { + if (state.response['success']) { + successAlert(context, "Update Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context.read().add(LoadFamily()); + }); + } else { + errorAlert(context, "Update Failed", + "Something went wrong. Please try again.", () { + Navigator.of(context).pop(); + context.read().add(LoadFamily()); + }); } } - //get all related persons - var relateds = state.families - .where((element) => element.relationship!.id! > 4); - if (relateds.isNotEmpty) { - for (var element in relateds) { - otherRelated.add(element); + if (state is DeletedState) { + if (state.success) { + successAlert(context, "Deletion Successfull", + "Family Background has been deleted successfully", + () { + Navigator.of(context).pop(); + context.read().add(LoadFamily()); + }); + } else { + errorAlert(context, "Deletion Failed", + "Error deleting Family", () { + Navigator.of(context).pop(); + context.read().add(LoadFamily()); + }); } } - return ListView(children: [ - //Father---------------------------------------------- - Container( - decoration: box1(), - padding: const EdgeInsets.symmetric( - horizontal: 12, vertical: 8), - width: screenWidth, - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - const Text(fatherText), - const SizedBox( - height: 5, - ), - Text( - " ${father!.relatedPerson!.firstName} ${father!.relatedPerson!.middleName} ${father!.relatedPerson!.lastName} ${father!.relatedPerson!.nameExtension ?? ''},", - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith( - fontWeight: FontWeight.w500), - ), - Text( - " ", - style: Theme.of(context) - .textTheme - .bodySmall, - ), - Row( - children: [ - Checkbox( - value: false, - onChanged: (value) { - setState(() { - value = !value!; - }); - }), - const Text(incaseOfEmergency) - ], - ) - ]), - ), - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - )) - ], - ), - ), - const SizedBox( - height: 5, - ), - - //Mother----------------------------------------------------- - Container( - decoration: box1(), - padding: const EdgeInsets.symmetric( - horizontal: 12, vertical: 8), - width: screenWidth, - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - const Text(motherText), - const SizedBox( - height: 5, - ), - Text( - " ${mother!.relatedPerson!.firstName} ${mother!.relatedPerson!.middleName} ${mother!.relatedPerson!.lastName} ${mother!.relatedPerson!.nameExtension ?? ''}", - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith( - fontWeight: FontWeight.w500), - ), - Text(" ", - style: Theme.of(context) - .textTheme - .bodySmall), - Row( - children: [ - Checkbox( - value: false, - onChanged: (value) {}), - const Text(incaseOfEmergency) - ], - ) - ]), - ), - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - )) - ], - ), - ), - const SizedBox( - height: 5, - ), - //Spouse --------------------------------------------------------- - spouse != null - ? Container( - decoration: box1(), - padding: const EdgeInsets.symmetric( - horizontal: 12, vertical: 8), - width: screenWidth, - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, + }, + builder: (context, state) { + if (state is FamilyLoaded) { + if (state.families != null) { + children.clear(); + otherRelated.clear(); + father = null; + mother = null; + spouse = null; + for (var family in state.families!) { + if (family.relationship!.id == 1) { + father = family; + } + if (family.relationship!.id == 2) { + mother = family; + } + if (family.relationship!.id == 3) { + spouse = family; + } + if (family.relationship!.id == 4) { + children.add(family); + } + if (family.relationship!.id! > 4) { + otherRelated.add(family); + } + } + } + return ListView(children: [ + ////Father---------------------------------------------- + Container( + decoration: box1(), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + width: screenWidth, + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Row( + mainAxisSize: MainAxisSize.min, children: [ - const Text(spouseText), - const SizedBox( - height: 5, + Expanded( + child: Text( + fatherText.toUpperCase(), + style: Theme.of(context) + .textTheme + .titleLarge! + .copyWith(color: primary), + ), ), - Text( - " ${spouse!.relatedPerson!.firstName} ${spouse!.relatedPerson!.middleName} ${spouse!.relatedPerson!.lastName} ${spouse!.relatedPerson!.nameExtension ?? ''}", - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith( - fontWeight: - FontWeight.w500)), - Text(" ", - style: Theme.of(context) - .textTheme - .bodySmall), - Row( - children: [ - Checkbox( - value: false, - onChanged: (value) {}), - const Text(incaseOfEmergency) - ], + Container( + child: father == null + ? IconButton( + onPressed: () { + ////Show Add Alert Dialog + showDialog( + context: context, + builder: + (BuildContext + context) { + return FatherAlert( + familyBloc: + familyBloc, + profileId: + profileId!, + token: + token!, + bloodType: + bloodType, + civilStatus: + civilStatus, + gender: + gender, + nameExtensions: + nameExtensions, + sexes: + sexes); + }); + }, + icon: const Icon( + Entypo.plus, + color: second, + )) + : const SizedBox(), ) - ]), - ), - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - )) - ], - ), - ) - : const SizedBox(), - const SizedBox( - height: 5, - ), + ], + ), + Container( + child: father != null + ? Row( + children: [ + Expanded( + child: Flexible( + child: Column( + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment + .start, + mainAxisSize: + MainAxisSize + .min, + children: [ + ListTile( + dense: true, + visualDensity: const VisualDensity( + horizontal: + -4, + vertical: + -4), + title: Text( + "${father?.relatedPerson?.firstName} ${father?.relatedPerson?.middleName} ${father?.relatedPerson!.lastName} ${father?.relatedPerson?.nameExtension ?? ''}", + style: Theme.of( + context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: FontWeight.w500), + ), + subtitle: + const Text( + "fullname"), + ), + Row( + mainAxisSize: + MainAxisSize + .min, + children: [ + Checkbox( + visualDensity: const VisualDensity( + horizontal: + 0, + vertical: + -4), + value: father! + .incaseOfEmergency!, + onChanged: + (value) { + confirmAlertWithCancel(context, + () { + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); - // Childrens ---------------------------------- - children.isNotEmpty - ? Container( - decoration: box1(), - child: Column( + context.read().add(AddEmergencyEvent(requestType: "CHECKBOX", contactInfoId: father!.emergencyContact!.isNotEmpty ? father!.emergencyContact!.first.contactinfoid : null, numberMail: father!.emergencyContact!.isNotEmpty ? father!.emergencyContact!.first.numbermail : null, profileId: profileId!, relatedPersonId: father!.relatedPerson!.id!, token: token!)); + }, () {}, "Emergency Contact Information?", + father!.incaseOfEmergency == true ? "Remove as emergency contact information?" : "Add as emergency contact information?"); + }), + const Text( + incaseOfEmergency), + ////Add mobile icon + Container( + child: father!.incaseOfEmergency! && father!.emergencyContact!.isEmpty + ? IconButton( + visualDensity: const VisualDensity(horizontal: -4, vertical: -4), + onPressed: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AddMobileNumber( + onPressed: () { + Navigator.of(_scaffoldKey.currentContext!).pop(); + familyBloc.add(AddEmergencyEvent(contactInfoId: father!.emergencyContact!.isNotEmpty ? father!.emergencyContact!.first.contactinfoid : null, numberMail: formKey.currentState!.value['number_mail'], profileId: profileId!, relatedPersonId: father!.relatedPerson!.id!, token: token!, requestType: "CONTACT")); + }, + formKey: formKey); + }); + }, + icon: const Icon( + Entypo.plus_circled, + color: third, + size: 18, + )) + : Container()) + ], + ), + Visibility( + visible: + father! + .incaseOfEmergency!, + child: Container( + child: father!.emergencyContact!.isNotEmpty + ? Row( + children: [ + //// edit mobile + GestureDetector( + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AddMobileNumber( + formKey: formKey, + onPressed: () { + Navigator.of(_scaffoldKey.currentContext!).pop(); + familyBloc.add(AddEmergencyEvent(contactInfoId: father!.emergencyContact!.isNotEmpty ? father!.emergencyContact!.first.contactinfoid : null, numberMail: formKey.currentState!.value['number_mail'], profileId: profileId!, relatedPersonId: father!.relatedPerson!.id!, token: token!, requestType: "CONTACT")); + }); + }); + }, + child: Row( + children: [ + const SizedBox( + width: 16, + ), + Badge( + backgroundColor: third, + textColor: Colors.white, + label: Text(father!.emergencyContact!.first.numbermail!), + ), + ], + ), + ), + ], + ) + : Container())), + ]), + ), + ), + AppPopupMenu( + offset: const Offset( + -10, -10), + elevation: 3, + onSelected: (value) { + ////delete -= = = = = = = = =>> + if (value == 2) { + confirmAlert( + context, () { + final progress = + ProgressHUD.of( + context); + progress! + .showWithText( + "Loading..."); + context.read().add(DeleteFamily( + id: father! + .relatedPerson! + .id!, + profileId: + profileId!, + token: + token!)); + }, "Delete?", + "Confirm Delete?"); + } + if (value == 1) { + ////edit eligibilty-= = = = = = = = =>> + showDialog( + context: + context, + builder: + (BuildContext + context) { + return FatherEditAlert( + familyBackground: + father!, + familyBloc: + familyBloc, + profileId: + profileId!, + token: + token!, + bloodType: + bloodType, + civilStatus: + civilStatus, + gender: + gender, + nameExtensions: + nameExtensions, + sexes: + sexes); + }); + } + }, + menuItems: [ + popMenuItem( + text: "Update", + value: 1, + icon: + Icons.edit), + popMenuItem( + text: "Reset", + value: 2, + icon: Icons + .delete), + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ) + ], + ) + : SizedBox( + width: screenWidth, + child: const Text( + "Provide your father's primary information.", + textAlign: + TextAlign.center, + ), + )) + ]), + ), + ], + ), + ), + + const SizedBox( + height: 8, + ), + + ////Mother----------------------------------------------------- + Container( + decoration: box1(), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + width: screenWidth, + child: Row( + children: [ + Expanded( + child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, - children: children.map((child) { - int index = children.indexOf(child); - return Container( - padding: const EdgeInsets.symmetric( - horizontal: 12, vertical: 8), - width: screenWidth, - child: Column( - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - index == 0 - ? const Text(childrenText) - : const SizedBox(), - const SizedBox( - height: 5, + children: [ + Row( + children: [ + Expanded( + child: Text( + motherText.toUpperCase(), + style: Theme.of(context) + .textTheme + .titleLarge! + .copyWith(color: primary), ), - Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: - MainAxisAlignment - .start, - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - Text( - " ${child.relatedPerson!.firstName} ${child.relatedPerson!.middleName} ${child.relatedPerson!.lastName} ${child.relatedPerson!.nameExtension ?? ''}", - style: Theme.of( - context) + ), + Container( + child: mother == null + ? IconButton( + onPressed: () { + ////Show Alert Dialog + showDialog( + context: context, + builder: + (BuildContext + context) { + return MotherAlert( + familyBloc: + familyBloc, + token: token!, + profileId: + profileId!, + bloodType: + bloodType, + civilStatus: + civilStatus, + gender: + gender, + nameExtensions: + nameExtensions, + sexes: sexes); + }); + }, + icon: const Icon( + Entypo.plus, + color: second, + )) + : const SizedBox(), + ) + ], + ), + Container( + child: mother != null + ? Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment + .start, + mainAxisSize: + MainAxisSize + .min, + children: [ + ListTile( + dense: true, + visualDensity: + const VisualDensity( + horizontal: + -4, + vertical: + -4), + title: Text( + "${mother?.relatedPerson?.firstName} ${mother?.relatedPerson?.middleName} ${mother?.relatedPerson?.lastName} ${mother?.relatedPerson?.nameExtension ?? ''}", + style: Theme.of( + context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight.w500), + ), + subtitle: + const Text( + "fullname"), + ), + Row( + mainAxisSize: + MainAxisSize + .min, + children: [ + Checkbox( + visualDensity: const VisualDensity( + horizontal: + 0, + vertical: + -4), + value: mother! + .incaseOfEmergency!, + onChanged: + (value) { + confirmAlertWithCancel( + context, + () { + final progress = + ProgressHUD.of(context); + progress! + .showWithText("Loading..."); + + context.read().add(AddEmergencyEvent( + requestType: 'CHECKBOX', + contactInfoId: mother!.emergencyContact!.isNotEmpty ? mother!.emergencyContact!.first.contactinfoid : null, + numberMail: mother!.emergencyContact!.isNotEmpty ? mother!.emergencyContact!.first.numbermail : null, + profileId: profileId!, + relatedPersonId: mother!.relatedPerson!.id!, + token: token!)); + }, + () {}, + "Emergency Contact Information?", + mother!.incaseOfEmergency == true + ? "Remove as emergency contact information?" + : "Add as emergency contact information?"); + }), + const Text( + incaseOfEmergency), + Container( + child: mother!.incaseOfEmergency! && + mother!.emergencyContact!.isEmpty + ? IconButton( + visualDensity: const VisualDensity(horizontal: -4, vertical: -4), + onPressed: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AddMobileNumber( + onPressed: () { + Navigator.of(_scaffoldKey.currentContext!).pop(); + familyBloc.add(AddEmergencyEvent(contactInfoId: mother!.emergencyContact!.isNotEmpty ? mother!.emergencyContact!.first.contactinfoid : null, numberMail: formKey.currentState!.value['number_mail'], profileId: profileId!, relatedPersonId: mother!.relatedPerson!.id!, token: token!, requestType: "CONTACT")); + }, + formKey: formKey); + }); + }, + icon: const Icon( + Entypo.plus_circled, + color: third, + size: 18, + )) + : Container()) + ], + ), + Visibility( + visible: mother! + .incaseOfEmergency!, + child: Container( + child: mother!.emergencyContact!.isNotEmpty + ? Row( + children: [ + //// edit mobile + GestureDetector( + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AddMobileNumber( + formKey: formKey, + onPressed: () { + Navigator.of(_scaffoldKey.currentContext!).pop(); + familyBloc.add(AddEmergencyEvent(contactInfoId: mother!.emergencyContact!.isNotEmpty ? mother!.emergencyContact!.first.contactinfoid : null, numberMail: formKey.currentState!.value['number_mail'], profileId: profileId!, relatedPersonId: mother!.relatedPerson!.id!, token: token!, requestType: "CONTACT")); + }); + }); + }, + child: Row( + children: [ + const SizedBox( + width: 16, + ), + Badge( + backgroundColor: third, + textColor: Colors.white, + label: Text(mother!.emergencyContact!.first.numbermail!), + ), + ], + ), + ), + ], + ) + : Container())), + ]), + ), + AppPopupMenu( + offset: const Offset( + -10, -10), + elevation: 3, + onSelected: (value) { + ////delete -= = = = = = = = =>> + if (value == 2) { + confirmAlert( + context, () { + final progress = + ProgressHUD.of( + context); + progress! + .showWithText( + "Loading..."); + context + .read< + FamilyBloc>() + .add(DeleteFamily( + id: mother! + .relatedPerson! + .id!, + profileId: + profileId!, + token: + token!)); + }, "Delete?", + "Confirm Delete?"); + } + if (value == 1) { + ////edit eligibilty-= = = = = = = = =>> + showDialog( + context: + context, + builder: + (BuildContext + context) { + return MotherEditAlert( + familyBackground: + mother!, + familyBloc: + familyBloc, + token: + token!, + profileId: + profileId!, + bloodType: + bloodType, + civilStatus: + civilStatus, + gender: + gender, + nameExtensions: + nameExtensions, + sexes: + sexes); + }); + } + }, + menuItems: [ + popMenuItem( + text: "Update", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Reset", + value: 2, + icon: + Icons.delete), + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ) + ], + ) + : SizedBox( + width: screenWidth, + child: const Text( + "Provide your mother's primary information", + textAlign: + TextAlign.center, + ), + )), + ], + ), + ), + ], + ), + ), + const SizedBox( + height: 8, + ), + ////Spouse --------------------------------------------------------- + Container( + decoration: box1(), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + width: screenWidth, + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: Text( + spouseText.toUpperCase(), + style: Theme.of(context) + .textTheme + .titleLarge! + .copyWith(color: primary), + ), + ), + Container( + child: spouse == null + ? IconButton( + ////spouse + //// Show Dialog + onPressed: () async { + final progress = + ProgressHUD.of( + context); + progress!.showWithText( + "Loading..."); + if (positions.isEmpty) { + positions = + await ProfileUtilities + .instance + .getAgencyPosition(); + } + + if (agencices.isEmpty) { + agencices = + await ProfileUtilities + .instance + .getAgecies(); + } + if (categories + .isEmpty) { + categories = + await ProfileUtilities + .instance + .agencyCategory(); + } + progress.dismiss(); + showDialog( + context: NavigationService + .navigatorKey + .currentContext!, + builder: + (BuildContext + context) { + return SpouseAlert( + familyBloc: + familyBloc, + token: token!, + profileId: + profileId!, + positions: + positions, + agencies: + agencices, + category: + categories, + bloodType: + bloodType, + civilStatus: + civilStatus, + gender: + gender, + nameExtensions: + nameExtensions, + sexes: sexes); + }); + }, + icon: const Icon( + Entypo.plus, + color: second, + )) + : const SizedBox(), + ) + ], + ), + Container( + child: spouse != null + ? Row( + children: [ + Expanded( + child: Column(children: [ + const SizedBox( + height: 5, + ), + ListTile( + dense: true, + visualDensity: + const VisualDensity( + horizontal: -4, + vertical: -4), + title: Text( + "${spouse?.relatedPerson?.firstName} ${spouse?.relatedPerson?.middleName} ${spouse?.relatedPerson!.lastName} ${spouse?.relatedPerson?.nameExtension ?? ''}", + style: Theme.of(context) .textTheme .titleMedium! .copyWith( fontWeight: FontWeight - .w500)), - Text(" ", - style: Theme.of( - context) - .textTheme - .bodySmall), + .w500), + ), + subtitle: const Text( + "fullname"), + ), Row( children: [ Checkbox( - value: false, - onChanged: - (value) {}), + visualDensity: + const VisualDensity( + horizontal: + 0, + vertical: + -4), + value: spouse! + .incaseOfEmergency!, + onChanged: (value) { + confirmAlertWithCancel( + context, () { + final progress = + ProgressHUD.of( + context); + progress! + .showWithText( + "Loading..."); + + context.read().add(AddEmergencyEvent( + contactInfoId: spouse!.emergencyContact!.isNotEmpty + ? spouse! + .emergencyContact! + .first + .contactinfoid + : null, + numberMail: spouse! + .emergencyContact! + .isNotEmpty + ? spouse! + .emergencyContact! + .first + .numbermail + : null, + profileId: + profileId!, + relatedPersonId: + spouse! + .relatedPerson! + .id!, + token: + token!, + requestType: + "CHECKBOX")); + }, + () {}, + "Emergency Contact Information?", + spouse!.incaseOfEmergency == + true + ? "Remove as emergency contact information?" + : "Add as emergency contact information?"); + }), const Text( - incaseOfEmergency) + incaseOfEmergency), + ////Add mobile icon + Container( + child: spouse! + .incaseOfEmergency! && + spouse! + .emergencyContact! + .isEmpty + ? IconButton( + visualDensity: const VisualDensity( + horizontal: + -4, + vertical: + -4), + onPressed: + () { + showDialog( + context: + context, + builder: + (BuildContext context) { + return AddMobileNumber( + onPressed: () { + Navigator.of(_scaffoldKey.currentContext!).pop(); + familyBloc.add(AddEmergencyEvent(contactInfoId: spouse!.emergencyContact!.isNotEmpty ? spouse!.emergencyContact!.first.contactinfoid : null, numberMail: formKey.currentState!.value['number_mail'], profileId: profileId!, relatedPersonId: spouse!.relatedPerson!.id!, token: token!, requestType: "CONTACT")); + }, + formKey: formKey); + }); + }, + icon: + const Icon( + Entypo + .plus_circled, + color: + third, + size: 18, + )) + : Container()), ], + ), + Visibility( + visible: spouse! + .incaseOfEmergency!, + child: Container( + child: spouse! + .emergencyContact! + .isNotEmpty + ? Row( + children: [ + //// edit mobile + GestureDetector( + onTap: + () { + showDialog( + context: context, + builder: (BuildContext context) { + return AddMobileNumber( + formKey: formKey, + onPressed: () { + Navigator.of(_scaffoldKey.currentContext!).pop(); + familyBloc.add(AddEmergencyEvent(contactInfoId: spouse!.emergencyContact!.isNotEmpty ? spouse!.emergencyContact!.first.contactinfoid : null, numberMail: formKey.currentState!.value['number_mail'], profileId: profileId!, relatedPersonId: spouse!.relatedPerson!.id!, token: token!, requestType: "CONTACT")); + }); + }); + }, + child: + Row( + children: [ + const SizedBox( + width: 16, + ), + Badge( + backgroundColor: third, + textColor: Colors.white, + label: Text(spouse!.emergencyContact!.first.numbermail!), + ), + ], + ), + ), + ], + ) + : Container())), + Container( + padding: + const EdgeInsets.only( + left: 12), + alignment: + Alignment.topLeft, + child: spouse?.position != + null + ? Column( + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + const SizedBox( + height: 12, + ), + Text( + "OCCUPATION", + textAlign: + TextAlign + .start, + style: Theme.of( + context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight.w500, + color: primary)), + Column( + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + const SizedBox( + height: 3, + ), + Text( + spouse! + .position! + .title!, + style: Theme.of( + context) + .textTheme + .titleMedium, + ), + Text( + spouse! + .company! + .name!, + style: Theme.of( + context) + .textTheme + .titleMedium, + ), + Text( + spouse! + .companyAddress!, + style: Theme.of( + context) + .textTheme + .labelMedium, + ), + Text( + spouse! + .companyContactNumber!, + style: Theme.of( + context) + .textTheme + .labelMedium, + ), + ], + ) + ], + ) + : const SizedBox(), ) ]), - ), - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - )) - ], - ), - ], - ), - ); - }).toList()), - ) - : const SizedBox(), - const SizedBox( - height: 5, - ), - //Other related person - otherRelated.isNotEmpty - ? Container( - decoration: box1(), - child: Column( + ), + AppPopupMenu( + offset: const Offset(-10, -10), + elevation: 3, + onSelected: (value)async { + ////delete -= = = = = = = = =>> + if (value == 2) { + confirmAlert(context, () { + final progress = + ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add( + DeleteFamily( + id: mother! + .relatedPerson!.id!, + profileId: profileId!, + token: token!)); + }, "Delete?", "Confirm Delete?"); + } + if (value == 1) { + ////edit eligibilty-= = = = = = = = =>> + + final progress = + ProgressHUD.of( + context); + progress!.showWithText( + "Loading..."); + if (positions.isEmpty) { + positions = + await ProfileUtilities + .instance + .getAgencyPosition(); + } + + if (agencices.isEmpty) { + agencices = + await ProfileUtilities + .instance + .getAgecies(); + } + if (categories + .isEmpty) { + categories = + await ProfileUtilities + .instance + .agencyCategory(); + } + progress.dismiss(); + showDialog( + context: NavigationService + .navigatorKey + .currentContext!, + builder: + (BuildContext + context) { + return SpouseEditAlert( + familyBackground: spouse!, + familyBloc: + familyBloc, + token: token!, + profileId: + profileId!, + positions: + positions, + agencies: + agencices, + category: + categories, + bloodType: + bloodType, + civilStatus: + civilStatus, + gender: + gender, + nameExtensions: + nameExtensions, + sexes: sexes); + }); + + } + }, + menuItems: [ + popMenuItem( + text: "Update", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Reset", + value: 2, + icon: Icons.delete), + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ) + ], + ) + : SizedBox( + width: screenWidth, + child: const Padding( + padding: + EdgeInsets.symmetric( + vertical: 8, + horizontal: 0), + child: Text( + "Provide your spouse's primary and employment information. Leave empty if not applicable.", + textAlign: + TextAlign.center, + ), + ), + )), + ], + ), + ), + ], + ), + ), + + const SizedBox( + height: 8, + ), + //// Childrens ---------------------------------- + Container( + decoration: box1(), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + width: screenWidth, + child: Row( + children: [ + Expanded( + child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, - children: otherRelated.map((relative) { - int index2 = - otherRelated.indexOf(relative); - return Container( - padding: const EdgeInsets.symmetric( - horizontal: 12, vertical: 8), - width: screenWidth, - child: Column( - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - index2 == 0 - ? const Text(otherRelatedText) - : const SizedBox(), - const SizedBox( - height: 5, + children: [ + Row( + children: [ + Text( + childrenText.toUpperCase(), + style: Theme.of(context) + .textTheme + .titleLarge! + .copyWith(color: primary), + ), + const Expanded(child: SizedBox()), + IconButton( + ////childrens + ////Show add dialog + onPressed: () { + showDialog( + context: context, + builder: (BuildContext + context) { + return ChildAlert( + familyBloc: + familyBloc, + token: token!, + profileId: + profileId!, + bloodType: + bloodType, + civilStatus: + civilStatus, + gender: gender, + nameExtensions: + nameExtensions, + sexes: sexes); + }); + }, + icon: const Icon( + Entypo.plus, + color: second, + )) + ], + ), + Container( + child: children.isNotEmpty + ? Column( + mainAxisSize: + MainAxisSize.min, + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment + .start, + children: + children.map((child) { + return Container( + padding: + const EdgeInsets + .symmetric( + horizontal: 12, + vertical: 8), + width: screenWidth, + child: Column( + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + const SizedBox( + height: 5, + ), + Row( + mainAxisSize: + MainAxisSize + .min, + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment + .start, + mainAxisSize: + MainAxisSize + .min, + children: [ + ListTile( + dense: + true, + visualDensity: const VisualDensity( + horizontal: -4, + vertical: -4), + title: Text( + "${child.relatedPerson?.firstName} ${child.relatedPerson?.middleName} ${child.relatedPerson?.lastName} ${child.relatedPerson?.nameExtension ?? ''}", + style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500)), + subtitle: + const Text("fullname"), + ), + Row( + mainAxisSize: + MainAxisSize.min, + children: [ + Checkbox( + visualDensity: const VisualDensity(horizontal: 0, vertical: -4), + value: child.incaseOfEmergency!, + onChanged: (value) { + confirmAlertWithCancel(context, () { + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + + context.read().add(AddEmergencyEvent(contactInfoId: child.emergencyContact!.isNotEmpty ? child.emergencyContact!.first.contactinfoid : null, numberMail: child.emergencyContact!.isNotEmpty ? child.emergencyContact!.first.numbermail : null, profileId: profileId!, relatedPersonId: child.relatedPerson!.id!, token: token!, requestType: "CHECKBOX")); + }, () {}, "Emergency Contact Information?", child.incaseOfEmergency == true ? "Remove as emergency contact information?" : "Add as emergency contact information?"); + }), + const Text(incaseOfEmergency), + ////Add mobile icon + Container( + child: child.incaseOfEmergency! && child.emergencyContact!.isEmpty + ? IconButton( + visualDensity: const VisualDensity(horizontal: -4, vertical: -4), + onPressed: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AddMobileNumber( + onPressed: () { + Navigator.of(_scaffoldKey.currentContext!).pop(); + familyBloc.add(AddEmergencyEvent(contactInfoId: child.emergencyContact!.isNotEmpty ? child.emergencyContact!.first.contactinfoid : null, numberMail: formKey.currentState!.value['number_mail'], profileId: profileId!, relatedPersonId: child.relatedPerson!.id!, token: token!, requestType: "CONTACT")); + }, + formKey: formKey); + }); + }, + icon: const Icon( + Entypo.plus_circled, + color: third, + size: 18, + )) + : Container()) + ], + ), + //// child person add mobile number + Visibility( + visible: + child.incaseOfEmergency!, + child: Container( + child: child.emergencyContact!.isNotEmpty + ? Row( + children: [ + //// edit mobile + GestureDetector( + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AddMobileNumber( + formKey: formKey, + onPressed: () { + Navigator.of(_scaffoldKey.currentContext!).pop(); + familyBloc.add(AddEmergencyEvent(contactInfoId: child.emergencyContact!.isNotEmpty ? child.emergencyContact!.first.contactinfoid : null, numberMail: formKey.currentState!.value['number_mail'], profileId: profileId!, relatedPersonId: child.relatedPerson!.id!, token: token!, requestType: "CONTACT")); + }); + }); + }, + child: Row( + children: [ + const SizedBox( + width: 16, + ), + Badge( + backgroundColor: third, + textColor: Colors.white, + label: Text(child.emergencyContact!.first.numbermail!), + ), + ], + ), + ), + ], + ) + : Container())), + ]), + ), + AppPopupMenu< + int>( + offset: + const Offset( + -10, + -10), + elevation: 3, + onSelected: + (value) { + ////delete -= = = = = = = = =>> + if (value == + 2) { + confirmAlert( + context, + () { + final progress = + ProgressHUD.of(context); + progress! + .showWithText("Loading..."); + context.read().add(DeleteFamily( + id: child + .relatedPerson!.id!, + profileId: + profileId!, + token: + token!)); + }, "Delete?", + "Confirm Delete?"); + } + if (value == + 1) { + //// edit + showDialog( + context: + context, + builder: + (BuildContext context) { + return ChildEditAlert( + familyBackground: child, + familyBloc: familyBloc, + profileId: profileId!, + token: token!, + bloodType: bloodType, + civilStatus: civilStatus, + gender: gender, + nameExtensions: nameExtensions, + sexes: sexes); + }); + } + }, + menuItems: [ + popMenuItem( + text: + "Update", + value: + 1, + icon: Icons + .edit), + popMenuItem( + text: + "Reset", + value: + 2, + icon: Icons + .delete), + ], + icon: + const Icon( + Icons + .more_vert, + color: Colors + .grey, + ), + tooltip: + "Options", + ) + ], + ), + ], + ), + ); + }).toList()) + : SizedBox( + width: screenWidth, + child: const Padding( + padding: + EdgeInsets.symmetric( + vertical: 8, + horizontal: 0), + child: Text( + "Provide your child/children's primary information. Leave empty if not applicable..", + textAlign: + TextAlign.center, + ), + ), + )), + ], + ), + ), + ], + ), + ), + const SizedBox( + height: 8, + ), + ////Other related person + Container( + decoration: box1(), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + width: screenWidth, + child: Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: Text( + otherRelatedText.toUpperCase(), + style: Theme.of(context) + .textTheme + .titleLarge! + .copyWith(color: primary), ), - Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: - MainAxisAlignment - .start, - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - Text( - " ${relative.relatedPerson!.firstName} ${relative.relatedPerson!.middleName} ${relative.relatedPerson!.lastName} ${relative.relatedPerson!.nameExtension ?? ''}", - style: Theme.of( - context) - .textTheme - .titleMedium! - .copyWith( - fontWeight: - FontWeight - .w500)), - Text(" ", - style: Theme.of( - context) - .textTheme - .bodySmall!), - Row( - children: [ - Checkbox( - value: false, - onChanged: - (value) {}), - const Text( - incaseOfEmergency) - ], - ) - ]), - ), - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - )) - ], - ), - ], - ), - ); - }).toList()), - ) - : const SizedBox(), - ]); - } - return Container(); - }, - ); - } - return Container(); - }, - ); + ), + IconButton( + ////other related + //// Show Alert Dialog + onPressed: () async { + final progress = + ProgressHUD.of(context); + progress!.showWithText( + "Loading..."); + List + relationshipTypes = + await ProfileUtilities + .instance + .getRelationshipType(); + progress.dismiss(); + showDialog( + context: NavigationService + .navigatorKey + .currentContext!, + builder: (BuildContext + context) { + return RelatedAlert( + token: token!, + profileId: + profileId!, + familyBloc: + familyBloc, + relationships: + relationshipTypes, + bloodType: + bloodType, + civilStatus: + civilStatus, + gender: gender, + nameExtensions: + nameExtensions, + sexes: sexes); + }); + }, + icon: const Icon( + Entypo.plus, + color: second, + )) + ], + ), + Container( + child: otherRelated.isNotEmpty + ? Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment + .start, + children: otherRelated + .map((relative) { + return Container( + padding: + const EdgeInsets + .symmetric( + horizontal: 12, + vertical: 8), + width: screenWidth, + child: Column( + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + const SizedBox( + height: 5, + ), + Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment + .start, + mainAxisSize: + MainAxisSize + .min, + children: [ + ListTile( + dense: + true, + visualDensity: const VisualDensity( + horizontal: -4, + vertical: -4), + title: Text( + "${relative.relatedPerson?.firstName} ${relative.relatedPerson?.middleName ?? ""} ${relative.relatedPerson?.lastName} ${relative.relatedPerson?.nameExtension ?? ''}", + style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500)), + subtitle: + const Text("fullname"), + ), + Row( + mainAxisSize: + MainAxisSize.min, + children: [ + Checkbox( + visualDensity: const VisualDensity(horizontal: 0, vertical: -4), + value: relative.incaseOfEmergency!, + onChanged: (value) { + confirmAlertWithCancel(context, () { + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + + context.read().add(AddEmergencyEvent(contactInfoId: relative.emergencyContact!.isNotEmpty ? relative.emergencyContact!.first.contactinfoid : null, numberMail: relative.emergencyContact!.isNotEmpty ? relative.emergencyContact!.first.numbermail : null, profileId: profileId!, relatedPersonId: relative.relatedPerson!.id!, token: token!, requestType: "CHECKBOX")); + }, () {}, "Emergency Contact Information?", relative.incaseOfEmergency == true ? "Remove as emergency contact information?" : "Add as emergency contact information?"); + }), + const Text(incaseOfEmergency), + Container( + child: relative.incaseOfEmergency! && relative.emergencyContact!.isEmpty + ? IconButton( + visualDensity: const VisualDensity(horizontal: -4, vertical: -4), + onPressed: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AddMobileNumber( + onPressed: () { + Navigator.of(_scaffoldKey.currentContext!).pop(); + familyBloc.add(AddEmergencyEvent(contactInfoId: relative.emergencyContact!.isNotEmpty ? relative.emergencyContact!.first.contactinfoid : null, numberMail: formKey.currentState!.value['number_mail'], profileId: profileId!, relatedPersonId: relative.relatedPerson!.id!, token: token!, requestType: "CONTACT")); + }, + formKey: formKey); + }); + }, + icon: const Icon( + Entypo.plus_circled, + color: third, + size: 18, + )) + : Container()) + ], + ), + //// other related person add mobile number + Visibility( + visible: + relative.incaseOfEmergency!, + child: Container( + child: relative.emergencyContact!.isNotEmpty + ? Row( + children: [ + //// edit mobile + GestureDetector( + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AddMobileNumber( + formKey: formKey, + onPressed: () { + Navigator.of(_scaffoldKey.currentContext!).pop(); + familyBloc.add(AddEmergencyEvent(contactInfoId: relative.emergencyContact!.isNotEmpty ? relative.emergencyContact!.first.contactinfoid : null, numberMail: formKey.currentState!.value['number_mail'], profileId: profileId!, relatedPersonId: relative.relatedPerson!.id!, token: token!, requestType: "CONTACT")); + }); + }); + }, + child: Row( + children: [ + const SizedBox( + width: 16, + ), + Badge( + backgroundColor: third, + textColor: Colors.white, + label: Text(relative.emergencyContact!.first.numbermail!), + ), + ], + ), + ), + ], + ) + : Container()), + ) + ]), + ), + AppPopupMenu< + int>( + offset: + const Offset( + -10, + -10), + elevation: 3, + onSelected: + (value) async { + ////delete -= = = = = = = = =>> + if (value == + 2) { + confirmAlert( + context, + () { + final progress = + ProgressHUD.of(context); + progress! + .showWithText("Loading..."); + context.read().add(DeleteFamily( + id: relative + .relatedPerson!.id!, + profileId: + profileId!, + token: + token!)); + }, "Delete?", + "Confirm Delete?"); + } + if (value == + 1) { + ////edit eligibilty-= = = = = = = = =>> + await getRelationshiptypes( + context); + showDialog( + context: NavigationService + .navigatorKey + .currentContext!, + builder: + (BuildContext context) { + return RelatedEditAlert( + familyBackground: relative, + token: token!, + profileId: profileId!, + familyBloc: familyBloc, + relationships: relationshipTypes!, + bloodType: bloodType, + civilStatus: civilStatus, + gender: gender, + nameExtensions: nameExtensions, + sexes: sexes); + }); + } + }, + menuItems: [ + popMenuItem( + text: + "Update", + value: + 1, + icon: Icons + .edit), + popMenuItem( + text: + "Reset", + value: + 2, + icon: Icons + .delete), + ], + icon: + const Icon( + Icons + .more_vert, + color: Colors + .grey, + ), + tooltip: + "Options", + ) + ], + ), + ], + ), + ); + }).toList()) + : SizedBox( + width: screenWidth, + child: const Padding( + padding: + EdgeInsets.symmetric( + vertical: 8, + horizontal: 0), + child: Text( + "Provide the other related person's primary information. Leave empty if not applicable.", + textAlign: + TextAlign.center, + ), + ), + )), + ], + ), + ), + ], + ), + ), + ]); + } + return Container(); + }, + ); + } + return Container(); + }, + ); + } + return Container(); }, ), ), ); } + + getRelationshiptypes(BuildContext parentContext) async { + final progress = ProgressHUD.of(parentContext); + progress!.showWithText("Loading..."); + + relationshipTypes = await ProfileUtilities.instance.getRelationshipType(); + progress.dismiss(); + } + + PopupMenuItem popMenuItem({String? text, int? value, IconData? icon}) { + return PopupMenuItem( + value: value, + child: Row( + children: [ + Icon( + icon, + ), + const SizedBox( + width: 10, + ), + Text( + text!, + ), + ], + ), + ); + } } diff --git a/lib/screens/unit2/homepage.dart/module-screen.dart b/lib/screens/unit2/homepage.dart/module-screen.dart index 5638de6..aef990f 100644 --- a/lib/screens/unit2/homepage.dart/module-screen.dart +++ b/lib/screens/unit2/homepage.dart/module-screen.dart @@ -33,6 +33,9 @@ class _MainScreenState extends State { }, child: BlocBuilder(builder: (context, state) { if (state is UserLoggedIn) { + for (var element in roles) { + element.roles.clear(); + } for (var role in state.userData!.user!.login!.user!.roles!) { Role? getRole = role; for (var module in role!.modules!) { diff --git a/lib/sevices/profile/family_services.dart b/lib/sevices/profile/family_services.dart index f48bc91..ab47a8d 100644 --- a/lib/sevices/profile/family_services.dart +++ b/lib/sevices/profile/family_services.dart @@ -1,6 +1,3 @@ - - - import 'dart:convert'; import 'package:unit2/utils/request.dart'; @@ -8,31 +5,203 @@ import 'package:unit2/utils/request.dart'; import '../../model/profile/family_backround.dart'; import '../../utils/urls.dart'; import 'package:http/http.dart' as http; -class FamilyService{ + +class FamilyService { static final FamilyService _instance = FamilyService(); static FamilyService get instance => _instance; - Future< List> getFamilies(int profileId, String token)async{ - List families = []; - String authToken = "Token $token"; + + Future> getFamilies( + int profileId, String token) async { + List families = []; + String authToken = "Token $token"; String path = "${Url.instance.getFamilies()}$profileId/"; Map headers = { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': authToken }; -try{ - http.Response response = await Request.instance.getRequest(path:path, param: {},headers: headers); - if(response.statusCode == 200){ - Map data = jsonDecode(response.body); - if(data['data'] != null){ - data['data'].forEach((var family){ - FamilyBackground familyBackground = FamilyBackground.fromJson(family); - families.add(familyBackground); - }); - } + try { + http.Response response = await Request.instance + .getRequest(path: path, param: {}, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((var family) { + FamilyBackground familyBackground = + FamilyBackground.fromJson(family); + families.add(familyBackground); + }); + } + } + } catch (e) { + throw e.toString(); } -}catch(e){ - throw e.toString(); -} -return families; + return families; } -} \ No newline at end of file + + Future> add( + {required int profileId, + required String token, + required int relationshipId, + required FamilyBackground? family}) async { + Map? _response = {}; + String authtoken = "Token $token"; + String path = "${Url.instance.getFamilies()}$profileId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authtoken + }; + Map body = { + "relation_type_id": relationshipId, + "company_address": family?.companyAddress, + "company_contact_number": family?.companyContactNumber, + "first_name": family!.relatedPerson!.firstName, + "middle_name": family.relatedPerson!.middleName, + "last_name": family.relatedPerson!.lastName, + "name_extension": family.relatedPerson?.nameExtension, + "birthdate": family.relatedPerson!.birthdate.toString(), + "sex": family.relatedPerson!.sex, + "blood_type": family.relatedPerson?.bloodType, + "civil_status": family.relatedPerson?.civilStatus, + "height": family.relatedPerson?.heightM, + "weight": family.relatedPerson?.weightKg, + "position_id": family.position?.id, + "agency_id": family.company?.id, + "_positionName": family.position?.title, + "_agencyName": family.company?.name, + "_agencyCatId": family.company?.category?.id, + "_privateEntity": family.company?.privateEntity, + "maidenMiddleName": family.relatedPerson?.maidenName?.middleName, + "maidenLastName": family.relatedPerson?.maidenName?.lastName, + "gender": family.relatedPerson!.gender, + "deceased": family.relatedPerson!.deceased, + }; + try { + http.Response response = await Request.instance + .postRequest(param: {}, path: path, body: body, headers: headers); + if (response.statusCode == 201) { + Map data = jsonDecode(response.body); + _response = data; + } else { + _response.addAll({'success': false}); + } + } catch (e) { + throw (e.toString()); + } + return _response; + } +//// Add Emergency + Future> addEmergency( + {required int profileId, + required String token, + required int relatedPersonId, + required String? numberMail, + required int? contactInfoId, + required String requestType, + }) async { + Map? _response = {}; + String authtoken = "Token $token"; + String path = Url.instance.addEmergency(); + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authtoken + }; + Map body = { "personid": profileId, "related_personid": relatedPersonId, "request_type": requestType, "number_mail": numberMail, "contactinfoid": contactInfoId }; + try { + http.Response response = await Request.instance + .postRequest(param: {}, path: path, body: body, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + _response = data; + print(_response.toString()); + } else { + _response.addAll({'success': false}); + } + } catch (e) { + throw (e.toString()); + } + return _response; + } + ////Update + Future> update( + {required int profileId, + required String token, + required int relationshipId, + required FamilyBackground family}) async { + Map? _response = {}; + String authtoken = "Token $token"; + String path = "${Url.instance.getFamilies()}$profileId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authtoken + }; + Map body = { + "relation_type_id": relationshipId, + "related_person_id": family.relatedPerson!.id, + "company_address": family.companyAddress, + "company_contact_number": family.companyContactNumber, + "first_name": family.relatedPerson!.firstName, + "middle_name": family.relatedPerson!.middleName, + "last_name": family.relatedPerson!.lastName, + "name_extension": family.relatedPerson?.nameExtension, + "birthdate": family.relatedPerson!.birthdate.toString(), + "sex": family.relatedPerson!.sex, + "blood_type": family.relatedPerson?.bloodType, + "civil_status": family.relatedPerson?.civilStatus, + "height": family.relatedPerson!.heightM, + "weight": family.relatedPerson!.weightKg, + "position_id": family.position?.id, + "agency_id": family.company?.id, + "_positionName": family.position?.title, + "_agencyName": family.company?.name, + "_agencyCatId": family.company?.category?.id, + "_privateEntity": family.company?.privateEntity, + "maidenMiddleName": family.relatedPerson?.maidenName?.middleName, + "maidenLastName": family.relatedPerson?.maidenName?.lastName, + "gender": family.relatedPerson!.gender, + "deceased": family.relatedPerson!.deceased, + }; + try { + http.Response response = await Request.instance + .putRequest(param: {}, path: path, body: body, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + _response = data; + } else { + _response.addAll({'success': false}); + } + } catch (e) { + throw (e.toString()); + } + return _response; + } + + Future delete( + {required int personRelatedId, + required int profileId, + required String token}) async { + bool? success; + String authtoken = "Token $token"; + String path = "${Url.instance.getFamilies()}$profileId/"; + Map body = {"related_person_id": personRelatedId}; + Map params = {"force_mode": "true"}; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authtoken + }; + try { + http.Response response = await Request.instance.deleteRequest( + path: path, headers: headers, body: body, param: params); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + success = data['success']; + } else { + success = false; + } + } catch (e) { + throw (e.toString()); + } + return success!; + } + + +} diff --git a/lib/sevices/profile/identification_services.dart b/lib/sevices/profile/identification_services.dart new file mode 100644 index 0000000..64c537b --- /dev/null +++ b/lib/sevices/profile/identification_services.dart @@ -0,0 +1,125 @@ +import 'dart:convert'; + +import 'package:unit2/model/profile/basic_information/identification_information.dart'; +import 'package:http/http.dart' as http; +import '../../utils/request.dart'; +import '../../utils/urls.dart'; + +class IdentificationServices { + static final IdentificationServices _instance = IdentificationServices(); + static IdentificationServices get instance => _instance; + + Future> add( + {required Identification identification, + required int profileId, + required String token}) async { + Map? responseData = {}; + String authToken = "Token $token"; + String path = "${Url.instance.identifications()}$profileId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + Map body = { + "agency_id": identification.agency!.id, + "identification_number": identification.identificationNumber, + "date_issued": identification.dateIssued == null + ? null + : identification.dateIssued.toString(), + "expiration_date": identification.expirationDate == null + ? null + : identification.expirationDate.toString(), + "as_pdf_reference": identification.asPdfReference, + "_agencyName": identification.agency!.name, + "_agencyCatId": identification.agency!.category!.id, + "_privateEntity": identification.agency!.privateEntity, + "_citymunCode": identification.issuedAt?.cityMunicipality?.code, + "_countryId": identification.issuedAt!.country!.id + }; + try { + http.Response response = await Request.instance + .postRequest(param: {}, path: path, body: body, headers: headers); + if (response.statusCode == 201) { + Map data = jsonDecode(response.body); + responseData = data; + } else { + responseData.addAll({'success': false}); + } + } catch (e) { + throw e.toString(); + } + return responseData; + } + + ////delete + Future delete( + {required int identificationId, + required String token, + required int profileId}) async { + bool success = false; + String authToken = "Token $token"; + String path = "${Url.instance.identifications()}$profileId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + Map params = {"force_mode": "true"}; + Map body = { + "id": identificationId, + }; + try { + http.Response response = await Request.instance.deleteRequest( + path: path, headers: headers, body: body, param: params); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + success = data['success']; + } + } catch (e) { + throw (e.toString()); + } + return success; + } + + Future> update( + {required Identification identification, + required int profileId, + required String token}) async { + Map? responseData = {}; + String authToken = "Token $token"; + String path = "${Url.instance.identifications()}$profileId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + Map body = { + "id":identification.id, + "agency_id": identification.agency!.id, + "identification_number": identification.identificationNumber, + "date_issued": identification.dateIssued == null + ? null + : identification.dateIssued.toString(), + "expiration_date": identification.expirationDate == null + ? null + : identification.expirationDate.toString(), + "as_pdf_reference": identification.asPdfReference, + "_agencyName": identification.agency!.name, + "_agencyCatId": identification.agency!.category!.id, + "_privateEntity": identification.agency!.privateEntity, + "_citymunCode": identification.issuedAt?.cityMunicipality?.code, + "_countryId": identification.issuedAt!.country!.id + }; + try { + http.Response response = await Request.instance + .putRequest(param: {}, path: path, body: body, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + responseData = data; + } else { + responseData.addAll({'success': false}); + } + } catch (e) { + throw e.toString(); + } + return responseData; + } +} diff --git a/lib/utils/alerts.dart b/lib/utils/alerts.dart index 1cd6a82..b10f049 100644 --- a/lib/utils/alerts.dart +++ b/lib/utils/alerts.dart @@ -37,7 +37,7 @@ confirmAlert(context, Function() yes,String title, String subtitle) { ).show(); } -confirmAlertWithCancel(context, Function() yes,Function() no,String title, String subtitle) { +confirmAlertWithCancel(context, Function() yes,Function() no,String title, String subtitle,) { AwesomeDialog( context: context, dialogType: DialogType.question, diff --git a/lib/utils/formatters.dart b/lib/utils/formatters.dart new file mode 100644 index 0000000..8b45879 --- /dev/null +++ b/lib/utils/formatters.dart @@ -0,0 +1,13 @@ + import 'package:mask_text_input_formatter/mask_text_input_formatter.dart'; + +var mobileFormatter = MaskTextInputFormatter( + mask: "+63 (###) ###-####", + filter: {"#": RegExp(r"^[1-9][0-9]*$")}, + type: MaskAutoCompletionType.lazy, + initialText: "0"); + + var landLineFormatter = MaskTextInputFormatter( + mask: "(###) ###-###", + filter: {"#": RegExp(r"^[0-9]")}, + type: MaskAutoCompletionType.lazy, + initialText: "0"); \ No newline at end of file diff --git a/lib/utils/profile_utilities.dart b/lib/utils/profile_utilities.dart index 6debf6f..c5c0dab 100644 --- a/lib/utils/profile_utilities.dart +++ b/lib/utils/profile_utilities.dart @@ -8,6 +8,7 @@ import 'package:unit2/utils/request.dart'; import 'package:unit2/utils/urls.dart'; import '../model/profile/basic_information/contact_information.dart'; +import '../model/profile/family_backround.dart'; import '../model/utils/agency.dart'; import '../model/utils/category.dart'; import '../model/utils/position.dart'; @@ -141,4 +142,33 @@ class ProfileUtilities { } return serviceTypes; } + + //// get relationship type + Future> getRelationshipType() async { + List relationshipTypes= []; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + String path = Url.instance.getRelationshipTypes(); + + try { + http.Response response = await Request.instance + .getRequest(param: {}, path: path, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + for (var element in data['data']) { + Relationship relationship = Relationship.fromJson(element); + if(relationship.category=="personal reference"){ + relationshipTypes.add(relationship); + } + + } + } + } + } catch (e) { + throw e.toString(); + } + return relationshipTypes; + } } diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index 3d755e1..4f41d94 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -6,7 +6,7 @@ class Url { // return '192.168.10.183:3000'; // return 'agusandelnorte.gov.ph'; // return "192.168.10.219:3000"; - return "devweb.agusandelnorte.gov.ph"; + return "playweb.agusandelnorte.gov.ph"; // return 'devapi.agusandelnorte.gov.ph:3004'; } @@ -66,6 +66,10 @@ String getAgencyCategory(){ return "api/jobnet_app/agency_categories/"; } +String identifications(){ + return "/api/jobnet_app/profile/pds/basic/identification/"; +} + ////educational background paths String educationalBackground(){ @@ -119,6 +123,13 @@ String getNonAcademicRecognition(){ String getFamilies(){ return "/api/jobnet_app/profile/pds/family/"; } +String addEmergency(){ + return "/api/profile_app/person_emergency/"; +} +String getRelationshipTypes(){ + return "/api/jobnet_app/relationship_types"; +} + //// contacts path String getServiceTypes(){ From 60c5a295f40be581456d0940ff7fb5fb706e6651 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Mon, 22 May 2023 10:23:56 +0800 Subject: [PATCH 69/86] Successfully implmented Profile API --- ios/Runner.xcodeproj/project.pbxproj | 15 + .../learning_development_bloc.dart | 268 ++- .../learning_development_event.dart | 51 + .../learning_development_state.dart | 111 +- lib/bloc/profile/profile_bloc.dart | 120 +- lib/bloc/profile/profile_event.dart | 23 + lib/bloc/profile/profile_state.dart | 33 + .../voluntary_works/voluntary_work_state.dart | 2 +- .../employee_info/employee_info.dart | 102 +- lib/model/profile/basic_info.dart | 3 +- .../primary-information.dart | 137 +- lib/model/profile/learning_development.dart | 71 +- .../edit_basic_info_modal.dart | 523 ++++++ .../primary_information_screen.dart | 449 +++-- .../basic_information/profile_other_info.dart | 25 + .../components/family_background_screen.dart | 594 ++++--- .../learning_and_development_screen.dart | 224 ++- .../learning_development/add_modal.dart | 1435 ++++++++++++++++ .../learning_development/display_details.dart | 150 ++ .../learning_development/edit_modal.dart | 1451 +++++++++++++++++ .../search_field_widget.dart | 0 .../training_details.dart | 73 + .../profile/components/loading_screen.dart | 8 +- .../components/voluntary_works/add_modal.dart | 2 +- lib/screens/profile/profile.dart | 21 +- .../unit2/homepage.dart/components/menu.dart | 1 + .../profile/learningDevelopment_service.dart | 260 ++- lib/sevices/profile/profile_other_info.dart | 137 ++ lib/sevices/profile/profile_service.dart | 255 ++- lib/utils/global.dart | 2 + lib/utils/request.dart | 2 +- lib/utils/urls.dart | 43 +- pubspec.lock | 8 + pubspec.yaml | 1 + 34 files changed, 5712 insertions(+), 888 deletions(-) create mode 100644 lib/screens/profile/components/basic_information/edit_basic_info_modal.dart create mode 100644 lib/screens/profile/components/basic_information/profile_other_info.dart create mode 100644 lib/screens/profile/components/learning_development/add_modal.dart create mode 100644 lib/screens/profile/components/learning_development/display_details.dart create mode 100644 lib/screens/profile/components/learning_development/edit_modal.dart create mode 100644 lib/screens/profile/components/learning_development/search_field_widget.dart create mode 100644 lib/screens/profile/components/learning_development/training_details.dart create mode 100644 lib/sevices/profile/profile_other_info.dart diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 4cf4c30..230f8d3 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -364,10 +364,15 @@ "$(inherited)", "@executable_path/Frameworks", ); + MARKETING_VERSION = "1.0.0 "; PRODUCT_BUNDLE_IDENTIFIER = "uniT-App"; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; }; name = Profile; @@ -493,11 +498,16 @@ "$(inherited)", "@executable_path/Frameworks", ); + MARKETING_VERSION = "1.0.0 "; PRODUCT_BUNDLE_IDENTIFIER = "uniT-App"; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; @@ -516,10 +526,15 @@ "$(inherited)", "@executable_path/Frameworks", ); + MARKETING_VERSION = "1.0.0 "; PRODUCT_BUNDLE_IDENTIFIER = "uniT-App"; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; diff --git a/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart b/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart index de39dac..b91e8ef 100644 --- a/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart +++ b/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart @@ -1,8 +1,22 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import 'package:unit2/model/location/country.dart'; +import 'package:unit2/model/location/region.dart'; +import 'package:unit2/screens/profile/components/other_information/org_membership/add_modal.dart'; +import 'package:unit2/screens/unit2/login/components/update_required.dart'; import 'package:unit2/sevices/profile/learningDevelopment_service.dart'; +import 'package:unit2/test_data.dart'; +import 'package:unit2/utils/text_container.dart'; +import '../../../model/location/barangay.dart'; +import '../../../model/location/city.dart'; +import '../../../model/location/provinces.dart'; import '../../../model/profile/learning_development.dart'; +import '../../../model/utils/agency.dart'; +import '../../../model/utils/category.dart'; +import '../../../utils/location_utilities.dart'; +import '../../../utils/profile_utilities.dart'; part 'learning_development_event.dart'; part 'learning_development_state.dart'; @@ -10,19 +24,251 @@ part 'learning_development_state.dart'; class LearningDevelopmentBloc extends Bloc { LearningDevelopmentBloc() : super(LearningDevelopmentInitial()) { - List learningsAndDevelopments; + List learningsAndDevelopments = []; + List types = []; + List topics = []; + List globalCountries = []; + List globalRegions = []; + List globalProvinces = []; + List globalCities = []; + List globalBarangay = []; + List agencies = []; + List agencyCategory = []; + + Region? currentRegion; + Country? currentCountry; + Province? currentProvince; + CityMunicipality? currentCity; + Barangay? currentBarangay; on((event, emit) async { - // try { + try { + emit(LearningDevelopmentLoadingState()); + List learnings = await LearningDevelopmentServices + .instance + .getLearningDevelopments(event.profileId, event.token); + learningsAndDevelopments = learnings; + emit(LearningDevelopmentLoadedState( + learningsAndDevelopment: learningsAndDevelopments)); + } catch (e) { + emit(LeaningDevelopmentErrorState(message: e.toString())); + } + }); + ////load + on((event, emit) { + emit(LearningDevelopmentLoadedState( + learningsAndDevelopment: learningsAndDevelopments)); + }); + //// show add form + on((event, emit) async { + try { emit(LearningDevelopmentLoadingState()); - List learnings = await LearningDevelopmentServices - .instance - .getLearningDevelopments(event.profileId, event.token); - learningsAndDevelopments = learnings; - emit(LearningDevelopmentLoadedState( - learningsAndDevelopment: learningsAndDevelopments)); - // } catch (e) { - // emit(LeaningDevelopmentErrorState(message: e.toString())); - // } + if (types.isEmpty) { + List newTypes = + await LearningDevelopmentServices.instance + .getLearningDevelopmentType(); + types = newTypes; + } + if (topics.isEmpty) { + List newTopics = + await LearningDevelopmentServices.instance.getTrainingTopics(); + topics = newTopics; + } + if (globalRegions.isEmpty) { + List regions = await LocationUtils.instance.getRegions(); + globalRegions = regions; + } + if (globalCountries.isEmpty) { + List countries = await LocationUtils.instance.getCountries(); + globalCountries = countries; + } + if (agencies.isEmpty) { + List newAgencies = + await ProfileUtilities.instance.getAgecies(); + agencies = newAgencies; + } + if (agencyCategory.isEmpty) { + List categoryAgencies = + await ProfileUtilities.instance.agencyCategory(); + agencyCategory = categoryAgencies; + } + + emit(LearningDevelopmentAddingState( + types: types, + topics: topics, + countries: globalCountries, + regions: globalRegions, + conductedBy: agencies, + sponsorAgencies: agencies, + agencyCategory: agencyCategory)); + } catch (e) { + emit(LeaningDevelopmentErrorState(message: e.toString())); + } + }); + ////Show edit form + on((event, emit) async { + try{ + if (globalRegions.isEmpty) { + List regions = await LocationUtils.instance.getRegions(); + globalRegions = regions; + } + if (globalCountries.isEmpty) { + List countries = await LocationUtils.instance.getCountries(); + globalCountries = countries; + } + currentCountry = globalCountries.firstWhere((Country country) => + event.learningDevelopment.conductedTraining!.venue!.country!.code == + country.code); + if (!event.isOverseas) { + //// if not overseas + currentRegion = globalRegions.firstWhere((Region region) => + event.learningDevelopment.conductedTraining!.venue! + .cityMunicipality!.province!.region!.code == + region.code); + + globalProvinces = await LocationUtils.instance + .getProvinces(regionCode: currentRegion!.code.toString()); + currentProvince = globalProvinces.firstWhere((Province province) => + event.learningDevelopment.conductedTraining!.venue! + .cityMunicipality!.province!.code == + province.code); + + globalCities = await LocationUtils.instance + .getCities(code: currentProvince!.code.toString()); + + currentCity = globalCities.firstWhere( + (CityMunicipality cityMunicipality) => + event.learningDevelopment.conductedTraining!.venue! + .cityMunicipality!.code == + cityMunicipality.code); + globalBarangay = await LocationUtils.instance + .getBarangay(code: currentCity!.code.toString()); + + if (event.learningDevelopment.conductedTraining?.venue?.barangay?.cityMunicipality != + null) { + currentBarangay = globalBarangay.firstWhere((Barangay barangay) => + event.learningDevelopment.conductedTraining!.venue!.barangay! + .code == + barangay.code); + } else { + currentBarangay = null; + } + } + if (topics.isEmpty) { + List newTopics = + await LearningDevelopmentServices.instance.getTrainingTopics(); + topics = newTopics; + } + if (types.isEmpty) { + List newTypes = + await LearningDevelopmentServices.instance + .getLearningDevelopmentType(); + types = newTypes; + } + if (agencies.isEmpty) { + List newAgencies = await ProfileUtilities.instance.getAgecies(); + agencies = newAgencies; + } + if (agencyCategory.isEmpty) { + List categoryAgencies = + await ProfileUtilities.instance.agencyCategory(); + agencyCategory = categoryAgencies; + } + emit(LearningDevelopmentUpdatingState( + cities: globalCities, + currentBarangay: currentBarangay, + barangay: globalBarangay, + + provinces: globalProvinces, + types: types, + topics: topics, + training: event.learningDevelopment.conductedTraining!.title!, + learningDevelopement: event.learningDevelopment, + currentConductedBy: + event.learningDevelopment.conductedTraining!.conductedBy!, + currentSponsor: event.learningDevelopment.sponsoredBy, + conductedBy: agencies, + sponsorAgencies: agencies, + agencyCategory: agencyCategory, + countries: globalCountries, + regions: globalRegions, + currentRegion: currentRegion, + currentCountry: currentCountry!, + currentProvince: currentProvince, + currentCity: currentCity, + overseas: event.isOverseas)); + }catch(e){ + emit(LearningDevelopmentErrorState(message: e.toString())); + } + }); + + ////Add + on((event, emit) async { + try { + Map status = + await LearningDevelopmentServices.instance.add( + learningDevelopement: event.learningDevelopement, + token: event.token, + profileId: event.profileId); + if (status['success']) { + LearningDevelopement learningDevelopement = + LearningDevelopement.fromJson(status['data']); + learningsAndDevelopments.add(learningDevelopement); + emit(LearningDevelopmentAddedState(response: status)); + } else { + emit(LearningDevelopmentAddedState(response: status)); + } + } catch (e) { + emit(LearningDevelopmentErrorState(message: e.toString())); + } + }); + + ////Update + on((event, emit) async { + try { + Map status = + await LearningDevelopmentServices.instance.update( + learningDevelopement: event.learningDevelopement, + token: event.token, + profileId: event.profileId); + if (status['success']) { + learningsAndDevelopments.removeWhere((LearningDevelopement element) => + element.conductedTraining!.id == event.learningDevelopement.conductedTraining!.id && + element.conductedTraining!.totalHours == event.learningDevelopement.conductedTraining!.totalHours); + LearningDevelopement learningDevelopement = + LearningDevelopement.fromJson(status['data']); + learningsAndDevelopments.add(learningDevelopement); + emit(LearningDevelopmentUpdatedState(response: status)); + } else { + emit(LearningDevelopmentUpdatedState(response: status)); + } + } catch (e) { + emit(LearningDevelopmentErrorState(message: e.toString())); + } + }); + ////delete + on((event, emit) async { + try { + final bool success = await LearningDevelopmentServices.instance.delete( + profileId: event.profileId, + token: event.token, + sponsorId: event.sponsorId, + totalHours: event.hours, + trainingId: event.trainingId); + if (success) { + learningsAndDevelopments.removeWhere((LearningDevelopement element) => + element.conductedTraining!.id == event.trainingId && + element.conductedTraining!.totalHours == event.hours); + emit(DeleteLearningDevelopmentState(success: success)); + } else { + emit(DeleteLearningDevelopmentState(success: success)); + } + } catch (e) { + emit(LearningDevelopmentErrorState(message: e.toString())); + } + }); + //// call errror + on((event, emit) { + emit(LeaningDevelopmentErrorState(message: event.message)); }); } } diff --git a/lib/bloc/profile/learningDevelopment/learning_development_event.dart b/lib/bloc/profile/learningDevelopment/learning_development_event.dart index 9aedc7d..1b1453f 100644 --- a/lib/bloc/profile/learningDevelopment/learning_development_event.dart +++ b/lib/bloc/profile/learningDevelopment/learning_development_event.dart @@ -15,3 +15,54 @@ class GetLearningDevelopments extends LearningDevelopmentEvent { @override List get props => [profileId, token]; } + +class ShowAddLearningDevelopmentForm extends LearningDevelopmentEvent{ + +} +class ShowEditLearningDevelopmentForm extends LearningDevelopmentEvent{ + final LearningDevelopement learningDevelopment; + final int profileId; + final String token; + final bool isOverseas; + const ShowEditLearningDevelopmentForm({required this.isOverseas, required this.learningDevelopment, required this.profileId, required this.token}); +} +class LoadLearniningDevelopment extends LearningDevelopmentEvent{ + +} +////delete +class DeleteLearningDevelopment extends LearningDevelopmentEvent { + final String token; + final int profileId; + final int? sponsorId; + final int trainingId; + final double hours; + const DeleteLearningDevelopment( + {required this.profileId, required this.token, required this.hours,required this.sponsorId, required this.trainingId}); + @override + List get props => [profileId, token, hours,trainingId]; +} + +////add +class AddLearningAndDevelopment extends LearningDevelopmentEvent{ + final LearningDevelopement learningDevelopement; + final int profileId; + final String token; + const AddLearningAndDevelopment({required this.learningDevelopement, required this.profileId, required this.token}); + @override + List get props => [profileId, token,learningDevelopement]; +} + +////update +class UpdateLearningDevelopment extends LearningDevelopmentEvent{ + final LearningDevelopement learningDevelopement; + final int profileId; + final String token; + const UpdateLearningDevelopment({required this.learningDevelopement, required this.profileId, required this.token}); + @override + List get props => [profileId, token,learningDevelopement]; +} + +class CallErrorState extends LearningDevelopmentEvent{ + final String message; + const CallErrorState({required this.message}); +} diff --git a/lib/bloc/profile/learningDevelopment/learning_development_state.dart b/lib/bloc/profile/learningDevelopment/learning_development_state.dart index c4b2337..bf0276b 100644 --- a/lib/bloc/profile/learningDevelopment/learning_development_state.dart +++ b/lib/bloc/profile/learningDevelopment/learning_development_state.dart @@ -2,27 +2,120 @@ part of 'learning_development_bloc.dart'; abstract class LearningDevelopmentState extends Equatable { const LearningDevelopmentState(); - + @override List get props => []; } class LearningDevelopmentInitial extends LearningDevelopmentState {} -class LearningDevelopmentLoadedState extends LearningDevelopmentState{ - final List learningsAndDevelopment; - const LearningDevelopmentLoadedState({required this.learningsAndDevelopment}); - @override +class LearningDevelopmentLoadedState extends LearningDevelopmentState { + final List learningsAndDevelopment; + const LearningDevelopmentLoadedState({required this.learningsAndDevelopment}); + @override List get props => [learningsAndDevelopment]; } -class LeaningDevelopmentErrorState extends LearningDevelopmentState{ +class LeaningDevelopmentErrorState extends LearningDevelopmentState { final String message; const LeaningDevelopmentErrorState({required this.message}); - @override + @override List get props => [message]; } -class LearningDevelopmentLoadingState extends LearningDevelopmentState{ - +class LearningDevelopmentLoadingState extends LearningDevelopmentState {} + +////added State +class LearningDevelopmentAddedState extends LearningDevelopmentState { + final Map response; + const LearningDevelopmentAddedState({required this.response}); + @override + List get props => [response]; +} + +////Updated State +class LearningDevelopmentUpdatedState extends LearningDevelopmentState { + final Map response; + const LearningDevelopmentUpdatedState({required this.response}); + @override + List get props => [response]; +} + +//// Deleted State +class DeleteLearningDevelopmentState extends LearningDevelopmentState { + final bool success; + const DeleteLearningDevelopmentState({required this.success}); + @override + List get props => [success]; +} + +////Update State +class LearningDevelopmentUpdatingState extends LearningDevelopmentState { + final List types; + final List topics; + final LearningDevelopmentType training; + final LearningDevelopement learningDevelopement; + final Agency currentConductedBy; + final Agency? currentSponsor; + final List conductedBy; + final List sponsorAgencies; + final List agencyCategory; + final List countries; + final List regions; + final List provinces; + final List barangay; + final List cities; + final Region? currentRegion; + final Country currentCountry; + final Province? currentProvince; + final CityMunicipality? currentCity; + final Barangay? currentBarangay; + final bool overseas; + + const LearningDevelopmentUpdatingState( + {required this.currentBarangay, + required this.cities, + required this.barangay, + required this.provinces, + required this.types, + required this.topics, + required this.training, + required this.learningDevelopement, + required this.currentConductedBy, + required this.currentSponsor, + required this.conductedBy, + required this.sponsorAgencies, + required this.agencyCategory, + required this.countries, + required this.regions, + required this.currentRegion, + required this.currentCountry, + required this.currentProvince, + required this.currentCity, + required this.overseas}); +} + +////Adding State +class LearningDevelopmentAddingState extends LearningDevelopmentState { + final List types; + final List topics; + final List conductedBy; + final List sponsorAgencies; + final List countries; + final List regions; + final List agencyCategory; + + const LearningDevelopmentAddingState( + {required this.types, + required this.topics, + required this.countries, + required this.regions, + required this.conductedBy, + required this.sponsorAgencies, + required this.agencyCategory}); +} + +class LearningDevelopmentErrorState extends LearningDevelopmentState { + final String message; + const LearningDevelopmentErrorState({required this.message}); } diff --git a/lib/bloc/profile/profile_bloc.dart b/lib/bloc/profile/profile_bloc.dart index 162bb9e..eb2ca08 100644 --- a/lib/bloc/profile/profile_bloc.dart +++ b/lib/bloc/profile/profile_bloc.dart @@ -1,17 +1,62 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:unit2/model/profile/profileInfomation.dart'; +import 'package:unit2/sevices/profile/profile_other_info.dart'; import 'package:unit2/sevices/profile/profile_service.dart'; + +import '../../model/profile/basic_information/primary-information.dart'; +import '../../screens/profile/components/basic_information/profile_other_info.dart'; part 'profile_event.dart'; part 'profile_state.dart'; class ProfileBloc extends Bloc { ProfileBloc() : super(ProfileInitial()) { + Profile? currentProfileInformation; + List religions = []; + List ethnicities = []; + List indigencies = []; + List disabilities = []; + List genders = []; + List bloodType = [ + "NONE", + "A+", + "B+", + "A-", + "B-", + "AB+", + "AB-", + "0+", + "0-" + ]; + List nameExtensions = [ + "NONE", + "N/A", + "SR.", + "JR.", + "I", + "II", + "III", + "IV", + "V", + "VI", + "VII", + "VIII", + "IX", + "X" + ]; + List sexes = ["MALE", "FEMALE"]; + List civilStatus = [ + "NONE", + "SINGLE", + "MARRIED", + "SEPARATED", + "WIDOWED" + ]; + ProfileInformation? globalProfileInformation; on((event, emit) async { - emit(ProfileLoading()); + emit(ProfileLoading()); try { - ProfileInformation? profileInformation = await ProfileService.instance.getProfile(event.token, event.userID); globalProfileInformation = profileInformation; @@ -20,7 +65,74 @@ class ProfileBloc extends Bloc { emit(ProfileErrorState(mesage: e.toString())); } }); - - + on((event, emit) { + currentProfileInformation = event.primaryBasicInformation; + emit(BasicInformationProfileLoaded( + primaryBasicInformation: event.primaryBasicInformation)); + }); + on((event,emit){ + emit(BasicInformationProfileLoaded( + primaryBasicInformation: currentProfileInformation!)); + }); + on((event, emit) async { + try { + emit(BasicPrimaryInformationLoadingState()); + if (religions.isEmpty) { + religions = await ProfileOtherInfoServices.instace + .getReligions(token: event.token); + } + if (genders.isEmpty) { + genders = await ProfileOtherInfoServices.instace + .getGenders(token: event.token); + } + if (ethnicities.isEmpty) { + ethnicities = await ProfileOtherInfoServices.instace + .getEthnicity(token: event.token); + } + if (disabilities.isEmpty) { + disabilities = await ProfileOtherInfoServices.instace + .getDisability(token: event.token); + } + if (indigencies.isEmpty) { + indigencies = await ProfileOtherInfoServices.instace + .getIndigency(token: event.token); + } + emit(BasicInformationEditingState( + primaryInformation: currentProfileInformation!, + extensions: nameExtensions, + sexes: sexes, + bloodTypes: bloodType, + genders: genders, + civilStatus: civilStatus, + disability: disabilities, + ethnicity: ethnicities, + indigenous: indigencies, + religion: religions)); + } catch (e) { + emit(BasicPrimaryInformationErrorState(message: e.toString())); + } + }); + on((event, emit) async { + try { + Map status = await ProfileService.instance + .updateBasicProfileInfo( + token: event.token, + profileId: event.profileId, + profileInfo: event.profileInformation, + genderId: event.genderId, + indigencyId: event.indigencyId, + disabilityId: event.disabilityId, + ethnicityId: event.ethnicityId, + reqligionId: event.religionId); + if (status['success']) { + Profile profile = Profile.fromJson(status['data']); + currentProfileInformation = profile; + emit(BasicProfileInfoEditedState(response: status)); + } + emit(BasicProfileInfoEditedState(response: status)); + } catch (e) { + emit(BasicPrimaryInformationErrorState(message: e.toString())); + } + }); } } diff --git a/lib/bloc/profile/profile_event.dart b/lib/bloc/profile/profile_event.dart index 218cb3e..864edc2 100644 --- a/lib/bloc/profile/profile_event.dart +++ b/lib/bloc/profile/profile_event.dart @@ -19,6 +19,29 @@ class LoadProfileInformation extends ProfileEvent { @override List get props => []; } +class GetPrimaryBasicInfo extends ProfileEvent{ + final Profile primaryBasicInformation; + const GetPrimaryBasicInfo({required this.primaryBasicInformation}); +} +class LoadBasicPrimaryInfo extends ProfileEvent{ + +} + +class ShowPrimaryInfoEditForm extends ProfileEvent{ + final String token; + const ShowPrimaryInfoEditForm({required this.token}); +} +class EditBasicProfileInformation extends ProfileEvent{ +final Profile profileInformation; +final int profileId; +final String token; +final int? genderId; +final int? indigencyId; +final int? disabilityId; +final int? religionId; +final int? ethnicityId; +const EditBasicProfileInformation({required this.disabilityId,required this.ethnicityId, required this.genderId, required this.indigencyId, required this.profileId,required this.profileInformation,required this.religionId,required this.token}); +} diff --git a/lib/bloc/profile/profile_state.dart b/lib/bloc/profile/profile_state.dart index 7c2bddb..811c9a1 100644 --- a/lib/bloc/profile/profile_state.dart +++ b/lib/bloc/profile/profile_state.dart @@ -22,6 +22,39 @@ class ProfileErrorState extends ProfileState { @override List get props => [mesage]; } +class BasicInformationEditingState extends ProfileState{ + final Profile primaryInformation; + final List religion; +final List ethnicity; +final List disability; +final List indigenous; +final List genders; +final Listsexes; +final List bloodTypes; +final List civilStatus; +final List extensions; +const BasicInformationEditingState( {required this.genders, required this.extensions, required this.primaryInformation, required this.sexes, required this.bloodTypes, required this.civilStatus, required this.disability,required this.ethnicity,required this.indigenous,required this.religion}); +} +////Edited State +class BasicProfileInfoEditedState extends ProfileState{ + final Map response; + const BasicProfileInfoEditedState({required this.response}); + @override + List get props => [response]; +} + +class BasicInformationProfileLoaded extends ProfileState{ + final Profile primaryBasicInformation; + const BasicInformationProfileLoaded({required this.primaryBasicInformation}); + +} +class BasicPrimaryInformationLoadingState extends ProfileState{ + +} +class BasicPrimaryInformationErrorState extends ProfileState{ + final String message; + const BasicPrimaryInformationErrorState({required this.message}); +} class ProfileLoading extends ProfileState {} diff --git a/lib/bloc/profile/voluntary_works/voluntary_work_state.dart b/lib/bloc/profile/voluntary_works/voluntary_work_state.dart index a5baeeb..efa0fd7 100644 --- a/lib/bloc/profile/voluntary_works/voluntary_work_state.dart +++ b/lib/bloc/profile/voluntary_works/voluntary_work_state.dart @@ -33,7 +33,7 @@ class VoluntaryWorkAddedState extends VoluntaryWorkState{ @override List get props => [response]; } -////Added State +////Edited State class VoluntaryWorkEditedState extends VoluntaryWorkState{ final Map response; const VoluntaryWorkEditedState({required this.response}); diff --git a/lib/model/login_data/employee_info/employee_info.dart b/lib/model/login_data/employee_info/employee_info.dart index da8b94a..20166df 100644 --- a/lib/model/login_data/employee_info/employee_info.dart +++ b/lib/model/login_data/employee_info/employee_info.dart @@ -1,4 +1,7 @@ import 'package:unit2/model/login_data/employee_info/office.dart'; +import 'package:unit2/model/profile/profileInfomation.dart'; + +import '../../profile/basic_information/primary-information.dart'; class EmployeeInfo { EmployeeInfo({ @@ -35,103 +38,4 @@ class EmployeeInfo { "profile": profile!.toJson(), }; } -class Profile { - Profile({ - this.id, - this.sex, - this.gender, - this.deceased, - this.heightM, - this.birthdate, - this.esigPath, - this.fullName, - this.lastName, - this.weightKg, - this.bloodType, - this.firstName, - this.photoPath, - this.maidenName, - this.middleName, - this.uuidQrcode, - this.civilStatus, - this.titlePrefix, - this.titleSuffix, - this.showTitleId, - this.lastFullName, - this.nameExtension, - }); - int? id; - String? sex; - String? gender; - bool? deceased; - double? heightM; - DateTime? birthdate; - String? esigPath; - String? fullName; - String? lastName; - int? weightKg; - String? bloodType; - String? firstName; - String? photoPath; - String? maidenName; - String? middleName; - String? uuidQrcode; - String? civilStatus; - String? titlePrefix; - String? titleSuffix; - bool? showTitleId; - String? lastFullName; - String? nameExtension; - - factory Profile.fromJson(Map json) => Profile( - id: json["id"], - sex: json["sex"], - gender: json["gender"], - deceased: json["deceased"], - heightM: json["height_m"].toDouble(), - birthdate: DateTime.parse(json["birthdate"]), - esigPath: json["esig_path"], - fullName: json["full_name"], - lastName: json["last_name"], - weightKg: json["weight_kg"], - bloodType: json["blood_type"], - firstName: json["first_name"], - photoPath: json["photo_path"], - maidenName: json["maiden_name"], - middleName: json["middle_name"], - uuidQrcode: json["uuid_qrcode"], - civilStatus: json["civil_status"], - titlePrefix: json["title_prefix"], - titleSuffix: json["title_suffix"], - showTitleId: json["show_title_id"], - lastFullName: json["last_full_name"], - nameExtension: json["name_extension"], - ); - - Map toJson() => { - "id": id, - "sex": sex, - "gender": gender, - "deceased": deceased, - "height_m": heightM, - "birthdate": - "${birthdate!.year.toString().padLeft(4, '0')}-${birthdate!.month.toString().padLeft(2, '0')}-${birthdate!.day.toString().padLeft(2, '0')}", - "esig_path": esigPath, - "full_name": fullName, - "last_name": lastName, - "weight_kg": weightKg, - "blood_type": bloodType, - "first_name": firstName, - "photo_path": photoPath, - "maiden_name": maidenName, - "middle_name": middleName, - "uuid_qrcode": uuidQrcode, - "civil_status": civilStatus, - "title_prefix": titlePrefix, - "title_suffix": titleSuffix, - "show_title_id": showTitleId, - "last_full_name": lastFullName, - "name_extension": nameExtension, - }; -} diff --git a/lib/model/profile/basic_info.dart b/lib/model/profile/basic_info.dart index 243070e..c2a9b31 100644 --- a/lib/model/profile/basic_info.dart +++ b/lib/model/profile/basic_info.dart @@ -5,11 +5,10 @@ import 'package:unit2/model/profile/basic_information/identification_information import 'package:unit2/model/profile/basic_information/primary-information.dart'; class BasicInfo{ - PrimaryInformation? primaryInformation; List contactInformation; List identifications; List citizenships; List addresses; - BasicInfo({required this.addresses, required this.contactInformation, required this.primaryInformation,required this.identifications,required this.citizenships}); + BasicInfo({required this.addresses, required this.contactInformation,required this.identifications,required this.citizenships}); } \ No newline at end of file diff --git a/lib/model/profile/basic_information/primary-information.dart b/lib/model/profile/basic_information/primary-information.dart index adb26f8..80b7535 100644 --- a/lib/model/profile/basic_information/primary-information.dart +++ b/lib/model/profile/basic_information/primary-information.dart @@ -1,83 +1,106 @@ +// To parse this JSON data, do +// +// final primaryInformation = primaryInformationFromJson(jsonString); + +import 'package:meta/meta.dart'; import 'dart:convert'; -class PrimaryInformation { - PrimaryInformation({ - required this.id, - required this.lastName, - required this.firstName, - required this.middleName, - required this.nameExtension, - required this.sex, - required this.birthdate, - required this.civilStatus, - required this.bloodType, - required this.heightM, - required this.weightKg, - required this.photoPath, - required this.esigPath, - required this.maidenName, - required this.deceased, - required this.gender, - required this.uuidQrcode, - required this.titlePrefix, - required this.titleSuffix, - required this.showTitleId, - }); +import 'package:unit2/model/profile/family_backround.dart'; - int? id; - String? lastName; - String? firstName; - String? middleName; - String? nameExtension; - String? sex; - DateTime? birthdate; - String? civilStatus; - String? bloodType; - double? heightM; - double? weightKg; - String? photoPath; - String? esigPath; - String? maidenName; - bool? deceased; - String? gender; - String? uuidQrcode; - String? titlePrefix; - String? titleSuffix; - bool? showTitleId; +import '../../../screens/profile/components/basic_information/profile_other_info.dart'; - factory PrimaryInformation.fromJson(Map json) => - PrimaryInformation( +Profile primaryInformationFromJson(String str) => Profile.fromJson(json.decode(str)); + +String primaryInformationToJson(Profile data) => json.encode(data.toJson()); + +class Profile { + int? id; + String? lastName; + String? firstName; + String? middleName; + String? nameExtension; + String? sex; + DateTime? birthdate; + String? civilStatus; + String? bloodType; + double? heightM; + double? weightKg; + String? photoPath; + String? esigPath; + MaidenName? maidenName; + bool? deceased; + String? uuidQrcode; + String? titlePrefix; + String? titleSuffix; + bool? showTitleId; + String? ethnicity; + String? disability; + String? gender; + String? religion; + String? ip; + + Profile({ + required this.id, + required this.lastName, + required this.firstName, + required this.middleName, + required this.nameExtension, + required this.sex, + required this.birthdate, + required this.civilStatus, + required this.bloodType, + required this.heightM, + required this.weightKg, + required this.photoPath, + required this.esigPath, + required this.maidenName, + required this.deceased, + required this.uuidQrcode, + required this.titlePrefix, + required this.titleSuffix, + required this.showTitleId, + required this.ethnicity, + required this.disability, + required this.gender, + required this.religion, + required this.ip, + }); + + factory Profile.fromJson(Map json) => Profile( id: json["id"], lastName: json["last_name"], firstName: json["first_name"], middleName: json["middle_name"], nameExtension: json["name_extension"], sex: json["sex"], - birthdate: json['birthdate']==null?null:DateTime.parse(json["birthdate"]), + birthdate: DateTime.parse(json["birthdate"]), civilStatus: json["civil_status"], bloodType: json["blood_type"], heightM: json["height_m"]?.toDouble(), - weightKg: json["weight_kg"], + weightKg: json["weight_kg"]?.toDouble(), photoPath: json["photo_path"], esigPath: json["esig_path"], - maidenName: json["maiden_name"], + maidenName: json["maiden_name"]==null?null:MaidenName.fromJson(json["maiden_name"]), deceased: json["deceased"], - gender: json["gender"], uuidQrcode: json["uuid_qrcode"], titlePrefix: json["title_prefix"], titleSuffix: json["title_suffix"], showTitleId: json["show_title_id"], - ); + ethnicity: json["ethnicity"]== null?null:json["ethnicity"]['name'], + disability: json["disability"] == null?null:json['disability']['name'], + gender: json["gender"] == null?null:json['gender']['name'], + religion: json["religion"] == null?null:json['religion']['name'], + ip: json["ip"]==null?null:json['ip']['name'], + ); - Map toJson() => { + Map toJson() => { "id": id, "last_name": lastName, "first_name": firstName, "middle_name": middleName, "name_extension": nameExtension, "sex": sex, - "birthdate": - "${birthdate!.year.toString().padLeft(4, '0')}-${birthdate!.month.toString().padLeft(2, '0')}-${birthdate!.day.toString().padLeft(2, '0')}", + "birthdate": "${birthdate?.year.toString().padLeft(4, '0')}-${birthdate?.month.toString().padLeft(2, '0')}-${birthdate?.day.toString().padLeft(2, '0')}", "civil_status": civilStatus, "blood_type": bloodType, "height_m": heightM, @@ -86,10 +109,14 @@ class PrimaryInformation { "esig_path": esigPath, "maiden_name": maidenName, "deceased": deceased, - "gender": gender, "uuid_qrcode": uuidQrcode, "title_prefix": titlePrefix, "title_suffix": titleSuffix, "show_title_id": showTitleId, - }; + "ethnicity": ethnicity, + "disability": disability, + "gender": gender, + "religion": religion, + "ip": ip, + }; } diff --git a/lib/model/profile/learning_development.dart b/lib/model/profile/learning_development.dart index ce7a045..c00f276 100644 --- a/lib/model/profile/learning_development.dart +++ b/lib/model/profile/learning_development.dart @@ -4,8 +4,11 @@ import 'dart:convert'; +import 'package:unit2/model/location/barangay.dart'; + import '../location/city.dart'; import '../location/country.dart'; +import '../utils/agency.dart'; import '../utils/industry_class.dart'; LearningDevelopement learningDevelopementFromJson(String str) => LearningDevelopement.fromJson(json.decode(str)); @@ -21,13 +24,13 @@ class LearningDevelopement { }); final dynamic attachments; - final EdBy? sponsoredBy; + final Agency? sponsoredBy; final ConductedTraining? conductedTraining; final double? totalHoursAttended; factory LearningDevelopement.fromJson(Map json) => LearningDevelopement( attachments: json["attachments"], - sponsoredBy: json["sponsored_by"] == null ? null : EdBy.fromJson(json["sponsored_by"]), + sponsoredBy: json["sponsored_by"] == null ? null : Agency.fromJson(json["sponsored_by"]), conductedTraining: json["conducted_training"] == null ? null : ConductedTraining.fromJson(json["conducted_training"]), totalHoursAttended: json["total_hours_attended"], ); @@ -62,8 +65,8 @@ class ConductedTraining { final bool? locked; final DateTime? toDate; final DateTime? fromDate; - final int? totalHours; - final EdBy? conductedBy; + final double? totalHours; + final Agency? conductedBy; final List? sessionsAttended; final LearningDevelopmentType? learningDevelopmentType; @@ -75,8 +78,8 @@ class ConductedTraining { locked: json["locked"], toDate: json["to_date"] == null ? null : DateTime.parse(json["to_date"]), fromDate: json["from_date"] == null ? null : DateTime.parse(json["from_date"]), - totalHours: json["total_hours"], - conductedBy: json["conducted_by"] == null ? null : EdBy.fromJson(json["conducted_by"]), + totalHours: double.parse(json["total_hours"].toString()), + conductedBy: json["conducted_by"] == null ? null : Agency.fromJson(json["conducted_by"]), sessionsAttended: json["sessions_attended"] == null ? [] : List.from(json["sessions_attended"]!.map((x) => x)), learningDevelopmentType: json["learning_development_type"] == null ? null : LearningDevelopmentType.fromJson(json["learning_development_type"]), ); @@ -96,58 +99,6 @@ class ConductedTraining { }; } -class EdBy { - EdBy({ - this.id, - this.name, - this.category, - this.privateEntity, - }); - - final int? id; - final String? name; - final SponsoredByCategory? category; - final bool? privateEntity; - - factory EdBy.fromJson(Map json) => EdBy( - id: json["id"], - name: json["name"], - category: json["category"] == null ? null : SponsoredByCategory.fromJson(json["category"]), - privateEntity: json["private_entity"], - ); - - Map toJson() => { - "id": id, - "name": name, - "category": category?.toJson(), - "private_entity": privateEntity, - }; -} - -class SponsoredByCategory { - SponsoredByCategory({ - this.id, - this.name, - this.industryClass, - }); - - final int? id; - final String? name; - final IndustryClass? industryClass; - - factory SponsoredByCategory.fromJson(Map json) => SponsoredByCategory( - id: json["id"], - name: json["name"], - industryClass: json["industry_class"] == null ? null : IndustryClass.fromJson(json["industry_class"]), - ); - - Map toJson() => { - "id": id, - "name": name, - "industry_class": industryClass?.toJson(), - }; -} - class LearningDevelopmentType { LearningDevelopmentType({ @@ -181,7 +132,7 @@ class Venue { final int? id; final Country? country; - final dynamic barangay; + final Barangay? barangay; final VenueCategory? category; final dynamic areaClass; final CityMunicipality? cityMunicipality; @@ -189,7 +140,7 @@ class Venue { factory Venue.fromJson(Map json) => Venue( id: json["id"], country: json["country"] == null ? null : Country.fromJson(json["country"]), - barangay: json["barangay"], + barangay:json["barangay"] == null ? null : Barangay.fromJson(json["country"]), category: json["category"] == null ? null : VenueCategory.fromJson(json["category"]), areaClass: json["area_class"], cityMunicipality: json["city_municipality"] == null ? null : CityMunicipality.fromJson(json["city_municipality"]), diff --git a/lib/screens/profile/components/basic_information/edit_basic_info_modal.dart b/lib/screens/profile/components/basic_information/edit_basic_info_modal.dart new file mode 100644 index 0000000..2cb6160 --- /dev/null +++ b/lib/screens/profile/components/basic_information/edit_basic_info_modal.dart @@ -0,0 +1,523 @@ +import 'package:date_time_picker/date_time_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/model/profile/basic_information/primary-information.dart'; +import 'package:unit2/screens/profile/components/basic_information/profile_other_info.dart'; +import 'package:unit2/screens/unit2/roles/qr_code_scanner.dart/components/save_settings.dart'; +import 'package:unit2/theme-data.dart/btn-style.dart'; + +import '../../../../theme-data.dart/colors.dart'; +import '../../../../theme-data.dart/form-style.dart'; +import '../../../../utils/global.dart'; + +class EditBasicProfileInfoScreen extends StatefulWidget { + final int profileId; + final String token; + const EditBasicProfileInfoScreen( + {super.key, required this.profileId, required this.token}); + + @override + State createState() => + _EditBasicProfileInfoScreenState(); +} + +final bdayController = TextEditingController(); +ProfileOtherInfo? selectedIndigency; +ProfileOtherInfo? selectedEthnicity; +ProfileOtherInfo? selectedReligion; +ProfileOtherInfo? selectedDisability; +ProfileOtherInfo? selectedGender; +String? selectedSex; +String? selectedBloodType; +String? selectedStatus; +String? selectedExtension; +final _formKey = GlobalKey(); + +class _EditBasicProfileInfoScreenState + extends State { + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is BasicInformationEditingState) { + bdayController.text = state.primaryInformation.birthdate.toString(); + selectedSex = state.sexes.firstWhere((element) => + element.toLowerCase() == + state.primaryInformation.sex!.toLowerCase()); + if (state.primaryInformation.bloodType != null) { + selectedBloodType = state.bloodTypes.firstWhere((element) => + element.toLowerCase() == + state.primaryInformation.bloodType?.toLowerCase()); + } + if (state.primaryInformation.nameExtension != null && + state.primaryInformation.nameExtension != "N/A") { + selectedExtension = state.extensions.firstWhere((element) => + element.toLowerCase() == + state.primaryInformation.nameExtension?.toLowerCase()); + } + if (state.primaryInformation.civilStatus != null) { + selectedStatus = state.civilStatus.firstWhere((element) => + element.toLowerCase() == + state.primaryInformation.civilStatus?.toLowerCase()); + } + if (state.primaryInformation.gender != null && + state.primaryInformation.gender != "N/A") { + selectedGender = state.genders.firstWhere((element) => + element.name!.toLowerCase() == + state.primaryInformation.gender?.toLowerCase()); + } + if (state.primaryInformation.ip != null && + state.primaryInformation.ip != "N/A") { + selectedIndigency = state.indigenous.firstWhere((element) => + element.name!.toLowerCase() == + state.primaryInformation.ip!.toLowerCase()); + } + if (state.primaryInformation.ethnicity != null && + state.primaryInformation.ethnicity != "N/A") { + selectedEthnicity = state.ethnicity.firstWhere((element) => + element.name!.toLowerCase() == + state.primaryInformation.ethnicity!.toLowerCase()); + } + if (state.primaryInformation.religion != null && + state.primaryInformation.religion != "N/A") { + selectedReligion = state.religion.firstWhere((element) => + element.name!.toLowerCase() == + state.primaryInformation.religion!.toLowerCase()); + } + if (state.primaryInformation.disability != null && + state.primaryInformation.disability != "N/A") { + selectedDisability = state.disability.firstWhere((element) => + element.name!.toLowerCase() == + state.primaryInformation.disability!.toLowerCase()); + } + + state.genders.insert( + 0, ProfileOtherInfo(id: null, name: "NONE", description: null)); + state.indigenous.insert( + 0, ProfileOtherInfo(id: null, name: "NONE", description: null)); + state.disability.insert( + 0, ProfileOtherInfo(id: null, name: "NONE", description: null)); + state.ethnicity.insert( + 0, ProfileOtherInfo(id: null, name: "NONE", description: null)); + state.religion.insert( + 0, ProfileOtherInfo(id: null, name: "NONE", description: null)); + return Container( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 32), + child: FormBuilder( + key: _formKey, + child: Column( + children: [ + const SizedBox( + height: 28, + ), + FormBuilderTextField( + name: "lastname", + initialValue: state.primaryInformation.lastName, + decoration: normalTextFieldStyle("Last name", ""), + validator: FormBuilderValidators.required( + errorText: "This field is required"), + ), + const SizedBox( + height: 15, + ), + FormBuilderTextField( + name: "firstname", + initialValue: state.primaryInformation.firstName, + decoration: normalTextFieldStyle("First name", ""), + validator: FormBuilderValidators.required( + errorText: "This field is required"), + ), + const SizedBox( + height: 15, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 2, + child: FormBuilderTextField( + name: "middlename", + initialValue: state.primaryInformation.middleName!, + decoration: normalTextFieldStyle("Middle name", ""), + ), + ), + const SizedBox( + width: 10, + ), + Flexible( + flex: 1, + //// name extension + child: DropdownButtonFormField( + value: selectedExtension, + decoration: + normalTextFieldStyle("Name Extension", ""), + validator: FormBuilderValidators.required( + errorText: "This field is required"), + items: state.extensions + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + print(e); + print(selectedExtension); + selectedExtension = e; + }, + ), + ) + ]), + ), + const SizedBox( + height: 15, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + ////Bday + Flexible( + flex: 1, + child: DateTimePicker( + controller: bdayController, + use24HourFormat: false, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + timeHintText: "Birthdate", + decoration: + normalTextFieldStyle("Birthdate *", "*").copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + firstDate: DateTime(1970), + lastDate: DateTime(2100), + icon: const Icon(Icons.date_range), + ), + ), + const SizedBox( + width: 10, + ), + + ////sex + Flexible( + flex: 1, + child: FormBuilderDropdown( + initialValue: selectedSex, + decoration: normalTextFieldStyle("Sex", ""), + name: "sex", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + items: state.sexes + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) {}, + ), + ) + ]), + ), + const SizedBox( + height: 15, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + ////blood type + Flexible( + flex: 1, + child: FormBuilderDropdown( + initialValue: selectedBloodType, + decoration: normalTextFieldStyle("Blood type", ""), + name: "bloodtype", + items: state.bloodTypes + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) {}, + ), + ), + const SizedBox( + width: 10, + ), + //// civil status + Flexible( + flex: 1, + child: FormBuilderDropdown( + initialValue: selectedStatus, + decoration: normalTextFieldStyle("Civil status", ""), + name: "extension", + items: state.civilStatus + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedStatus = e; + }, + ), + ), + const SizedBox( + width: 10, + ), + //// gender + Flexible( + flex: 1, + child: DropdownButtonFormField( + isExpanded: true, + value: selectedGender, + decoration: normalTextFieldStyle("Gender", ""), + items: state.genders + .map((element) => + DropdownMenuItem( + value: element, + child: Text(element.name!))) + .toList(), + onChanged: (e) { + selectedGender = e; + }, + ), + ), + ]), + ), + const SizedBox( + height: 15, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + name: "height", + initialValue: state.primaryInformation.heightM + .toString() + .toString(), + decoration: normalTextFieldStyle("Height", ""), + ), + ), + const SizedBox( + width: 10, + ), + Flexible( + flex: 1, + child: FormBuilderTextField( + name: "weigth", + initialValue: + state.primaryInformation.weightKg!.toString(), + decoration: normalTextFieldStyle("Weight", ""), + ), + ), + ]), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + name: "prefix", + initialValue: state.primaryInformation.titlePrefix + .toString() + .toString(), + decoration: normalTextFieldStyle( + "Title Prefix", "Dr.,Atty.,Engr."), + ), + ), + const SizedBox( + width: 10, + ), + Flexible( + flex: 1, + child: FormBuilderTextField( + name: "suffix", + initialValue: state.primaryInformation.titleSuffix, + decoration: normalTextFieldStyle( + "Title Suffix", "PhD.,MD.,MS.,CE"), + ), + ), + ]), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + ////Indigency + Flexible( + flex: 1, + child: DropdownButtonFormField( + isExpanded: true, + value: selectedIndigency, + decoration: normalTextFieldStyle("Indigency", ""), + items: state.indigenous + .map((element) => + DropdownMenuItem( + value: element, + child: Text(element.name!))) + .toList(), + onChanged: (e) { + selectedIndigency = e; + }, + ), + ), + const SizedBox( + width: 10, + ), + ////Ethnicity + Flexible( + flex: 1, + child: DropdownButtonFormField( + isExpanded: true, + value: selectedEthnicity, + decoration: normalTextFieldStyle("Ethnicity", ""), + items: state.ethnicity + .map((element) => + DropdownMenuItem( + value: element, + child: Text(element.name!))) + .toList(), + onChanged: (e) { + selectedEthnicity = e; + }, + ), + ), + ]), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + ////religion + Flexible( + flex: 1, + child: DropdownButtonFormField( + isExpanded: true, + value: selectedReligion, + decoration: normalTextFieldStyle("Religion", ""), + items: state.religion + .map((element) => + DropdownMenuItem( + value: element, + child: Text(element.name!))) + .toList(), + onChanged: (e) { + selectedReligion = e; + }, + ), + ), + const SizedBox( + width: 10, + ), + ////disabilty + Flexible( + flex: 1, + child: DropdownButtonFormField( + isExpanded: true, + value: selectedDisability, + decoration: normalTextFieldStyle("Disability", ""), + items: state.disability + .map((element) => + DropdownMenuItem( + value: element, + child: Text(element.name!))) + .toList(), + onChanged: (e) { + selectedDisability = e; + }, + ), + ), + ]), + ), + const SizedBox( + height: 32, + ), + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: + mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + if (_formKey.currentState!.saveAndValidate()) { + String lastName = + _formKey.currentState!.value['lastname']; + String firstName = + _formKey.currentState!.value['firstname']; + String? middleName = + _formKey.currentState?.value['middlename']; + String? pref = + _formKey.currentState?.value['prefix']; + String suf = _formKey.currentState?.value['suffix']; + DateTime birthdate = + DateTime.parse(bdayController.text); + double? hM = + _formKey.currentState!.value['height'] == null + ? null + : double.parse( + _formKey.currentState?.value['height']); + double? wKg = + _formKey.currentState!.value['weigth'] == null + ? null + : double.parse( + _formKey.currentState?.value['weigth']); + Profile primaryInformation = Profile( + id: state.primaryInformation.id, + lastName: lastName, + firstName: firstName, + middleName: middleName, + nameExtension: selectedExtension, + sex: selectedSex, + birthdate: birthdate, + civilStatus: selectedStatus, + bloodType: selectedBloodType, + heightM: hM, + weightKg: wKg, + photoPath: state.primaryInformation.photoPath, + esigPath: state.primaryInformation.esigPath, + maidenName: state.primaryInformation.maidenName, + deceased: state.primaryInformation.deceased, + uuidQrcode: state.primaryInformation.uuidQrcode, + titlePrefix: pref, + titleSuffix: suf, + showTitleId: + state.primaryInformation.showTitleId, + ethnicity: selectedEthnicity?.name, + disability: selectedDisability?.name, + gender: selectedGender?.name, + religion: selectedReligion?.name, + ip: selectedIndigency?.name); + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add( + EditBasicProfileInformation( + disabilityId: selectedDisability?.id, + ethnicityId: selectedDisability?.id, + genderId: selectedGender?.id, + indigencyId: selectedIndigency?.id, + profileId: widget.profileId, + profileInformation: primaryInformation, + religionId: selectedReligion?.id, + token: widget.token)); + } + }, + child: const Text("Submit")), + ) + ], + ), + ), + ); + } + return Placeholder(); + }, + ); + } +} diff --git a/lib/screens/profile/components/basic_information/primary_information_screen.dart b/lib/screens/profile/components/basic_information/primary_information_screen.dart index 3dddc42..b506f04 100644 --- a/lib/screens/profile/components/basic_information/primary_information_screen.dart +++ b/lib/screens/profile/components/basic_information/primary_information_screen.dart @@ -1,15 +1,31 @@ +import 'package:date_time_picker/date_time_picker.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:intl/intl.dart'; +import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/model/profile/basic_information/primary-information.dart'; +import 'package:unit2/screens/profile/components/basic_information/edit_basic_info_modal.dart'; +import 'package:unit2/theme-data.dart/btn-style.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/theme-data.dart/form-style.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; +import 'package:unit2/widgets/error_state.dart'; + +import '../../../../utils/alerts.dart'; class PrimaryInfo extends StatefulWidget { - final PrimaryInformation primaryInformation; - const PrimaryInfo({super.key, required this.primaryInformation}); + final int profileId; + final String token; + const PrimaryInfo({ + super.key, + required this.profileId, + required this.token + }); @override State createState() => _PrimaryInfoState(); @@ -19,141 +35,316 @@ class _PrimaryInfoState extends State { @override Widget build(BuildContext context) { final _formKey = GlobalKey(); + bool enabled = false; - DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); + DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); return Scaffold( appBar: AppBar( title: const Text(primaryInformationScreenTitle), centerTitle: true, backgroundColor: primary, + actions: [ + IconButton( + onPressed: () { + context.read().add(ShowPrimaryInfoEditForm(token: widget.token)); + }, + icon: const Icon(Icons.edit)) + ], ), - body: Container( - padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 24), - child: FormBuilder( - child: Column( - children: [ - FormBuilderTextField( - enabled: enabled, - name: lastname, - initialValue: widget.primaryInformation.lastName!, - decoration: normalTextFieldStyle("Last name", ""), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocConsumer( + listener: (context, state) { + if (state is BasicPrimaryInformationLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is BasicInformationProfileLoaded || + state is BasicInformationEditingState || state is BasicPrimaryInformationErrorState || state is BasicProfileInfoEditedState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + if (state is BasicProfileInfoEditedState) { + if (state.response['success']) { + successAlert(context, "Updated Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context + .read() + .add(LoadBasicPrimaryInfo()); + }); + } else { + errorAlert(context, "Update Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context + .read() + .add(LoadBasicPrimaryInfo()); + }); + } + } + }, + builder: (context, state) { + if (state is BasicInformationProfileLoaded) { + return Container( + padding: + const EdgeInsets.symmetric(vertical: 24, horizontal: 24), + child: Column( + children: [ + const SizedBox( + height: 28, + ), + FormBuilderTextField( + enabled: enabled, + name: lastname, + initialValue: state.primaryBasicInformation.lastName!, + decoration: normalTextFieldStyle("Last name", ""), + ), + const SizedBox( + height: 15, + ), + FormBuilderTextField( + enabled: enabled, + name: firstname, + initialValue: state.primaryBasicInformation.firstName!, + decoration: normalTextFieldStyle("First name", ""), + ), + const SizedBox( + height: 15, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 2, + child: FormBuilderTextField( + enabled: enabled, + name: middlename, + initialValue: + state.primaryBasicInformation.middleName!, + decoration: + normalTextFieldStyle("Middle name", ""), + ), + ), + const SizedBox( + width: 10, + ), + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: extensionName, + initialValue: state.primaryBasicInformation + .nameExtension ??= 'N/A', + decoration: + normalTextFieldStyle("Name extension", ""), + ), + ) + ]), + ), + const SizedBox( + height: 15, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: extensionName, + initialValue: dteFormat2.format( + state.primaryBasicInformation.birthdate!), + decoration: + normalTextFieldStyle("Birth date", ""), + ), + ), + const SizedBox( + width: 10, + ), + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: sex, + initialValue: state.primaryBasicInformation.sex!, + decoration: normalTextFieldStyle("Sex", ""), + ), + ) + ]), + ), + const SizedBox( + height: 15, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: "bloodType", + initialValue: + state.primaryBasicInformation.bloodType!, + decoration: + normalTextFieldStyle("Blood type", ""), + ), + ), + const SizedBox( + width: 10, + ), + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: "civilStatus", + initialValue: + state.primaryBasicInformation.civilStatus!, + decoration: + normalTextFieldStyle("Civil Status", ""), + ), + ), + const SizedBox( + width: 10, + ), + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: "gender", + initialValue: state + .primaryBasicInformation.gender ??= "N/A", + decoration: normalTextFieldStyle("Gender", ""), + ), + ), + ]), + ), + const SizedBox( + height: 15, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: height, + initialValue: state + .primaryBasicInformation.heightM! + .toString(), + decoration: normalTextFieldStyle("Height", ""), + ), + ), + const SizedBox( + width: 10, + ), + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: width, + initialValue: state + .primaryBasicInformation.weightKg! + .toString(), + decoration: normalTextFieldStyle("Weight", ""), + ), + ), + const SizedBox( + width: 10, + ), + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: prefixSuffix, + initialValue: + "${state.primaryBasicInformation.titlePrefix ??= "NA"} | ${state.primaryBasicInformation.titleSuffix ??= "N/A"}", + decoration: normalTextFieldStyle( + "Title Prefix and Suffix", ""), + ), + ), + ]), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: height, + initialValue: state.primaryBasicInformation.ip ??= + "N/A", + decoration: + normalTextFieldStyle("Indigenous", ""), + ), + ), + const SizedBox( + width: 10, + ), + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: width, + initialValue: state + .primaryBasicInformation.ethnicity ??= "N/A", + decoration: normalTextFieldStyle("Ethnicity", ""), + ), + ), + ]), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: height, + initialValue: state + .primaryBasicInformation.religion ??= "N/A", + decoration: normalTextFieldStyle("Religion", ""), + ), + ), + const SizedBox( + width: 10, + ), + Flexible( + flex: 1, + child: FormBuilderTextField( + enabled: enabled, + name: width, + initialValue: state + .primaryBasicInformation.disability ??= "N/A", + decoration: + normalTextFieldStyle("Disability", ""), + ), + ), + ]), + ), + ], + ), + ); + }if(state is BasicPrimaryInformationErrorState){ + return SomethingWentWrong(message: state.message, onpressed: (){}); - ), - const SizedBox(height: 15,), - FormBuilderTextField( - enabled: enabled, - name: firstname, - initialValue: widget.primaryInformation.firstName!, - decoration: normalTextFieldStyle("First name", ""), - ), - const SizedBox(height: 15,), - SizedBox( - width: screenWidth, - child: Row(children: [ - Flexible( - flex: 2, - child: FormBuilderTextField( - enabled: enabled, - name: middlename, - initialValue: widget.primaryInformation.middleName!, - decoration: normalTextFieldStyle("Middle name", ""), - ),), - const SizedBox(width: 10,), - Flexible( - flex: 1, - child: FormBuilderTextField( - enabled: enabled, - name: extensionName, - initialValue: widget.primaryInformation.nameExtension??='N/A', - decoration: normalTextFieldStyle("Name extension", ""), - ),) - ]), - - ), - const SizedBox(height: 15,), - SizedBox(width: screenWidth, - child: Row(children: [ - Flexible( - flex: 1, - child: FormBuilderTextField( - enabled: enabled, - name: extensionName, - initialValue: dteFormat2.format(widget.primaryInformation.birthdate!), - decoration: normalTextFieldStyle("Birth date", ""), - ),), - const SizedBox(width: 10,), - Flexible( - flex: 1, - child: FormBuilderTextField( - enabled: enabled, - name: sex, - initialValue: widget.primaryInformation.sex!, - decoration: normalTextFieldStyle("Sex", ""), - ),) - ]),), - const SizedBox(height: 15,), - SizedBox(width: screenWidth, - child: Row(children: [ - Flexible( - flex: 1, - child: FormBuilderTextField( - enabled: enabled, - name: bloodType, - initialValue:widget.primaryInformation.bloodType!, - decoration: normalTextFieldStyle("Blood type", ""), - ),), - const SizedBox(width: 10,), - Flexible( - flex: 1, - child: FormBuilderTextField( - enabled: enabled, - name: civilStatus, - initialValue: widget.primaryInformation.civilStatus!, - decoration: normalTextFieldStyle("Civil Status", ""), - ),), - const SizedBox(width: 10,), - Flexible( - flex: 1, - child: FormBuilderTextField( - enabled: enabled, - name: gender, - initialValue:widget.primaryInformation.gender??="N/A", - decoration: normalTextFieldStyle("Gender", ""), - ),), - ]),), - - const SizedBox(height: 15,), - SizedBox(width: screenWidth, - child: Row(children: [ - Flexible( - flex: 1, - child: FormBuilderTextField( - enabled: enabled, - name: height, - initialValue:widget.primaryInformation.heightM!.toString(), - decoration: normalTextFieldStyle("Height", ""), - ),), - const SizedBox(width: 10,), - Flexible( - flex: 1, - child: FormBuilderTextField( - enabled: enabled, - name: width, - initialValue: widget.primaryInformation.weightKg!.toString(), - decoration: normalTextFieldStyle("Weight", ""), - ),), - const SizedBox(width: 10,), - Flexible( - flex: 1, - child: FormBuilderTextField( - enabled: enabled, - name: prefixSuffix, - initialValue:"${widget.primaryInformation.titlePrefix??="NA"} | ${widget.primaryInformation.titleSuffix??="N/A"}", - decoration: normalTextFieldStyle("Title Prefix and Suffix", ""), - ),), - ]),), - ], - )), + }if(state is BasicInformationEditingState){ + return EditBasicProfileInfoScreen(profileId: widget.profileId, token: widget.token); + } + return Container(); + }, + ), )); } } diff --git a/lib/screens/profile/components/basic_information/profile_other_info.dart b/lib/screens/profile/components/basic_information/profile_other_info.dart new file mode 100644 index 0000000..79f75da --- /dev/null +++ b/lib/screens/profile/components/basic_information/profile_other_info.dart @@ -0,0 +1,25 @@ + + +class ProfileOtherInfo { + final int? id; + final String? name; + final String? description; + + ProfileOtherInfo({ + required this.id, + required this.name, + required this.description, + }); + + factory ProfileOtherInfo.fromJson(Map json) => ProfileOtherInfo( + id: json["id"], + name: json["name"], + description: json["description"], + ); + + Map toJson() => { + "id": id, + "name": name, + "description": description, + }; +} diff --git a/lib/screens/profile/components/family_background_screen.dart b/lib/screens/profile/components/family_background_screen.dart index b7b9109..95de086 100644 --- a/lib/screens/profile/components/family_background_screen.dart +++ b/lib/screens/profile/components/family_background_screen.dart @@ -921,347 +921,307 @@ class _FamilyBackgroundScreenState extends State { Container( child: spouse != null ? Row( - children: [ - Expanded( - child: Column(children: [ - const SizedBox( - height: 5, - ), - ListTile( - dense: true, - visualDensity: - const VisualDensity( - horizontal: -4, - vertical: -4), - title: Text( - "${spouse?.relatedPerson?.firstName} ${spouse?.relatedPerson?.middleName} ${spouse?.relatedPerson!.lastName} ${spouse?.relatedPerson?.nameExtension ?? ''}", - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith( - fontWeight: - FontWeight - .w500), - ), - subtitle: const Text( - "fullname"), - ), - Row( + children: [ + Expanded( + child: Column( children: [ - Checkbox( - visualDensity: - const VisualDensity( + const SizedBox( + height: 5, + ), + ListTile( + dense: true, + visualDensity: + const VisualDensity( + horizontal: + -4, + vertical: + -4), + title: Text( + "${spouse?.relatedPerson?.firstName} ${spouse?.relatedPerson?.middleName} ${spouse?.relatedPerson!.lastName} ${spouse?.relatedPerson?.nameExtension ?? ''}", + style: Theme.of( + context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight.w500), + ), + subtitle: + const Text( + "fullname"), + ), + Row( + children: [ + Checkbox( + visualDensity: const VisualDensity( horizontal: 0, vertical: -4), - value: spouse! - .incaseOfEmergency!, - onChanged: (value) { - confirmAlertWithCancel( - context, () { - final progress = - ProgressHUD.of( - context); - progress! - .showWithText( - "Loading..."); - - context.read().add(AddEmergencyEvent( - contactInfoId: spouse!.emergencyContact!.isNotEmpty - ? spouse! - .emergencyContact! - .first - .contactinfoid - : null, - numberMail: spouse! - .emergencyContact! - .isNotEmpty - ? spouse! - .emergencyContact! - .first - .numbermail - : null, - profileId: - profileId!, - relatedPersonId: - spouse! - .relatedPerson! - .id!, - token: - token!, - requestType: - "CHECKBOX")); - }, - () {}, - "Emergency Contact Information?", - spouse!.incaseOfEmergency == - true - ? "Remove as emergency contact information?" - : "Add as emergency contact information?"); - }), - const Text( - incaseOfEmergency), - ////Add mobile icon - Container( - child: spouse! - .incaseOfEmergency! && - spouse! - .emergencyContact! - .isEmpty - ? IconButton( - visualDensity: const VisualDensity( - horizontal: - -4, - vertical: - -4), - onPressed: - () { - showDialog( - context: - context, - builder: - (BuildContext context) { - return AddMobileNumber( - onPressed: () { - Navigator.of(_scaffoldKey.currentContext!).pop(); - familyBloc.add(AddEmergencyEvent(contactInfoId: spouse!.emergencyContact!.isNotEmpty ? spouse!.emergencyContact!.first.contactinfoid : null, numberMail: formKey.currentState!.value['number_mail'], profileId: profileId!, relatedPersonId: spouse!.relatedPerson!.id!, token: token!, requestType: "CONTACT")); - }, - formKey: formKey); - }); - }, - icon: - const Icon( - Entypo - .plus_circled, - color: - third, - size: 18, - )) - : Container()), - ], - ), - Visibility( - visible: spouse! - .incaseOfEmergency!, - child: Container( - child: spouse! - .emergencyContact! - .isNotEmpty - ? Row( - children: [ - //// edit mobile - GestureDetector( - onTap: - () { + value: spouse! + .incaseOfEmergency!, + onChanged: + (value) { + confirmAlertWithCancel( + context, + () { + final progress = + ProgressHUD.of(context); + progress! + .showWithText("Loading..."); + + context.read().add(AddEmergencyEvent( + contactInfoId: spouse!.emergencyContact!.isNotEmpty ? spouse!.emergencyContact!.first.contactinfoid : null, + numberMail: spouse!.emergencyContact!.isNotEmpty ? spouse!.emergencyContact!.first.numbermail : null, + profileId: profileId!, + relatedPersonId: spouse!.relatedPerson!.id!, + token: token!, + requestType: "CHECKBOX")); + }, + () {}, + "Emergency Contact Information?", + spouse!.incaseOfEmergency == true + ? "Remove as emergency contact information?" + : "Add as emergency contact information?"); + }), + const Text( + incaseOfEmergency), + ////Add mobile icon + Container( + child: spouse!.incaseOfEmergency! && + spouse!.emergencyContact!.isEmpty + ? IconButton( + visualDensity: const VisualDensity(horizontal: -4, vertical: -4), + onPressed: () { showDialog( context: context, builder: (BuildContext context) { return AddMobileNumber( - formKey: formKey, onPressed: () { Navigator.of(_scaffoldKey.currentContext!).pop(); familyBloc.add(AddEmergencyEvent(contactInfoId: spouse!.emergencyContact!.isNotEmpty ? spouse!.emergencyContact!.first.contactinfoid : null, numberMail: formKey.currentState!.value['number_mail'], profileId: profileId!, relatedPersonId: spouse!.relatedPerson!.id!, token: token!, requestType: "CONTACT")); - }); + }, + formKey: formKey); }); }, - child: - Row( - children: [ - const SizedBox( - width: 16, + icon: const Icon( + Entypo.plus_circled, + color: third, + size: 18, + )) + : Container()), + ], + ), + Visibility( + visible: spouse! + .incaseOfEmergency!, + child: Container( + child: spouse!.emergencyContact!.isNotEmpty + ? Row( + children: [ + //// edit mobile + GestureDetector( + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AddMobileNumber( + formKey: formKey, + onPressed: () { + Navigator.of(_scaffoldKey.currentContext!).pop(); + familyBloc.add(AddEmergencyEvent(contactInfoId: spouse!.emergencyContact!.isNotEmpty ? spouse!.emergencyContact!.first.contactinfoid : null, numberMail: formKey.currentState!.value['number_mail'], profileId: profileId!, relatedPersonId: spouse!.relatedPerson!.id!, token: token!, requestType: "CONTACT")); + }); + }); + }, + child: Row( + children: [ + const SizedBox( + width: 16, + ), + Badge( + backgroundColor: third, + textColor: Colors.white, + label: Text(spouse!.emergencyContact!.first.numbermail!), + ), + ], ), - Badge( - backgroundColor: third, - textColor: Colors.white, - label: Text(spouse!.emergencyContact!.first.numbermail!), - ), - ], - ), - ), - ], - ) - : Container())), - Container( - padding: - const EdgeInsets.only( - left: 12), - alignment: - Alignment.topLeft, - child: spouse?.position != - null - ? Column( - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - const SizedBox( - height: 12, - ), - Text( - "OCCUPATION", - textAlign: - TextAlign - .start, - style: Theme.of( - context) - .textTheme - .titleMedium! - .copyWith( - fontWeight: - FontWeight.w500, - color: primary)), - Column( - mainAxisAlignment: - MainAxisAlignment - .start, + ), + ], + ) + : Container())), + Container( + padding: + const EdgeInsets + .only( + left: + 12), + alignment: + Alignment + .topLeft, + child: spouse + ?.position != + null + ? Column( crossAxisAlignment: - CrossAxisAlignment - .start, + CrossAxisAlignment.start, children: [ const SizedBox( - height: 3, + height: + 12, ), Text( - spouse! - .position! - .title!, - style: Theme.of( - context) - .textTheme - .titleMedium, - ), - Text( - spouse! - .company! - .name!, - style: Theme.of( - context) - .textTheme - .titleMedium, - ), - Text( - spouse! - .companyAddress!, - style: Theme.of( - context) - .textTheme - .labelMedium, - ), - Text( - spouse! - .companyContactNumber!, - style: Theme.of( - context) - .textTheme - .labelMedium, - ), + "OCCUPATION", + textAlign: TextAlign.start, + style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500, color: primary)), + Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + const SizedBox( + height: 3, + ), + Text( + spouse!.position!.title!, + style: Theme.of(context).textTheme.titleMedium, + ), + Text( + spouse!.company!.name!, + style: Theme.of(context).textTheme.titleMedium, + ), + Text( + spouse!.companyAddress!, + style: Theme.of(context).textTheme.labelMedium, + ), + Text( + spouse!.companyContactNumber!, + style: Theme.of(context).textTheme.labelMedium, + ), + ], + ) ], ) - ], - ) - : const SizedBox(), - ) - ]), - ), - AppPopupMenu( - offset: const Offset(-10, -10), - elevation: 3, - onSelected: (value)async { - ////delete -= = = = = = = = =>> - if (value == 2) { - confirmAlert(context, () { - final progress = - ProgressHUD.of(context); - progress!.showWithText("Loading..."); - context.read().add( - DeleteFamily( - id: mother! - .relatedPerson!.id!, - profileId: profileId!, - token: token!)); - }, "Delete?", "Confirm Delete?"); - } - if (value == 1) { - ////edit eligibilty-= = = = = = = = =>> + : const SizedBox(), + ) + ]), + ), + AppPopupMenu( + offset: const Offset( + -10, -10), + elevation: 3, + onSelected: + (value) async { + ////delete -= = = = = = = = =>> + if (value == 2) { + confirmAlert( + context, () { + final progress = + ProgressHUD.of( + context); + progress! + .showWithText( + "Loading..."); + context + .read< + FamilyBloc>() + .add(DeleteFamily( + id: mother! + .relatedPerson! + .id!, + profileId: + profileId!, + token: + token!)); + }, "Delete?", + "Confirm Delete?"); + } + if (value == 1) { + ////edit eligibilty-= = = = = = = = =>> - final progress = - ProgressHUD.of( - context); - progress!.showWithText( - "Loading..."); - if (positions.isEmpty) { - positions = - await ProfileUtilities - .instance - .getAgencyPosition(); - } + final progress = + ProgressHUD.of( + context); + progress! + .showWithText( + "Loading..."); + if (positions + .isEmpty) { + positions = + await ProfileUtilities + .instance + .getAgencyPosition(); + } - if (agencices.isEmpty) { - agencices = - await ProfileUtilities - .instance - .getAgecies(); - } - if (categories - .isEmpty) { - categories = - await ProfileUtilities - .instance - .agencyCategory(); - } - progress.dismiss(); - showDialog( - context: NavigationService - .navigatorKey - .currentContext!, - builder: - (BuildContext - context) { - return SpouseEditAlert( - familyBackground: spouse!, - familyBloc: - familyBloc, - token: token!, - profileId: - profileId!, - positions: - positions, - agencies: - agencices, - category: - categories, - bloodType: - bloodType, - civilStatus: - civilStatus, - gender: - gender, - nameExtensions: - nameExtensions, - sexes: sexes); - }); - - } - }, - menuItems: [ - popMenuItem( - text: "Update", - value: 1, - icon: Icons.edit), - popMenuItem( - text: "Reset", - value: 2, - icon: Icons.delete), - ], - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - ), - tooltip: "Options", - ) - ], - ) + if (agencices + .isEmpty) { + agencices = + await ProfileUtilities + .instance + .getAgecies(); + } + if (categories + .isEmpty) { + categories = + await ProfileUtilities + .instance + .agencyCategory(); + } + progress.dismiss(); + showDialog( + context: NavigationService + .navigatorKey + .currentContext!, + builder: + (BuildContext + context) { + return SpouseEditAlert( + familyBackground: + spouse!, + familyBloc: + familyBloc, + token: + token!, + profileId: + profileId!, + positions: + positions, + agencies: + agencices, + category: + categories, + bloodType: + bloodType, + civilStatus: + civilStatus, + gender: + gender, + nameExtensions: + nameExtensions, + sexes: + sexes); + }); + } + }, + menuItems: [ + popMenuItem( + text: "Update", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Reset", + value: 2, + icon: + Icons.delete), + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ) + ], + ) : SizedBox( width: screenWidth, child: const Padding( diff --git a/lib/screens/profile/components/learning_and_development_screen.dart b/lib/screens/profile/components/learning_and_development_screen.dart index f2e8aa1..ae6add0 100644 --- a/lib/screens/profile/components/learning_and_development_screen.dart +++ b/lib/screens/profile/components/learning_and_development_screen.dart @@ -1,13 +1,16 @@ +import 'package:app_popup_menu/app_popup_menu.dart'; import 'package:flutter/material.dart'; import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter/src/widgets/placeholder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; import 'package:intl/intl.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/learning_development.dart'; +import 'package:unit2/screens/profile/components/learning_development/edit_modal.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; @@ -15,33 +18,44 @@ import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; - import '../../../bloc/profile/learningDevelopment/learning_development_bloc.dart'; +import '../../../utils/alerts.dart'; +import 'learning_development/add_modal.dart'; class LearningAndDevelopmentScreen extends StatelessWidget { - - const LearningAndDevelopmentScreen( - {super.key,}); + const LearningAndDevelopmentScreen({ + super.key, + }); @override Widget build(BuildContext context) { + String token; + int profileId; DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); return Scaffold( appBar: AppBar( title: const Text(learningAndDevelopmentScreenTitle), centerTitle: true, backgroundColor: primary, - actions: [AddLeading(onPressed: () {})], + actions: [ + AddLeading(onPressed: () { + context + .read() + .add(ShowAddLearningDevelopmentForm()); + }) + ], ), body: ProgressHUD( padding: const EdgeInsets.all(24), indicatorWidget: const SpinKitFadingCircle( color: Colors.white, ), - backgroundColor: Colors.black87, + backgroundColor: Colors.black87, child: BlocBuilder( builder: (context, state) { if (state is UserLoggedIn) { + token = state.userData!.user!.login!.token!; + profileId = state.userData!.user!.login!.user!.profileId!; return BlocBuilder( builder: (context, state) { if (state is ProfileLoaded) { @@ -53,10 +67,76 @@ class LearningAndDevelopmentScreen extends StatelessWidget { progress!.showWithText("Please wait..."); } if (state is LearningDevelopmentLoadedState || - state is LeaningDevelopmentErrorState) { + state is LearningDevelopmentErrorState || + state is LearningDevelopmentAddingState || + state is LearningDevelopmentAddedState || + state is LearningDevelopmentUpdatingState) { final progress = ProgressHUD.of(context); progress!.dismiss(); } + //// Added State + if (state is LearningDevelopmentAddedState) { + if (state.response['success']) { + successAlert(context, "Adding Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context + .read() + .add(LoadLearniningDevelopment()); + }); + } else { + errorAlert(context, "Adding Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context + .read() + .add(LoadLearniningDevelopment()); + }); + } + } + ////Updated State + if (state is LearningDevelopmentUpdatedState) { + if (state.response['success']) { + successAlert(context, "Update Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context + .read() + .add(LoadLearniningDevelopment()); + }); + } else { + errorAlert(context, "Update Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context + .read() + .add(LoadLearniningDevelopment()); + }); + } + } + ////Deleted State + if (state is DeleteLearningDevelopmentState) { + if (state.success) { + successAlert(context, "Deletion Successfull!", + "Deleted Successfully", () { + Navigator.of(context).pop(); + context + .read() + .add(LoadLearniningDevelopment()); + }); + } else { + errorAlert(context, "Deletion Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context + .read() + .add(LoadLearniningDevelopment()); + }); + } + } // TODO: implement listener }, builder: (context, state) { @@ -141,13 +221,98 @@ class LearningAndDevelopmentScreen extends StatelessWidget { const SizedBox( height: 5, ), - ]), ), - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.more_vert)), + AppPopupMenu( + offset: const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + ////delete -= = = = = = = = =>> + if (value == 2) { + confirmAlert(context, () { + final progress = + ProgressHUD.of( + context); + progress!.showWithText( + "Loading..."); + BlocProvider.of< + LearningDevelopmentBloc>( + context) + .add(DeleteLearningDevelopment( + trainingId: state + .learningsAndDevelopment[ + index] + .conductedTraining! + .id!, + hours: state + .learningsAndDevelopment[ + index] + .conductedTraining! + .totalHours!, + sponsorId: state + .learningsAndDevelopment[ + index] + .sponsoredBy + ?.id, + profileId: + profileId, + token: token)); + }, "Delete?", + "Confirm Delete?"); + } + if (value == 1) { + bool isOverseas; + ////edit = = = = = = = =>> + final progress = + ProgressHUD.of(context); + progress!.showWithText( + "Loading..."); + + if (state + .learningsAndDevelopment[ + index] + .conductedTraining + ?.venue + ?.cityMunicipality == + null) { + isOverseas = true; + } else { + isOverseas = false; + } + context + .read< + LearningDevelopmentBloc>() + .add(ShowEditLearningDevelopmentForm( + profileId: + profileId, + token: token, + learningDevelopment: + state.learningsAndDevelopment[ + index], + isOverseas: + isOverseas)); + } + }, + menuItems: [ + popMenuItem( + text: "Edit", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Delete", + value: 2, + icon: Icons.delete), + popMenuItem( + text: "Attachment", + value: 3, + icon: FontAwesome.attach) + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ) ], ), ), @@ -158,15 +323,27 @@ class LearningAndDevelopmentScreen extends StatelessWidget { ); }); } else { - const EmptyData( + return const EmptyData( message: "You don't have any Learning and Development added. Please click + to add."); } } - if (state is LeaningDevelopmentErrorState) { + if (state is LearningDevelopmentErrorState) { return (SomethingWentWrong( message: state.message, onpressed: () {})); } + if (state is LearningDevelopmentAddingState) { + return AddLearningAndDevelopmentScreen( + token: token, + profileId: profileId, + ); + } + if (state is LearningDevelopmentUpdatingState) { + return EditLearningAndDevelopmentScreen( + token: token, + profileId: profileId, + ); + } return Container(); }, ); @@ -180,4 +357,23 @@ class LearningAndDevelopmentScreen extends StatelessWidget { ), )); } + + PopupMenuItem popMenuItem({String? text, int? value, IconData? icon}) { + return PopupMenuItem( + value: value, + child: Row( + children: [ + Icon( + icon, + ), + const SizedBox( + width: 10, + ), + Text( + text!, + ), + ], + ), + ); + } } diff --git a/lib/screens/profile/components/learning_development/add_modal.dart b/lib/screens/profile/components/learning_development/add_modal.dart new file mode 100644 index 0000000..52fe09b --- /dev/null +++ b/lib/screens/profile/components/learning_development/add_modal.dart @@ -0,0 +1,1435 @@ +import 'package:date_time_picker/date_time_picker.dart'; +import 'package:flutter/material.dart'; + +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:intl/intl.dart'; +import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; +import 'package:searchable_paginated_dropdown/searchable_paginated_dropdown.dart'; +import 'package:searchfield/searchfield.dart'; +import 'package:unit2/bloc/profile/learningDevelopment/learning_development_bloc.dart'; +import 'package:unit2/model/profile/learning_development.dart'; +import 'package:unit2/screens/profile/components/learning_development/display_details.dart'; +import 'package:unit2/screens/profile/components/learning_development/training_details.dart'; +import 'package:unit2/sevices/profile/learningDevelopment_service.dart'; +import 'package:unit2/theme-data.dart/btn-style.dart'; +import 'package:unit2/utils/text_container.dart'; +import 'package:unit2/utils/validators.dart'; + +import '../../../../model/location/barangay.dart'; +import '../../../../model/location/city.dart'; +import '../../../../model/location/country.dart'; +import '../../../../model/location/provinces.dart'; +import '../../../../model/location/region.dart'; +import '../../../../model/utils/agency.dart'; +import '../../../../model/utils/category.dart'; +import '../../../../theme-data.dart/box_shadow.dart'; +import '../../../../theme-data.dart/colors.dart'; +import '../../../../theme-data.dart/form-style.dart'; +import '../../../../utils/global.dart'; +import '../../../../utils/location_utilities.dart'; +import '../../shared/add_for_empty_search.dart'; + +class AddLearningAndDevelopmentScreen extends StatefulWidget { + final int profileId; + final String token; + const AddLearningAndDevelopmentScreen( + {super.key, required this.profileId, required this.token}); + + @override + State createState() => + _AddLearningAndDevelopmentScreenState(); +} + +class _AddLearningAndDevelopmentScreenState + extends State { + final formKey = GlobalKey(); + + DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); + + bool isPrivate = false; + + bool showSponsorByAgency = false; + bool showOtherInputs = false; + bool showTrainingDetails = false; + bool hasSponsor = false; + + LearningDevelopmentType? selectedLearningDevelopmentType; + final fromDateController = TextEditingController(); + final toDateController = TextEditingController(); +////Training + bool show = true; + final addTrainingController = TextEditingController(); + ConductedTraining? selectedConductedTraining; + LearningDevelopmentType? selectedTraining; + +////Topic + final topicFocusNode = FocusNode(); + final addTopicController = TextEditingController(); + LearningDevelopmentType? selectedTopic; + + +////address + Barangay? selectedBarangay; + Country? selectedCountry; + Region? selectedRegion; + Province? selectedProvince; + CityMunicipality? selectedMunicipality; + bool overseas = false; + bool provinceCall = false; + bool cityCall = false; + bool barangayCall = false; + List? provinces; + List? citymuns; + List? barangays; + + ////Sponsor Agency Searchfield + Agency? selectedSponsorAgency; + Category? selectedSponsorAgencyCategory; + bool showSponsorCategoryAgency = false; + bool showSponsorAgencyPrivateRadio = false; + bool sponsorAgencyIsPrivate = false; + final addSponsorAgencyController = TextEditingController(); + final sponsorByFocusNode = FocusNode(); + final sponsorAgencyCategoryFocusNode = FocusNode(); + +////Conducted By + Agency? selectedConductedByAgency; + Category? selectedConductedByAgencyCategory; + bool showConductedByAgencyPrivateRadio = false; + final conductedByFocusNode = FocusNode(); + final conductedByAgencyCategoryFocusNode = FocusNode(); + final addConductedByController = TextEditingController(); + bool showConductedByAgencyCategory = false; + bool conductedByCategoryIsPrivate = false; + + final selectedTrainingController = TextEditingController(); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is LearningDevelopmentAddingState) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 24), + child: FormBuilder( + key: formKey, + child: ListView( + children: [ + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + ////Training SearchField + SizedBox( + child: show + ? SearchableDropdownFormField.paginated( + noRecordTex: SizedBox( + width: double.infinity, + height: 300, + child: EmptyWidget( + controller: addTrainingController, + onpressed: () { + setState(() { + show = false; + showOtherInputs = true; + + selectedTrainingController.text = + addTrainingController.text.toUpperCase(); + selectedTraining = + LearningDevelopmentType( + id: null, + title: + selectedTrainingController + .text); + addTrainingController.text =""; + Navigator.of(context).pop(); + Navigator.of(context).pop(); + }); + }, + title: "Add Training"), + ), + isDialogExpanded: false, + hintText: const Text('Search Training'), + margin: const EdgeInsets.all(15), + backgroundDecoration: (child) { + return SizedBox( + width: double.infinity, + child: Card( + child: Padding( + padding: const EdgeInsets.all(16), + child: child), + ), + ); + }, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + paginatedRequest: + (int page, String? searchKey) async { + List paginatedList = + await LearningDevelopmentServices + .instance + .getConductedTrainings( + page: page, + key: searchKey ??= ""); + return paginatedList.map((e) { + return SearchableDropdownMenuItem( + value: e, + onTap: () {}, + label: e.title!.title!, + child: TrainingDisplayDetails( + ////not what you are looking for + notWhatYourLookingFor: () { + setState(() { + if (e.learningDevelopmentType + ?.id != + null) { + selectedTraining = + LearningDevelopmentType( + id: e.title!.id, + title: null); + } else { + selectedTraining = + LearningDevelopmentType( + id: null, + title: + e.title!.title); + } + show = false; + showOtherInputs = true; + + selectedTrainingController + .text = e.title!.title!; + Navigator.of(context).pop(); + }); + }, + e: e, + )); + }).toList(); + }, + dropDownMaxHeight: 500, + requestItemCount: 5, + //// on chage + onChanged: (ConductedTraining? value) { + setState(() { + selectedConductedTraining = value; + selectedTrainingController.text = + selectedConductedTraining! + .title!.title!; + show = false; + showTrainingDetails = true; + }); + }, + ) + : const SizedBox(), + ), + const SizedBox( + height: 10, + ), + SizedBox( + ////Training selected Textfield + child: !show + ? FormBuilderTextField( + maxLines: 5, + readOnly: true, + controller: selectedTrainingController, + name: "", + decoration: normalTextFieldStyle("", "") + .copyWith( + labelText: "Training", + suffixIcon: IconButton( + onPressed: () { + setState(() { + selectedConductedTraining = + null; + selectedTrainingController + .text = ""; + show = true; + showTrainingDetails = + false; + showOtherInputs = false; + selectedTraining = null; + }); + }, + icon: + const Icon(Icons.close))), + ) + : const SizedBox()), + + ////ShowTraining Details + SizedBox( + child: showTrainingDetails + ? TrainingDetails( + trainingTitle: selectedConductedTraining! + .learningDevelopmentType!.title!, + totalHours: + selectedConductedTraining!.totalHours!, + trainingTopic: selectedConductedTraining! + .topic!.title!, + toDate: selectedConductedTraining!.toDate + .toString(), + fromDate: selectedConductedTraining! + .fromDate! + .toString(), + conductedBy: selectedConductedTraining! + .conductedBy!.name!) + : const SizedBox(), + ), + ///// end show training details + //// show other inputs + SizedBox( + child: showOtherInputs + ? SizedBox( + child: Column(children: [ + const SizedBox( + height: 12, + ), + ////learning development type + FormBuilderDropdown< + LearningDevelopmentType>( + decoration: normalTextFieldStyle( + "Learning Development Type *", + ""), + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + name: "types", + items: state.types + .map((e) => DropdownMenuItem< + LearningDevelopmentType>( + value: e, + child: Text(e.title!))) + .toList(), + onChanged: (value) { + selectedLearningDevelopmentType = + value; + }, + ), + + //// learning development topics + const SizedBox( + height: 12, + ), + StatefulBuilder( + builder: (context, setState) { + return SearchField( + focusNode: topicFocusNode, + itemHeight: 100, + suggestionsDecoration: box1(), + suggestions: state.topics + .map((LearningDevelopmentType + topic) => + SearchFieldListItem( + topic.title!, + item: topic, + child: Padding( + padding: + const EdgeInsets + .symmetric( + horizontal: + 10), + child: ListTile( + title: Text( + topic.title!, + softWrap: true, + ), + )))) + .toList(), + + searchInputDecoration: + normalTextFieldStyle( + "Topic *", "") + .copyWith( + suffixIcon: const Icon( + Icons + .arrow_drop_down)), + onSuggestionTap: (topic) { + if (topic.item?.id != null) { + selectedTopic = + LearningDevelopmentType( + id: topic.item!.id, + title: null); + } else { + selectedTopic = + LearningDevelopmentType( + id: null, + title: + topic.item!.title); + } + setState(() { + topicFocusNode.unfocus(); + }); + }, + ////EMPTY WIDGET + emptyWidget: EmptyWidget( + title: "Add Topic", + controller: addTopicController, + onpressed: () { + setState(() { + LearningDevelopmentType + newTopic = + LearningDevelopmentType( + id: null, + title: addTopicController + .text + .toUpperCase()); + state.topics + .insert(0, newTopic); + topicFocusNode.unfocus(); + addTopicController.text = + ""; + Navigator.pop(context); + }); + }), + validator: (position) { + if (position!.isEmpty) { + return "This field is required"; + } + return null; + }, + ); + }), + + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + //// FROM DATE + Flexible( + flex: 1, + child: DateTimePicker( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + use24HourFormat: false, + icon: const Icon( + Icons.date_range), + controller: + fromDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + timeHintText: + "Date of Examination/Conferment", + decoration: + normalTextFieldStyle( + "From *", + "From *") + .copyWith( + prefixIcon: + const Icon( + Icons.date_range, + color: Colors.black87, + )), + initialValue: null, + )), + const SizedBox( + width: 12, + ), + //// TO DATE + Flexible( + flex: 1, + child: DateTimePicker( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + controller: toDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + decoration: + normalTextFieldStyle( + "To *", "To *") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + ), + ), + initialValue: null, + ), + ), + ], + ), + ), + const SizedBox( + height: 12, + ), + //// total hours conducted + FormBuilderTextField( + validator: numericRequired, + name: "total_hours", + decoration: normalTextFieldStyle( + "Total Hours Conducted *", "0"), + keyboardType: TextInputType.number, + ), + const SizedBox( + height: 12, + ), + ////Address + StatefulBuilder( + builder: (context, setState) { + return Column( + children: [ + ////overseas textformfield + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: + normalTextFieldStyle( + "Overseas Address?", + ''), + name: 'overseas', + title: Text( + overseas ? "YES" : "NO"), + ), + SizedBox( + height: + overseas == true ? 8 : 0, + ), + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + FormBuilderDropdown< + Region?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: + FormBuilderValidators + .required( + errorText: + "This field is required"), + onChanged: (Region? + region) async { + if (selectedRegion != + region) { + setState(() { + provinceCall = + true; + }); + selectedRegion = + region; + getProvinces(); + } + }, + initialValue: null, + decoration: + normalTextFieldStyle( + "Region*", + "Region"), + name: 'region', + items: state.regions.map< + DropdownMenuItem< + Region>>((Region + region) { + return DropdownMenuItem< + Region>( + value: region, + child: Text(region + .description!)); + }).toList(), + ), + const SizedBox( + height: 8, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: + ModalProgressHUD( + color: Colors + .transparent, + inAsyncCall: + provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == + null + ? 'required' + : null, + isExpanded: + true, + value: + selectedProvince, + onChanged: + (Province? + province) { + if (selectedProvince != + province) { + setState( + () { + cityCall = + true; + }); + selectedProvince = + province; + getCities(); + } + }, + items: provinces == + null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>((Province + province) { + return DropdownMenuItem( + value: province, + child: FittedBox( + child: Text(province.description!), + )); + }).toList(), + decoration: normalTextFieldStyle( + "Province*", + "Province")), + ), + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: + ModalProgressHUD( + color: + Colors.white, + inAsyncCall: + cityCall, + child: DropdownButtonFormField< + CityMunicipality>( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + isExpanded: + true, + onChanged: + (CityMunicipality? + city) { + if (selectedMunicipality != + city) { + setState( + () { + barangayCall = + true; + }); + selectedMunicipality = + city; + getBarangays(); + } + }, + decoration: normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: + selectedMunicipality, + items: citymuns == + null + ? [] + : citymuns!.map< + DropdownMenuItem>( + (CityMunicipality + c) { + return DropdownMenuItem( + value: + c, + child: + Text(c.description!)); + }).toList(), + ), + ), + ), + //// BARANGAY + SizedBox( + height: 60, + child: + ModalProgressHUD( + color: + Colors.white, + inAsyncCall: + barangayCall, + child: + DropdownButtonFormField< + Barangay>( + isExpanded: + true, + onChanged: + (Barangay? + baragay) { + selectedBarangay = + baragay; + }, + decoration: normalTextFieldStyle( + "Barangay*", + "Barangay"), + value: + selectedBarangay, + items: barangays == + null + ? [] + : barangays!.map< + DropdownMenuItem< + Barangay>>((Barangay + barangay) { + return DropdownMenuItem( + value: + barangay, + child: + Text(barangay.description!)); + }).toList(), + ), + ), + ), + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( + height: 60, + child: + FormBuilderDropdown< + Country>( + initialValue: null, + validator: + FormBuilderValidators + .required( + errorText: + "This field is required"), + items: state.countries.map< + DropdownMenuItem< + Country>>( + (Country + country) { + return DropdownMenuItem< + Country>( + value: country, + child: FittedBox( + child: Text( + country + .name!))); + }).toList(), + name: 'country', + decoration: + normalTextFieldStyle( + "Country*", + "Country"), + onChanged: + (Country? value) { + selectedCountry = + value; + }, + ), + ), + ), + ], + ); + }), + + ////Conducted By + StatefulBuilder( + builder: (context, setState) { + ////has sponsor switch + return Column( + children: [ + ////Add Conducted Agency============ + SizedBox( + child: SizedBox( + child: Column(children: [ + SearchField( + suggestionDirection: + SuggestionDirection + .up, + itemHeight: 70, + focusNode: + conductedByFocusNode, + suggestions: state + .conductedBy + .map((Agency + agency) => + SearchFieldListItem( + agency.name!, + item: agency, + child: + ListTile( + title: Text( + agency + .name!, + overflow: + TextOverflow + .ellipsis, + ), + subtitle: Text(agency.privateEntity == + true + ? "Private" + : agency.privateEntity == + false + ? "Government" + : ""), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle( + " Conducted By *", + "") + .copyWith( + suffixIcon: + GestureDetector( + child: const Icon( + Icons.arrow_drop_down, + ), + onTap: () => + conductedByFocusNode + .unfocus(), + )), + ////SELETECTED + onSuggestionTap: + (agency) { + setState(() { + if (agency.item?.id != + null) { + selectedConductedByAgency = + Agency( + name: null, + id: agency + .item! + .id); + } else { + selectedConductedByAgency = + Agency( + id: null, + name: agency + .item! + .name); + } + + if (agency.item! + .privateEntity == + null) { + showConductedByAgencyCategory = + true; + showConductedByAgencyPrivateRadio = + true; + } else { + showConductedByAgencyCategory = + false; + showConductedByAgencyPrivateRadio = + false; + } + conductedByFocusNode + .unfocus(); + }); + }, + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + ////conducter empty widget + emptyWidget: EmptyWidget( + controller: + addConductedByController, + onpressed: () { + setState(() { + Agency newAgency = Agency( + id: null, + name: addConductedByController + .text + .toUpperCase(), + category: + null, + privateEntity: + null); + state.conductedBy + .insert(0, + newAgency); + + addConductedByController + .text = ""; + Navigator.pop( + context); + }); + }, + title: + "Add Conducted By Agency")), + SizedBox( + height: + showConductedByAgencyCategory + ? 12 + : 0, + ), + ////Sponsor Agency Category + SizedBox( + child: + showConductedByAgencyCategory + ? SearchField( + suggestionDirection: + SuggestionDirection + .up, + focusNode: + conductedByAgencyCategoryFocusNode, + itemHeight: 70, + suggestions: state + .agencyCategory + .map((Category category) => SearchFieldListItem( + category + .name!, + item: + category, + child: + ListTile( + title: + Text(category.name!), + subtitle: Text(category + .industryClass! + .name!), + ))) + .toList(), + emptyWidget: + Container( + height: 100, + decoration: + box1(), + child: const Center( + child: Text( + "No result found ...")), + ), + onSuggestionTap: + (agencyCategory) { + setState(() { + selectedConductedByAgencyCategory = + agencyCategory + .item; + selectedConductedByAgency = Agency( + id: selectedConductedByAgency + ?.id, + name: selectedConductedByAgency! + .name, + category: + selectedConductedByAgencyCategory, + privateEntity: + showConductedByAgencyPrivateRadio); + conductedByAgencyCategoryFocusNode + .unfocus(); + }); + }, + searchInputDecoration: normalTextFieldStyle( + "Category *", + "") + .copyWith( + suffixIcon: + const Icon(Icons.arrow_drop_down)), + validator: + (value) { + if (value! + .isEmpty) { + return "This field is required"; + } + return null; + }, + ) + : const SizedBox(), + ), + + ////Sponsor Agency Private Radio + SizedBox( + height: + showConductedByAgencyPrivateRadio + ? 12 + : 0), + SizedBox( + child: showConductedByAgencyPrivateRadio + ? FormBuilderSwitch( + initialValue: + conductedByCategoryIsPrivate, + title: Text( + conductedByCategoryIsPrivate + ? "YES" + : "NO"), + decoration: normalTextFieldStyle( + "Private Entity?", + 'Private Entity?'), + + ////onvhange private sector + onChanged: + (value) { + setState(() { + conductedByCategoryIsPrivate = + value!; + selectedConductedByAgency = Agency( + category: + selectedConductedByAgency + ?.category, + id: selectedConductedByAgency + ?.id, + name: selectedConductedByAgency! + .name, + privateEntity: + conductedByCategoryIsPrivate); + conductedByAgencyCategoryFocusNode + .unfocus(); + }); + }, + name: + 'sponsorAgencyPrivate', + validator: + FormBuilderValidators + .required(), + ) + : const SizedBox()), + ]), + )), + ], + ); + }), + ]), + ) + : const SizedBox()), + const SizedBox( + height: 12, + ), + + ////Sponsor + StatefulBuilder(builder: (context, setState) { + ////has sponsor switch + return Column( + children: [ + FormBuilderSwitch( + initialValue: hasSponsor, + activeColor: second, + onChanged: (value) { + setState(() { + hasSponsor = value!; + }); + }, + decoration: + normalTextFieldStyle("Has Sponsor?", ''), + name: 'sponsor', + title: Text(hasSponsor ? "YES" : "NO"), + ), + ////Add Sponsor Agency============ + SizedBox( + child: hasSponsor + ? SizedBox( + child: Column(children: [ + const SizedBox( + height: 12, + ), + SearchField( + suggestionDirection: + SuggestionDirection.up, + itemHeight: 70, + focusNode: sponsorByFocusNode, + suggestions: state + .sponsorAgencies + .map((Agency agency) => + SearchFieldListItem( + agency.name!, + item: agency, + child: ListTile( + title: Text( + agency.name!, + overflow: + TextOverflow + .ellipsis, + ), + subtitle: Text(agency + .privateEntity == + true + ? "Private" + : agency.privateEntity == + false + ? "Government" + : ""), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle( + " Sponsor Agency *", + "") + .copyWith( + suffixIcon: + GestureDetector( + child: const Icon( + Icons.arrow_drop_down, + ), + onTap: () => + sponsorByFocusNode + .unfocus(), + )), + ////SELETECTED + onSuggestionTap: (agency) { + setState(() { + if (agency.item?.id != + null) { + selectedSponsorAgency = + Agency( + name: null, + id: agency + .item!.id); + } else { + selectedSponsorAgency = + Agency( + id: null, + name: agency + .item!.name); + } + if (agency.item! + .privateEntity == + null) { + showSponsorCategoryAgency = + true; + showSponsorAgencyPrivateRadio = + true; + } else { + showSponsorCategoryAgency = + false; + showSponsorAgencyPrivateRadio = + false; + } + sponsorByFocusNode + .unfocus(); + }); + }, + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + ////sponsor empty widget + emptyWidget: EmptyWidget( + controller: + addSponsorAgencyController, + onpressed: () { + setState(() { + Agency newAgency = Agency( + id: null, + name: addSponsorAgencyController + .text + .toUpperCase(), + category: null, + privateEntity: + null); + state.sponsorAgencies + .insert( + 0, newAgency); + + addSponsorAgencyController + .text = ""; + Navigator.pop(context); + }); + }, + title: + "Add Sponsor Agency")), + SizedBox( + height: showSponsorCategoryAgency + ? 12 + : 0, + ), + ////Sponsor Agency Category + SizedBox( + child: showSponsorCategoryAgency + ? SearchField( + suggestionDirection: + SuggestionDirection + .up, + focusNode: + sponsorAgencyCategoryFocusNode, + itemHeight: 70, + suggestions: state + .agencyCategory + .map((Category + category) => + SearchFieldListItem( + category + .name!, + item: + category, + child: + ListTile( + title: Text( + category + .name!), + subtitle: Text(category + .industryClass! + .name!), + ))) + .toList(), + emptyWidget: Container( + height: 100, + decoration: box1(), + child: const Center( + child: Text( + "No result found ...")), + ), + onSuggestionTap: + (agencyCategory) { + setState(() { + selectedSponsorAgencyCategory = + agencyCategory + .item; + selectedSponsorAgency = Agency( + id: selectedSponsorAgency + ?.id, + name: + selectedSponsorAgency! + .name, + category: + selectedSponsorAgencyCategory, + privateEntity: + sponsorAgencyIsPrivate); + sponsorAgencyCategoryFocusNode + .unfocus(); + }); + }, + searchInputDecoration: + normalTextFieldStyle( + "Category *", + "") + .copyWith( + suffixIcon: + const Icon( + Icons + .arrow_drop_down)), + validator: (value) { + if (value!.isEmpty) { + return "This field is required"; + } + return null; + }, + ) + : const SizedBox(), + ), + + ////Sponsor Agency Private Radio + SizedBox( + height: + showSponsorAgencyPrivateRadio + ? 12 + : 0), + SizedBox( + child: + showSponsorAgencyPrivateRadio + ? FormBuilderSwitch( + initialValue: + sponsorAgencyIsPrivate, + title: Text( + sponsorAgencyIsPrivate + ? "YES" + : "NO"), + decoration: normalTextFieldStyle( + "Private Entity?", + 'Private Entity?'), + + ////onvhange private sector + onChanged: (value) { + setState(() { + sponsorAgencyIsPrivate = + value!; + selectedSponsorAgency = Agency( + category: + selectedSponsorAgency + ?.category, + id: selectedSponsorAgency + ?.id, + name: selectedSponsorAgency! + .name, + privateEntity: + selectedSponsorAgency + ?.privateEntity); + sponsorAgencyCategoryFocusNode + .unfocus(); + }); + }, + + name: + 'sponsorAgencyPrivate', + validator: + FormBuilderValidators + .required(), + ) + : const SizedBox()), + ]), + ) + : const SizedBox(), + ), + ], + ); + }), + const SizedBox( + height: 12, + ), + const SizedBox( + height: 12, + ), + FormBuilderTextField( + validator: numericRequired, + name: "total_hours_attended", + keyboardType: TextInputType.number, + decoration: normalTextFieldStyle( + "Total Hours Attended *", + "Total Hours Attended *"), + ), + ], + ); + }), + const SizedBox( + height: 12, + ), + SizedBox( + height: 60, + child: ElevatedButton( + style: + mainBtnStyle(primary, Colors.transparent, second), + child: const Text(submit), + onPressed: () { + if (formKey.currentState!.saveAndValidate()) { + ConductedTraining? training; + Agency? sponsor; + Venue venue; + ////Address + if (overseas) { + venue = Venue( + id: null, + country: selectedCountry, + barangay: null, + category: null, + areaClass: null, + cityMunicipality: null); + } else { + venue = Venue( + id: null, + country: Country( + id: 175, name: 'Philippines', code: 'PH'), + barangay: selectedBarangay, + areaClass: null, + category: null, + cityMunicipality: selectedMunicipality); + } + if (showTrainingDetails) { + training = ConductedTraining( + locked: false, + id: selectedConductedTraining!.id, + venue: Venue( + country: selectedConductedTraining! + .venue!.country)); + } else { + training = ConductedTraining( + title: selectedTraining, + topic: selectedTopic, + id: null, + locked: false, + venue: venue, + toDate: DateTime.parse(toDateController.text), + fromDate: + DateTime.parse(fromDateController.text), + totalHours: double.parse(formKey + .currentState!.value['total_hours']), + conductedBy: selectedConductedByAgency, + sessionsAttended: [], + learningDevelopmentType: + selectedLearningDevelopmentType); + } + if (hasSponsor) { + sponsor = selectedSponsorAgency; + } + LearningDevelopement learningDevelopement = + LearningDevelopement( + attachments: null, + sponsoredBy: sponsor, + conductedTraining: training, + totalHoursAttended: double.parse(formKey + .currentState! + .value['total_hours_attended'])); + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add( + AddLearningAndDevelopment( + learningDevelopement: learningDevelopement, + profileId: widget.profileId, + token: widget.token)); + } + }, + ), + ) + ], + )), + ); + } + return const Center( + child: Text("ds"), + ); + }, + ); + } + + Future getProvinces() async { + try { + List newProvinces = await LocationUtils.instance + .getProvinces(regionCode: selectedRegion!.code.toString()); + setState(() { + provinces = newProvinces; + selectedProvince = provinces![0]; + provinceCall = false; + cityCall = true; + getCities(); + }); + } catch (e) { + context + .read() + .add(CallErrorState(message: e.toString())); + } + } + + Future getCities() async { + try { + List newCities = await LocationUtils.instance + .getCities(code: selectedProvince!.code.toString()); + citymuns = newCities; + setState(() { + selectedMunicipality = newCities[0]; + cityCall = false; + barangayCall = true; + getBarangays(); + }); + } catch (e) { + context + .read() + .add(CallErrorState(message: e.toString())); + } + } + + Future getBarangays() async { + try { + List newBarangays = await LocationUtils.instance + .getBarangay(code: selectedMunicipality!.code.toString()); + barangays = newBarangays; + setState(() { + selectedBarangay = newBarangays[0]; + barangayCall = false; + }); + } catch (e) { + context + .read() + .add(CallErrorState(message: e.toString())); + } + } +} diff --git a/lib/screens/profile/components/learning_development/display_details.dart b/lib/screens/profile/components/learning_development/display_details.dart new file mode 100644 index 0000000..b946a1d --- /dev/null +++ b/lib/screens/profile/components/learning_development/display_details.dart @@ -0,0 +1,150 @@ +import 'package:date_time_picker/date_time_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; + +import '../../../../theme-data.dart/form-style.dart'; +import '../../../../utils/global.dart'; + +class TrainingDetails extends StatelessWidget { + final String trainingTitle; + final double totalHours; + final String trainingTopic; + final String toDate; + final String fromDate; + final String conductedBy; + + const TrainingDetails({super.key, required this.trainingTitle, required this.totalHours, required this.trainingTopic, required this.toDate, required this.fromDate, required this.conductedBy}); + + @override + Widget build(BuildContext context) { + return SizedBox( + child: Column( + children: [ + const SizedBox( + height: 12, + ), + SizedBox( + child: Row( + children: [ + Flexible( + child: TextFormField( + enabled: false, + initialValue: trainingTitle, + decoration: + normalTextFieldStyle("", "") + .copyWith( + labelText: "Type", + fillColor: Colors + .grey.shade200, + filled: true)), + ), + const SizedBox( + width: 6, + ), + Flexible( + child: TextFormField( + enabled: false, + initialValue: totalHours + .toString(), + decoration: normalTextFieldStyle( + "", "") + .copyWith( + labelText: + "Total Hours Conducted", + fillColor: Colors + .grey.shade200, + filled: true))), + ], + ), + ), + const SizedBox( + height: 12, + ), + TextFormField( + enabled: false, + maxLines: 3, + initialValue: + trainingTopic, + decoration: normalTextFieldStyle("", "") + .copyWith( + labelText: "Topic", + fillColor: Colors.grey.shade200, + filled: true)), + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + //// FROM DATE + Flexible( + flex: 1, + child: DateTimePicker( + enabled: false, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + initialValue:fromDate, + use24HourFormat: false, + icon: const Icon( + Icons.date_range), + timeHintText: + "Date of Examination/Conferment", + decoration: + normalTextFieldStyle( + "From", "From") + .copyWith( + fillColor: Colors + .grey.shade200, + filled: true, + prefixIcon: + const Icon( + Icons.date_range, + color: Colors + .black87, + )), + )), + const SizedBox( + width: 6, + ), + //// TO DATE + Flexible( + flex: 1, + child: DateTimePicker( + enabled: false, + initialValue:toDate, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + decoration: normalTextFieldStyle( + "To", "To") + .copyWith( + fillColor: Colors.grey.shade200, + filled: true, + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + ), + ), + ), + ), + ], + ), + ), + const SizedBox( + height: 12, + ), + TextFormField( + enabled: false, + maxLines: 3, + initialValue:conductedBy, + decoration: normalTextFieldStyle("", "") + .copyWith( + labelText: "Conducted By", + fillColor: Colors.grey.shade200, + filled: true)), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/screens/profile/components/learning_development/edit_modal.dart b/lib/screens/profile/components/learning_development/edit_modal.dart new file mode 100644 index 0000000..58f5426 --- /dev/null +++ b/lib/screens/profile/components/learning_development/edit_modal.dart @@ -0,0 +1,1451 @@ +import 'package:date_time_picker/date_time_picker.dart'; +import 'package:flutter/material.dart'; + +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:intl/intl.dart'; +import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; +import 'package:searchable_paginated_dropdown/searchable_paginated_dropdown.dart'; +import 'package:searchfield/searchfield.dart'; +import 'package:unit2/bloc/profile/learningDevelopment/learning_development_bloc.dart'; +import 'package:unit2/model/profile/learning_development.dart'; +import 'package:unit2/screens/profile/components/learning_development/display_details.dart'; +import 'package:unit2/screens/profile/components/learning_development/training_details.dart'; +import 'package:unit2/sevices/profile/learningDevelopment_service.dart'; +import 'package:unit2/theme-data.dart/btn-style.dart'; +import 'package:unit2/utils/text_container.dart'; +import 'package:unit2/utils/validators.dart'; + +import '../../../../model/location/barangay.dart'; +import '../../../../model/location/city.dart'; +import '../../../../model/location/country.dart'; +import '../../../../model/location/provinces.dart'; +import '../../../../model/location/region.dart'; +import '../../../../model/utils/agency.dart'; +import '../../../../model/utils/category.dart'; +import '../../../../theme-data.dart/box_shadow.dart'; +import '../../../../theme-data.dart/colors.dart'; +import '../../../../theme-data.dart/form-style.dart'; +import '../../../../utils/global.dart'; +import '../../../../utils/location_utilities.dart'; +import '../../shared/add_for_empty_search.dart'; + +class EditLearningAndDevelopmentScreen extends StatefulWidget { + final int profileId; + final String token; + const EditLearningAndDevelopmentScreen( + {super.key, required this.profileId, required this.token}); + + @override + State createState() => + _EditLearningAndDevelopmentScreenState(); +} + +class _EditLearningAndDevelopmentScreenState + extends State { + final formKey = GlobalKey(); + + DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); + + bool isPrivate = false; + + bool showSponsorByAgency = false; + bool showOtherInputs = false; + bool showTrainingDetails = false; + bool hasSponsor = false; + + LearningDevelopmentType? selectedLearningDevelopmentType; + final fromDateController = TextEditingController(); + final toDateController = TextEditingController(); +////Training + bool show = true; + final addTrainingController = TextEditingController(); + ConductedTraining? selectedConductedTraining; + LearningDevelopmentType? selectedTraining; + +////Topic + final topicFocusNode = FocusNode(); + final addTopicController = TextEditingController(); + LearningDevelopmentType? selectedTopic; + final currentTopicController = TextEditingController(); + +////address + Barangay? selectedBarangay; + Country? selectedCountry; + Region? selectedRegion; + Province? selectedProvince; + CityMunicipality? selectedMunicipality; + bool overseas = false; + bool provinceCall = false; + bool cityCall = false; + bool barangayCall = false; + List? provinces; + List? citymuns; + List? barangays; + + ////Sponsor Agency Searchfield + Agency? selectedSponsorAgency; + Category? selectedSponsorAgencyCategory; + bool showSponsorCategoryAgency = false; + bool showSponsorAgencyPrivateRadio = false; + bool sponsorAgencyIsPrivate = false; + final addSponsorAgencyController = TextEditingController(); + final sponsorByFocusNode = FocusNode(); + final sponsorAgencyCategoryFocusNode = FocusNode(); + final selectedSponsorAgencyController = TextEditingController(); + +////Conducted By + Agency? selectedConductedByAgency; + Category? selectedConductedByAgencyCategory; + bool showConductedByAgencyPrivateRadio = false; + final conductedByFocusNode = FocusNode(); + final conductedByAgencyCategoryFocusNode = FocusNode(); + final addConductedByController = TextEditingController(); + bool showConductedByAgencyCategory = false; + bool conductedByCategoryIsPrivate = false; + final selectedConductedByController = TextEditingController(); + + final selectedTrainingController = TextEditingController(); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is LearningDevelopmentUpdatingState) { + selectedTrainingController.text = + state.learningDevelopement.conductedTraining!.title!.title!; + selectedTraining = LearningDevelopmentType( + id: state.learningDevelopement.conductedTraining!.title!.id, + title: selectedTrainingController.text); + selectedConductedByAgency = Agency( + id: state.learningDevelopement.conductedTraining!.conductedBy!.id, + name: null); + selectedConductedByController.text = + state.learningDevelopement.conductedTraining!.conductedBy!.name!; + selectedLearningDevelopmentType = state.types.firstWhere((element) => + element.id == + state.learningDevelopement.conductedTraining! + .learningDevelopmentType!.id); + currentTopicController.text = + state.learningDevelopement.conductedTraining!.topic!.title!; + selectedTopic = LearningDevelopmentType( + id: state.learningDevelopement.conductedTraining!.topic!.id, + title: null); + fromDateController.text = + state.learningDevelopement.conductedTraining!.fromDate.toString(); + toDateController.text = + state.learningDevelopement.conductedTraining!.toDate.toString(); + overseas = state.overseas; + if (!overseas) { + selectedRegion = state.currentRegion; + provinces = state.provinces; + selectedProvince = state.currentProvince; + citymuns = state.cities; + selectedMunicipality = state.currentCity; + barangays = state.barangay; + selectedBarangay = state.currentBarangay; + } else { + selectedCountry = state.currentCountry; + } + hasSponsor = state.learningDevelopement.sponsoredBy != null; + if (hasSponsor) { + selectedSponsorAgency = Agency( + id: state.learningDevelopement.sponsoredBy!.id, name: null); + selectedSponsorAgencyController.text = + state.learningDevelopement.sponsoredBy!.name!; + } + bool enabled; + if (state.learningDevelopement.conductedTraining?.locked != null && + state.learningDevelopement.conductedTraining!.locked == true) { + enabled = false; + } else { + enabled = true; + } + return Padding( + padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 24), + child: FormBuilder( + key: formKey, + child: ListView( + children: [ + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + const SizedBox( + height: 10, + ), + SizedBox( + ////Training selected Textfield + child: FormBuilderTextField( + onChanged: (value) { + selectedTraining = LearningDevelopmentType( + id: null, title: selectedTrainingController.text); + }, + enabled: enabled, + maxLines: 5, + controller: selectedTrainingController, + name: "", + decoration: normalTextFieldStyle("", "").copyWith( + labelText: "Training", + filled: !enabled, + fillColor: Colors.grey.shade300), + )), + SizedBox( + child: SizedBox( + child: Column(children: [ + const SizedBox( + height: 12, + ), + ////learning development type + FormBuilderDropdown( + enabled: enabled, + name: "types", + decoration: normalTextFieldStyle( + "Learning Development Type *", "") + .copyWith( + filled: !enabled, + fillColor: Colors.grey.shade300), + validator: FormBuilderValidators.required( + errorText: "This field is required"), + items: state.types + .map((e) => DropdownMenuItem< + LearningDevelopmentType>( + value: e, child: Text(e.title!))) + .toList(), + onChanged: (value) { + selectedLearningDevelopmentType = value; + }, + initialValue: selectedLearningDevelopmentType, + ), + + //// learning development topics + const SizedBox( + height: 12, + ), + StatefulBuilder(builder: (context, setState) { + return SearchField( + enabled: enabled, + controller: currentTopicController, + focusNode: topicFocusNode, + itemHeight: 100, + suggestionsDecoration: box1(), + suggestions: state.topics + .map((LearningDevelopmentType topic) => + SearchFieldListItem(topic.title!, + item: topic, + child: Padding( + padding: const EdgeInsets + .symmetric( + horizontal: 10), + child: ListTile( + title: Text( + topic.title!, + softWrap: true, + ), + )))) + .toList(), + + searchInputDecoration: + normalTextFieldStyle("Topic *", "") + .copyWith( + filled: !enabled, + fillColor: Colors.grey.shade300, + suffixIcon: const Icon( + Icons.arrow_drop_down)), + onSuggestionTap: (topic) { + if (topic.item?.id != null) { + selectedTopic = LearningDevelopmentType( + id: topic.item!.id, title: null); + } else { + selectedTopic = LearningDevelopmentType( + id: null, title: topic.item!.title); + } + setState(() { + topicFocusNode.unfocus(); + }); + }, + ////EMPTY WIDGET + emptyWidget: EmptyWidget( + title: "Add Topic", + controller: addTopicController, + onpressed: () { + setState(() { + LearningDevelopmentType newTopic = + LearningDevelopmentType( + id: null, + title: addTopicController.text + .toUpperCase()); + state.topics.insert(0, newTopic); + topicFocusNode.unfocus(); + addTopicController.text = ""; + Navigator.pop(context); + }); + }), + validator: (position) { + if (position!.isEmpty) { + return "This field is required"; + } + return null; + }, + ); + }), + + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + //// FROM DATE + Flexible( + flex: 1, + child: DateTimePicker( + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + use24HourFormat: false, + icon: const Icon(Icons.date_range), + controller: fromDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + timeHintText: + "Date of Examination/Conferment", + decoration: normalTextFieldStyle( + "From *", "From *") + .copyWith( + filled: !enabled, + fillColor: + Colors.grey.shade300, + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + initialValue: null, + )), + const SizedBox( + width: 12, + ), + //// TO DATE + Flexible( + flex: 1, + child: DateTimePicker( + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + controller: toDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + decoration: + normalTextFieldStyle("To *", "To *") + .copyWith( + filled: !enabled, + fillColor: Colors.grey.shade300, + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + ), + ), + initialValue: null, + ), + ), + ], + ), + ), + const SizedBox( + height: 12, + ), + //// total hours conducted + FormBuilderTextField( + initialValue: state.learningDevelopement + .conductedTraining!.totalHours + .toString(), + validator: numericRequired, + name: "total_hours", + decoration: normalTextFieldStyle( + "Total Hours Conducted *", + "0", + ).copyWith( + filled: !enabled, + fillColor: Colors.grey.shade300), + keyboardType: TextInputType.number, + ), + const SizedBox( + height: 12, + ), + ////Address + + const SizedBox( + height: 12, + ), + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: normalTextFieldStyle( + "Overseas Address?", ''), + name: 'overseas', + title: Text(overseas ? "YES" : "NO"), + ), + SizedBox( + height: overseas == true ? 8 : 0, + ), + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + FormBuilderDropdown( + enabled: !enabled + ? overseas + : true, + name: "region", + isExpanded: true, + initialValue: selectedRegion, + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + onChanged: + (Region? region) async { + if (selectedRegion != + region) { + setState(() { + provinceCall = true; + }); + selectedRegion = region; + //// GET PROVINCES + provinces = await LocationUtils + .instance + .getProvinces( + regionCode: + selectedRegion! + .code + .toString()); + selectedProvince = + provinces![0]; + setState(() { + provinceCall = false; + cityCall = true; + }); + //// GET CITIES + citymuns = await LocationUtils + .instance + .getCities( + code: + selectedProvince! + .code!); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + barangayCall = true; + }); + //// GET BARANGAY + barangays = await LocationUtils + .instance + .getBarangay( + code: + selectedMunicipality! + .code!); + selectedBarangay = + barangays![0]; + setState(() { + barangayCall = false; + }); + ////GET CITY MUNICIPALITY + citymuns = await LocationUtils + .instance + .getCities( + code: + selectedProvince! + .code!); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + barangayCall = true; + }); + //// GET BARANGAYS + barangays = await LocationUtils + .instance + .getBarangay( + code: + selectedMunicipality! + .code!); + selectedBarangay = + barangays![0]; + setState(() { + barangayCall = false; + }); + } + }, + decoration: + normalTextFieldStyle( + "Region*", + "Region") + .copyWith( + filled: !enabled + ? !overseas + : false, + fillColor: Colors + .grey + .shade300), + items: state.regions.map< + DropdownMenuItem< + Region>>( + (Region region) { + return DropdownMenuItem< + Region>( + value: region, + child: Text(region + .description!)); + }).toList(), + ), + const SizedBox( + height: 8, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: FormBuilderDropdown< + Province?>( + enabled: !enabled + ? overseas + : true, + name: "province", + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + initialValue: + selectedProvince, + onChanged: (Province? + province) async { + if (selectedProvince != + province) { + selectedProvince = + province; + setState(() { + cityCall = true; + }); + + //// GET CITIES + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code!); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + barangayCall = + true; + }); + //// GET BARANGAY + barangays = await LocationUtils + .instance + .getBarangay( + code: selectedMunicipality! + .code!); + selectedBarangay = + barangays![0]; + setState(() { + barangayCall = + false; + }); + } + }, + items: provinces == null + ? [] + : provinces! + .map>( + (Province + province) { + return DropdownMenuItem( + value: + province, + child: + FittedBox( + child: Text( + province + .description!), + )); + }).toList(), + decoration: normalTextFieldStyle( + "Province*", "Province") + .copyWith( + filled: !enabled + ? !overseas + : false, + fillColor: Colors + .grey + .shade300)), + ), + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: FormBuilderDropdown< + CityMunicipality>( + enabled: !enabled + ? overseas + : true, + name: "city", + validator: + FormBuilderValidators + .required( + errorText: + "This field is required"), + isExpanded: true, + onChanged: + (CityMunicipality? + city) async { + if (selectedMunicipality != + city) { + setState(() { + barangayCall = true; + }); + selectedMunicipality = + city; + selectedMunicipality = + city; + //// GET BARANGAYS + barangays = + await LocationUtils + .instance + .getBarangay( + code: selectedMunicipality! + .code!); + selectedBarangay = + barangays![0]; + setState(() { + barangayCall = + false; + }); + } + }, + decoration: normalTextFieldStyle( + "Municipality*", + "Municipality") + .copyWith( + filled: !enabled + ? !overseas + : false, + fillColor: Colors + .grey + .shade300), + initialValue: + selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality + c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), + ), + ), + ), + //// BARANGAY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: barangayCall, + child: FormBuilderDropdown< + Barangay>( + enabled: !enabled + ? overseas + : true, + name: "barangay", + isExpanded: true, + onChanged: + (Barangay? baragay) { + selectedBarangay = + baragay; + }, + decoration: normalTextFieldStyle( + "Barangay*", + "Barangay") + .copyWith( + filled: !enabled + ? !overseas + : false, + fillColor: Colors + .grey + .shade300), + initialValue: + selectedBarangay, + items: barangays == null + ? [] + : barangays!.map< + DropdownMenuItem< + Barangay>>( + (Barangay + barangay) { + return DropdownMenuItem( + value: + barangay, + child: Text( + barangay + .description!)); + }).toList(), + ), + ), + ), + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( + height: 60, + child: + FormBuilderDropdown( + enabled: overseas, + initialValue: selectedCountry, + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + items: state.countries.map< + DropdownMenuItem< + Country>>( + (Country country) { + return DropdownMenuItem< + Country>( + value: country, + child: FittedBox( + child: Text( + country.name!))); + }).toList(), + name: 'country', + decoration: + normalTextFieldStyle( + "Country*", + "Country") + .copyWith( + filled: !overseas, + fillColor: Colors + .grey.shade300), + onChanged: (Country? value) { + selectedCountry = value; + }, + ), + ), + ), + ], + ); + }), + + ////Conducted By + StatefulBuilder(builder: (context, setState) { + ////has sponsor switch + return Column( + children: [ + ////Add Conducted Agency============ + SizedBox( + child: SizedBox( + child: Column(children: [ + SearchField( + enabled: enabled, + controller: + selectedConductedByController, + suggestionDirection: + SuggestionDirection.up, + itemHeight: 70, + focusNode: conductedByFocusNode, + suggestions: state.conductedBy + .map((Agency agency) => + SearchFieldListItem( + agency.name!, + item: agency, + child: ListTile( + title: Text( + agency.name!, + overflow: + TextOverflow + .ellipsis, + ), + subtitle: Text(agency + .privateEntity == + true + ? "Private" + : agency.privateEntity == + false + ? "Government" + : ""), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle( + " Conducted By *", "") + .copyWith( + suffixIcon: + GestureDetector( + child: const Icon( + Icons + .arrow_drop_down, + ), + onTap: () => + conductedByFocusNode + .unfocus(), + ), + filled: !enabled, + fillColor: Colors + .grey.shade300), + ////SELETECTED + onSuggestionTap: (agency) { + setState(() { + if (agency.item?.id != null) { + selectedConductedByAgency = + Agency( + name: null, + id: agency.item!.id); + } else { + selectedConductedByAgency = + Agency( + id: null, + name: agency + .item!.name); + } + + if (agency + .item!.privateEntity == + null) { + showConductedByAgencyCategory = + true; + showConductedByAgencyPrivateRadio = + true; + } else { + showConductedByAgencyCategory = + false; + showConductedByAgencyPrivateRadio = + false; + } + conductedByFocusNode.unfocus(); + }); + }, + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + ////conducter empty widget + emptyWidget: EmptyWidget( + controller: + addConductedByController, + onpressed: () { + setState(() { + Agency newAgency = Agency( + id: null, + name: + addConductedByController + .text + .toUpperCase(), + category: null, + privateEntity: null); + state.conductedBy + .insert(0, newAgency); + + addConductedByController + .text = ""; + Navigator.pop(context); + }); + }, + title: + "Add Conducted By Agency")), + SizedBox( + height: showConductedByAgencyCategory + ? 12 + : 0, + ), + ////Sponsor Agency Category + SizedBox( + child: showConductedByAgencyCategory + ? SearchField( + suggestionDirection: + SuggestionDirection.up, + focusNode: + conductedByAgencyCategoryFocusNode, + itemHeight: 70, + suggestions: state + .agencyCategory + .map((Category + category) => + SearchFieldListItem( + category.name!, + item: category, + child: ListTile( + title: Text( + category + .name!), + subtitle: Text( + category + .industryClass! + .name!), + ))) + .toList(), + emptyWidget: Container( + height: 100, + decoration: box1(), + child: const Center( + child: Text( + "No result found ...")), + ), + onSuggestionTap: + (agencyCategory) { + setState(() { + selectedConductedByAgencyCategory = + agencyCategory.item; + selectedConductedByAgency = Agency( + id: selectedConductedByAgency + ?.id, + name: + selectedConductedByAgency! + .name, + category: + selectedConductedByAgencyCategory, + privateEntity: + showConductedByAgencyPrivateRadio); + conductedByAgencyCategoryFocusNode + .unfocus(); + }); + }, + searchInputDecoration: + normalTextFieldStyle( + "Category *", "") + .copyWith( + suffixIcon: + const Icon(Icons + .arrow_drop_down)), + validator: (value) { + if (value!.isEmpty) { + return "This field is required"; + } + return null; + }, + ) + : const SizedBox(), + ), + + ////Sponsor Agency Private Radio + SizedBox( + height: + showConductedByAgencyPrivateRadio + ? 12 + : 0), + SizedBox( + child: + showConductedByAgencyPrivateRadio + ? FormBuilderSwitch( + initialValue: + conductedByCategoryIsPrivate, + title: Text( + conductedByCategoryIsPrivate + ? "YES" + : "NO"), + decoration: + normalTextFieldStyle( + "Private Entity?", + 'Private Entity?'), + + ////onvhange private sector + onChanged: (value) { + setState(() { + conductedByCategoryIsPrivate = + value!; + selectedConductedByAgency = Agency( + category: + selectedConductedByAgency + ?.category, + id: selectedConductedByAgency + ?.id, + name: + selectedConductedByAgency! + .name, + privateEntity: + conductedByCategoryIsPrivate); + conductedByAgencyCategoryFocusNode + .unfocus(); + }); + }, + name: + 'sponsorAgencyPrivate', + validator: + FormBuilderValidators + .required(), + ) + : const SizedBox()), + ]), + )), + ], + ); + }), + ]), + )), + const SizedBox( + height: 12, + ), + + ////Sponsor + StatefulBuilder(builder: (context, setState) { + ////has sponsor switch + return Column( + children: [ + FormBuilderSwitch( + initialValue: hasSponsor, + activeColor: second, + onChanged: (value) { + setState(() { + hasSponsor = value!; + }); + }, + decoration: + normalTextFieldStyle("Has Sponsor?", ''), + name: 'sponsor', + title: Text(hasSponsor ? "YES" : "NO"), + ), + ////Add Sponsor Agency============ + SizedBox( + child: hasSponsor + ? SizedBox( + child: Column(children: [ + const SizedBox( + height: 12, + ), + SearchField( + controller: + selectedSponsorAgencyController, + suggestionDirection: + SuggestionDirection.up, + itemHeight: 70, + focusNode: sponsorByFocusNode, + suggestions: state + .sponsorAgencies + .map((Agency agency) => + SearchFieldListItem( + agency.name!, + item: agency, + child: ListTile( + title: Text( + agency.name!, + overflow: + TextOverflow + .ellipsis, + ), + subtitle: Text(agency + .privateEntity == + true + ? "Private" + : agency.privateEntity == + false + ? "Government" + : ""), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle( + " Sponsor Agency *", + "") + .copyWith( + suffixIcon: + GestureDetector( + child: const Icon( + Icons.arrow_drop_down, + ), + onTap: () => + sponsorByFocusNode + .unfocus(), + )), + ////SELETECTED + onSuggestionTap: (agency) { + setState(() { + if (agency.item?.id != + null) { + selectedSponsorAgency = + Agency( + name: null, + id: agency + .item!.id); + } else { + selectedSponsorAgency = + Agency( + id: null, + name: agency + .item!.name); + } + if (agency.item! + .privateEntity == + null) { + showSponsorCategoryAgency = + true; + showSponsorAgencyPrivateRadio = + true; + } else { + showSponsorCategoryAgency = + false; + showSponsorAgencyPrivateRadio = + false; + } + sponsorByFocusNode + .unfocus(); + }); + }, + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + ////sponsor empty widget + emptyWidget: EmptyWidget( + controller: + addSponsorAgencyController, + onpressed: () { + setState(() { + Agency newAgency = Agency( + id: null, + name: addSponsorAgencyController + .text + .toUpperCase(), + category: null, + privateEntity: + null); + state.sponsorAgencies + .insert( + 0, newAgency); + + addSponsorAgencyController + .text = ""; + Navigator.pop(context); + }); + }, + title: + "Add Sponsor Agency")), + SizedBox( + height: showSponsorCategoryAgency + ? 12 + : 0, + ), + ////Sponsor Agency Category + SizedBox( + child: showSponsorCategoryAgency + ? SearchField( + suggestionDirection: + SuggestionDirection + .up, + focusNode: + sponsorAgencyCategoryFocusNode, + itemHeight: 70, + suggestions: state + .agencyCategory + .map((Category + category) => + SearchFieldListItem( + category + .name!, + item: + category, + child: + ListTile( + title: Text( + category + .name!), + subtitle: Text(category + .industryClass! + .name!), + ))) + .toList(), + emptyWidget: Container( + height: 100, + decoration: box1(), + child: const Center( + child: Text( + "No result found ...")), + ), + onSuggestionTap: + (agencyCategory) { + setState(() { + selectedSponsorAgencyCategory = + agencyCategory + .item; + selectedSponsorAgency = Agency( + id: selectedSponsorAgency + ?.id, + name: + selectedSponsorAgency! + .name, + category: + selectedSponsorAgencyCategory, + privateEntity: + sponsorAgencyIsPrivate); + sponsorAgencyCategoryFocusNode + .unfocus(); + }); + }, + searchInputDecoration: + normalTextFieldStyle( + "Category *", + "") + .copyWith( + suffixIcon: + const Icon( + Icons + .arrow_drop_down)), + validator: (value) { + if (value!.isEmpty) { + return "This field is required"; + } + return null; + }, + ) + : const SizedBox(), + ), + + ////Sponsor Agency Private Radio + SizedBox( + height: + showSponsorAgencyPrivateRadio + ? 12 + : 0), + SizedBox( + child: + showSponsorAgencyPrivateRadio + ? FormBuilderSwitch( + initialValue: + sponsorAgencyIsPrivate, + title: Text( + sponsorAgencyIsPrivate + ? "YES" + : "NO"), + decoration: normalTextFieldStyle( + "Private Entity?", + 'Private Entity?'), + + ////onvhange private sector + onChanged: (value) { + setState(() { + sponsorAgencyIsPrivate = + value!; + selectedSponsorAgency = Agency( + category: + selectedSponsorAgency + ?.category, + id: selectedSponsorAgency + ?.id, + name: selectedSponsorAgency! + .name, + privateEntity: + selectedSponsorAgency + ?.privateEntity); + sponsorAgencyCategoryFocusNode + .unfocus(); + }); + }, + + name: + 'sponsorAgencyPrivate', + validator: + FormBuilderValidators + .required(), + ) + : const SizedBox()), + ]), + ) + : const SizedBox(), + ), + ], + ); + }), + const SizedBox( + height: 12, + ), + const SizedBox( + height: 12, + ), + FormBuilderTextField( + initialValue: state + .learningDevelopement.totalHoursAttended + .toString(), + validator: numericRequired, + name: "total_hours_attended", + keyboardType: TextInputType.number, + decoration: normalTextFieldStyle( + "Total Hours Attended *", + "Total Hours Attended *"), + ), + ], + ); + }), + const SizedBox( + height: 12, + ), + SizedBox( + height: 60, + child: ElevatedButton( + style: + mainBtnStyle(primary, Colors.transparent, second), + child: const Text(submit), + onPressed: () { + if (formKey.currentState!.saveAndValidate()) { + ConductedTraining? training; + Agency? sponsor; + Venue venue; + ////Address + if (overseas) { + venue = Venue( + id: state.learningDevelopement + .conductedTraining!.venue!.id, + country: selectedCountry, + barangay: null, + category: null, + areaClass: null, + cityMunicipality: null); + } else { + venue = Venue( + id: state.learningDevelopement + .conductedTraining!.venue!.id, + country: Country( + id: 175, name: 'Philippines', code: 'PH'), + barangay: selectedBarangay, + areaClass: null, + category: null, + cityMunicipality: selectedMunicipality); + } + + training = ConductedTraining( + title: selectedTraining, + topic: selectedTopic, + id: state + .learningDevelopement.conductedTraining!.id, + locked: state.learningDevelopement + .conductedTraining?.locked, + venue: venue, + toDate: DateTime.parse(toDateController.text), + fromDate: + DateTime.parse(fromDateController.text), + totalHours: double.parse( + formKey.currentState!.value['total_hours']), + conductedBy: selectedConductedByAgency, + sessionsAttended: [], + learningDevelopmentType: + selectedLearningDevelopmentType); + + if (hasSponsor) { + sponsor = selectedSponsorAgency; + } + LearningDevelopement learningDevelopement = + LearningDevelopement( + attachments: null, + sponsoredBy: sponsor, + conductedTraining: training, + totalHoursAttended: double.parse(formKey + .currentState! + .value['total_hours_attended'])); + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add( + UpdateLearningDevelopment( + learningDevelopement: learningDevelopement, + profileId: widget.profileId, + token: widget.token)); + } + }, + ), + ) + ], + )), + ); + } + return const Center( + child: Text("ds"), + ); + }, + ); + } + + Future getProvinces() async { + try { + List newProvinces = await LocationUtils.instance + .getProvinces(regionCode: selectedRegion!.code.toString()); + setState(() { + provinces = newProvinces; + selectedProvince = provinces![0]; + provinceCall = false; + cityCall = true; + getCities(); + }); + } catch (e) { + context + .read() + .add(CallErrorState(message: e.toString())); + } + } + + Future getCities() async { + try { + List newCities = await LocationUtils.instance + .getCities(code: selectedProvince!.code.toString()); + citymuns = newCities; + setState(() { + selectedMunicipality = newCities[0]; + cityCall = false; + barangayCall = true; + getBarangays(); + }); + } catch (e) { + context + .read() + .add(CallErrorState(message: e.toString())); + } + } + + Future getBarangays() async { + try { + List newBarangays = await LocationUtils.instance + .getBarangay(code: selectedMunicipality!.code.toString()); + barangays = newBarangays; + setState(() { + selectedBarangay = newBarangays[0]; + barangayCall = false; + }); + } catch (e) { + context + .read() + .add(CallErrorState(message: e.toString())); + } + } +} diff --git a/lib/screens/profile/components/learning_development/search_field_widget.dart b/lib/screens/profile/components/learning_development/search_field_widget.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/screens/profile/components/learning_development/training_details.dart b/lib/screens/profile/components/learning_development/training_details.dart new file mode 100644 index 0000000..4b216ef --- /dev/null +++ b/lib/screens/profile/components/learning_development/training_details.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:intl/intl.dart'; + +import '../../../../model/profile/learning_development.dart'; + +class TrainingDisplayDetails extends StatelessWidget { + final ConductedTraining e; + final Function() notWhatYourLookingFor; + const TrainingDisplayDetails({super.key, required this.e, required this.notWhatYourLookingFor}); + + @override + Widget build(BuildContext context) { + DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); + return Card( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + e.title!.title!.toUpperCase(), + style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold), + ), + const SizedBox( + height: 6, + ), + Text( + "CONDUCTED BY:", + style: + Theme.of(context).textTheme.bodySmall!.copyWith(fontSize: 8), + ), + Text(e.conductedBy!.name!, style: const TextStyle(fontSize: 12)), + const SizedBox( + height: 6, + ), + Row( + children: [ + Flexible( + child: Text( + dteFormat2.format(e.fromDate!), + style: Theme.of(context).textTheme.labelSmall, + )), + const Text(" - "), + Flexible( + child: Text( + dteFormat2.format(e.toDate!), + style: Theme.of(context).textTheme.labelSmall, + )) + ], + ), + Text("Total (hours): ${e.totalHours}", + style: Theme.of(context).textTheme.labelSmall), + Align( + alignment: Alignment.centerRight, + child: GestureDetector( + onTap: notWhatYourLookingFor, + child: const Padding( + padding: EdgeInsets.all(8.0), + child: Text( + "NOT WHAT LOOKING FOR?", + style: TextStyle(fontSize: 10, color: Colors.black), + ), + ), + )), + ], + ), + ), + ); + } +} diff --git a/lib/screens/profile/components/loading_screen.dart b/lib/screens/profile/components/loading_screen.dart index 0d469b9..2747101 100644 --- a/lib/screens/profile/components/loading_screen.dart +++ b/lib/screens/profile/components/loading_screen.dart @@ -135,8 +135,8 @@ class LoadingScreen extends StatelessWidget { ), Center( child: Container( - height: 80, - width: 80, + height: 120, + width: 120, decoration:const BoxDecoration( color: Colors.black87, borderRadius: BorderRadius.all(Radius.circular(8)) @@ -145,9 +145,13 @@ class LoadingScreen extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: const[ + SpinKitFadingCircle( size: 42, color: Colors.white), + + SizedBox(height: 12,), + Text("Please wait..",style: TextStyle(color: Colors.white),), ], ), diff --git a/lib/screens/profile/components/voluntary_works/add_modal.dart b/lib/screens/profile/components/voluntary_works/add_modal.dart index 58fedba..a2b9c7e 100644 --- a/lib/screens/profile/components/voluntary_works/add_modal.dart +++ b/lib/screens/profile/components/voluntary_works/add_modal.dart @@ -124,7 +124,7 @@ class _AddVoluntaryWorkScreenState extends State { state.positions.insert(0, newAgencyPosition); addPositionController.text = ""; - Navigator.pop(context); + }); }), validator: (position) { diff --git a/lib/screens/profile/profile.dart b/lib/screens/profile/profile.dart index da19d56..9788394 100644 --- a/lib/screens/profile/profile.dart +++ b/lib/screens/profile/profile.dart @@ -12,6 +12,7 @@ import 'package:unit2/bloc/profile/primary_information/address/address_bloc.dart import 'package:unit2/bloc/profile/primary_information/contact/contact_bloc.dart'; import 'package:unit2/bloc/profile/primary_information/identification/identification_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/model/login_data/user_info/user_data.dart'; import 'package:unit2/screens/profile/components/basic_information/address_screen.dart'; import 'package:unit2/screens/profile/components/basic_information/citizenship_screen.dart'; import 'package:unit2/screens/profile/components/basic_information/contact_information_screen.dart'; @@ -29,6 +30,7 @@ import 'package:unit2/screens/profile/components/references_screen.dart'; import 'package:unit2/screens/profile/components/work_history_screen.dart'; import 'package:unit2/screens/profile/components/voluntary_works_screen.dart'; import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/widgets/error_state.dart'; import '../../bloc/profile/eligibility/eligibility_bloc.dart'; import '../../bloc/profile/family/family_bloc.dart'; import '../../bloc/profile/education/education_bloc.dart'; @@ -40,6 +42,8 @@ import '../../bloc/profile/references/references_bloc.dart'; import '../../bloc/profile/voluntary_works/voluntary_work_bloc.dart'; import '../../bloc/profile/workHistory/workHistory_bloc.dart'; import '../../bloc/user/user_bloc.dart'; +import '../../model/profile/basic_information/primary-information.dart'; +import '../unit2/homepage.dart/components/menu.dart'; import 'components/main_menu.dart'; import 'components/submenu.dart'; @@ -49,8 +53,8 @@ class ProfileInfo extends StatelessWidget { @override Widget build(BuildContext context) { int? profileId; - String? token; + Profile profile; return Scaffold( appBar: AppBar( backgroundColor: primary, @@ -65,13 +69,15 @@ class ProfileInfo extends StatelessWidget { if (state is UserLoggedIn) { profileId = state.userData!.user!.login!.user!.profileId; token = state.userData!.user!.login!.token!; + profile = state.userData!.employeeInfo!.profile!; + return BlocConsumer( listener: (context, state,){ if (state is ProfileLoading) { final progress = ProgressHUD.of(context); progress!.showWithText("Please wait..."); } - if (state is ProfileLoaded || state is ProfileErrorState) { + if (state is ProfileLoaded || state is ProfileErrorState || state is BasicInformationEditingState) { final progress = ProgressHUD.of(context); progress?.dismiss(); } @@ -104,13 +110,10 @@ class ProfileInfo extends StatelessWidget { ), items: [ subMenu(Icons.person, "Primary", () { + Navigator.push(context, MaterialPageRoute( builder: (BuildContext context) { - return PrimaryInfo( - primaryInformation: state - .profileInformation - .basicInfo - .primaryInformation!); + return BlocProvider.value(value: ProfileBloc()..add(GetPrimaryBasicInfo(primaryBasicInformation: profile)),child: PrimaryInfo(token: token!,profileId: profileId!,),); })); }), subMenu(Icons.home, "Home Addresses", () { @@ -363,7 +366,9 @@ class ProfileInfo extends StatelessWidget { return const LoadingScreen(); } if (state is ProfileErrorState) { - return Text(state.mesage); + return SomethingWentWrong(message: state.mesage, onpressed: (){ BlocProvider.of( + context) + .add(LoadProfile(token:token!, userID: profileId!));}); } return Container(); diff --git a/lib/screens/unit2/homepage.dart/components/menu.dart b/lib/screens/unit2/homepage.dart/components/menu.dart index 10103f7..d001e80 100644 --- a/lib/screens/unit2/homepage.dart/components/menu.dart +++ b/lib/screens/unit2/homepage.dart/components/menu.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:unit2/model/login_data/employee_info/employee_info.dart'; import 'package:unit2/model/login_data/user_info/user_data.dart'; import 'package:unit2/utils/alerts.dart'; +import '../../../../model/profile/basic_information/primary-information.dart'; import '../../../../theme-data.dart/colors.dart'; import '../../../../utils/global.dart'; diff --git a/lib/sevices/profile/learningDevelopment_service.dart b/lib/sevices/profile/learningDevelopment_service.dart index 9cf8154..c8775d3 100644 --- a/lib/sevices/profile/learningDevelopment_service.dart +++ b/lib/sevices/profile/learningDevelopment_service.dart @@ -1,6 +1,8 @@ import 'dart:convert'; +import 'package:flutter/material.dart'; import 'package:unit2/utils/request.dart'; +import 'package:unit2/utils/text_container.dart'; import '../../model/profile/learning_development.dart'; import '../../utils/urls.dart'; @@ -15,27 +17,265 @@ class LearningDevelopmentServices { int profileId, String token) async { List learningsAndDevelopments = []; String authToken = "Token $token"; - String path = "${Url.instance.getLearningAndDevelopments()}$profileId/"; + String path = "${Url.instance.learningAndDevelopments()}$profileId/"; Map headers = { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': authToken }; - // try { + try { + http.Response response = await Request.instance + .getRequest(path: path, param: {}, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((var learnings) { + LearningDevelopement learningDevelopement = + LearningDevelopement.fromJson(learnings); + learningsAndDevelopments.add(learningDevelopement); + }); + } + } + } catch (e) { + throw e.toString(); + } + return learningsAndDevelopments; + } + + ////Add + Future> add( + {required LearningDevelopement learningDevelopement, + required String token, + required int profileId}) async { + String authtoken = "Token $token"; + String path = '${Url.instance.learningAndDevelopments()}$profileId/'; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authtoken + }; + Map statusResponse = {}; + try { + Map body = { + "training_conduct_id": learningDevelopement.conductedTraining?.id, + "total_hours_attended": + learningDevelopement.totalHoursAttended.toString(), + "sponsor_id": learningDevelopement.sponsoredBy?.id, + "sponsor_name": learningDevelopement.sponsoredBy?.name, + "sponsor_category": learningDevelopement.sponsoredBy?.category?.id, + "sponsor_private": learningDevelopement.sponsoredBy?.privateEntity, + "training_id": learningDevelopement.conductedTraining?.title?.id, + "training_title": learningDevelopement.conductedTraining?.title?.title, + "topic_id": learningDevelopement.conductedTraining?.topic?.id, + "topic_title": learningDevelopement.conductedTraining?.topic?.title, + "conductor_id": learningDevelopement.conductedTraining?.conductedBy?.id, + "conductor_name": + learningDevelopement.conductedTraining?.conductedBy?.name, + "conductor_category": + learningDevelopement.conductedTraining?.conductedBy?.category?.id!, + "conductor_private": + learningDevelopement.conductedTraining?.conductedBy?.privateEntity, + "venue_city_municipality": learningDevelopement + .conductedTraining?.venue?.cityMunicipality?.code, + "venue_barangay": + learningDevelopement.conductedTraining?.venue?.barangay?.code, + "learning_development_type": + learningDevelopement.conductedTraining?.learningDevelopmentType?.id, + "from_date": + learningDevelopement.conductedTraining?.fromDate?.toString(), + "to_date": learningDevelopement.conductedTraining?.toDate?.toString(), + "total_hours": learningDevelopement.conductedTraining?.totalHours, + "locked": false, + "venue_country": + learningDevelopement.conductedTraining!.venue!.country!.id + }; + http.Response response = await Request.instance + .postRequest(path: path, param: {}, body: body, headers: headers); + if (response.statusCode == 201) { + Map data = jsonDecode(response.body); + statusResponse = data; + } else { + statusResponse.addAll({'success': false}); + } + return statusResponse; + } catch (e) { + throw e.toString(); + } + } + + ////Add + Future> update( + {required LearningDevelopement learningDevelopement, + required String token, + required int profileId}) async { + String authtoken = "Token $token"; + String path = '${Url.instance.learningAndDevelopments()}$profileId/'; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authtoken + }; + Map statusResponse = {}; + try { + Map body = { + "training_conduct_id": learningDevelopement.conductedTraining?.id, + "total_hours_attended": + learningDevelopement.totalHoursAttended.toString(), + "sponsor_id": learningDevelopement.sponsoredBy?.id, + "sponsor_name": learningDevelopement.sponsoredBy?.name, + "sponsor_category": learningDevelopement.sponsoredBy?.category?.id, + "sponsor_private": learningDevelopement.sponsoredBy?.privateEntity, + "training_id": learningDevelopement.conductedTraining?.title?.id, + "training_title": learningDevelopement.conductedTraining?.title?.title, + "topic_id": learningDevelopement.conductedTraining?.topic?.id, + "topic_title": learningDevelopement.conductedTraining?.topic?.title, + "conductor_id": learningDevelopement.conductedTraining?.conductedBy?.id, + "conductor_name": + learningDevelopement.conductedTraining?.conductedBy?.name, + "conductor_category": + learningDevelopement.conductedTraining?.conductedBy?.category?.id!, + "conductor_private": + learningDevelopement.conductedTraining?.conductedBy?.privateEntity, + "venue_city_municipality": learningDevelopement + .conductedTraining?.venue?.cityMunicipality?.code, + "venue_barangay": + learningDevelopement.conductedTraining?.venue?.barangay?.code, + "learning_development_type": + learningDevelopement.conductedTraining?.learningDevelopmentType?.id, + "from_date": + learningDevelopement.conductedTraining?.fromDate?.toString(), + "to_date": learningDevelopement.conductedTraining?.toDate?.toString(), + "total_hours": learningDevelopement.conductedTraining?.totalHours, + "locked": learningDevelopement.conductedTraining?.locked, + "venue_country": + learningDevelopement.conductedTraining!.venue!.country!.id + }; + http.Response response = await Request.instance + .putRequest(path: path, param: {}, body: body, headers: headers); + if (response.statusCode == 201) { + Map data = jsonDecode(response.body); + statusResponse = data; + } else { + statusResponse.addAll({'success': false}); + } + return statusResponse; + } catch (e) { + throw e.toString(); + } + } + + ////Delete + Future delete( + {required int profileId, + required String token, + required int? sponsorId, + required double totalHours, + required int trainingId}) async { + bool? success; + Map params = {"force_mode": "true"}; + String authToken = "Token $token"; + String path = '${Url.instance.learningAndDevelopments()}$profileId/'; + Map body = { + "sponsor_id": sponsorId, + "total_hours_attended": totalHours, + "training_conduct_id": trainingId + }; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + try { + http.Response response = await Request.instance.deleteRequest( + path: path, headers: headers, body: body, param: params); + + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + success = data['success']; + } else { + success = false; + } + } catch (e) { + throw e.toString(); + } + return success!; + } + + Future> getConductedTrainings( + {required String key, required int page}) async { + List trainings = []; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + Map params = { + "title__title___ilike": key, + "page": page.toString() + }; + + String path = Url.instance.conductedTrainings(); + try { + http.Response response = await Request.instance + .getRequest(path: path, param: params, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((var element) { + ConductedTraining training = ConductedTraining.fromJson(element); + trainings.add(training); + }); + } + } + } catch (e) { + throw e.toString(); + } + return trainings; + } + + Future> getLearningDevelopmentType() async { + List types = []; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + + String path = Url.instance.learningAndDevelopmentType(); + try { http.Response response = await Request.instance .getRequest(path: path, param: {}, headers: headers); if (response.statusCode == 200) { Map data = jsonDecode(response.body); if (data['data'] != null) { - data['data'].forEach((var learnings) { - LearningDevelopement learningDevelopement = - LearningDevelopement.fromJson(learnings); - learningsAndDevelopments.add(learningDevelopement); + data['data'].forEach((var element) { + LearningDevelopmentType type = + LearningDevelopmentType.fromJson(element); + types.add(type); }); } } - // } catch (e) { - // throw e.toString(); - // } - return learningsAndDevelopments; + } catch (e) { + throw e.toString(); + } + return types; + } + + Future> getTrainingTopics() async { + List topics = []; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + + String path = Url.instance.learningAndDevelopmentTopics(); + try { + http.Response response = await Request.instance + .getRequest(path: path, param: {}, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((var element) { + LearningDevelopmentType type = + LearningDevelopmentType.fromJson(element); + topics.add(type); + }); + } + } + } catch (e) { + throw e.toString(); + } + return topics; } } diff --git a/lib/sevices/profile/profile_other_info.dart b/lib/sevices/profile/profile_other_info.dart new file mode 100644 index 0000000..8945835 --- /dev/null +++ b/lib/sevices/profile/profile_other_info.dart @@ -0,0 +1,137 @@ +import 'dart:convert'; + +import 'package:unit2/screens/profile/components/basic_information/profile_other_info.dart'; +import 'package:unit2/utils/urls.dart'; +import 'package:http/http.dart' as http; + +import '../../utils/request.dart'; +class ProfileOtherInfoServices{ + static final ProfileOtherInfoServices _instance = ProfileOtherInfoServices(); + + static ProfileOtherInfoServices get instace => _instance; + + Future>getReligions({String? token})async{ + String path = Url.instance.getReligions(); + List religions = []; + String authToken = "Token $token"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + try { + http.Response response = await Request.instance + .getRequest(path: path, headers: headers, param: {}); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((var e) { + religions.add(ProfileOtherInfo.fromJson(e)); + }); + } + } + } catch (e) { + throw e.toString(); + } + return religions; + } + Future>getEthnicity({String? token})async{ + String path = Url.instance.getEthnicity(); + List ethnicity = []; + String authToken = "Token $token"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + try { + http.Response response = await Request.instance + .getRequest(path: path, headers: headers, param: {}); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((var e) { + ethnicity.add(ProfileOtherInfo.fromJson(e)); + }); + } + } + } catch (e) { + throw e.toString(); + } + return ethnicity; + } + + Future>getDisability({required token})async{ + String path = Url.instance.getDisability(); + List disabilities = []; + String authToken = "Token $token"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + try { + http.Response response = await Request.instance + .getRequest(path: path, headers: headers, param: {}); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((var e) { + disabilities.add(ProfileOtherInfo.fromJson(e)); + }); + } + } + } catch (e) { + throw e.toString(); + } + return disabilities; + } + Future>getGenders({required token})async{ + String path = Url.instance.getGenders(); + List genders = []; + String authToken = "Token $token"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + try { + http.Response response = await Request.instance + .getRequest(path: path, headers: headers, param: {}); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((var e) { + genders.add(ProfileOtherInfo.fromJson(e)); + }); + } + } + } catch (e) { + throw e.toString(); + } + return genders; + } + + Future>getIndigency({required String? token})async{ + String path = Url.instance.getIndigency(); + List indigencies = []; + String authToken = "Token $token"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + try { + http.Response response = await Request.instance + .getRequest(path: path, headers: headers, param: {}); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((var e) { + indigencies.add(ProfileOtherInfo.fromJson(e)); + }); + } + } + } catch (e) { + throw e.toString(); + } + return indigencies; + } + + +} \ No newline at end of file diff --git a/lib/sevices/profile/profile_service.dart b/lib/sevices/profile/profile_service.dart index d085a98..7ff4b12 100644 --- a/lib/sevices/profile/profile_service.dart +++ b/lib/sevices/profile/profile_service.dart @@ -30,7 +30,7 @@ class ProfileService { String url = Url.instance.profileInformation(); String path = url + id.toString(); ProfileInformation? profileInformation0; - PrimaryInformation? primaryInformation; + List addresses = []; List identificationInformation = []; List contactInformation = []; @@ -39,172 +39,107 @@ class ProfileService { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': "Token $token" }; - Map param={"basic":"true"}; - // try{ - http.Response response = await Request.instance - .getRequest(path: path, param: param, headers: headers); - if (response.statusCode == 200) { - Map data = jsonDecode(response.body); + Map param = {"basic": "true"}; + try { + http.Response response = await Request.instance + .getRequest(path: path, param: param, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); - // get primary information - if (data['data']['basic_information']['primary_information'] != null) { - primaryInformation = PrimaryInformation.fromJson( - data['data']['basic_information']['primary_information']); - } else { - primaryInformation = null; - } + // get all contacts + if (data['data']['basic_information']['contact_information'] != null) { + data['data']['basic_information']['contact_information'] + .forEach((var contact) { + ContactInfo contactInfo = + ContactInfo.fromJson(contact['contact_info']); + contactInformation.add(contactInfo); + }); + } - // get all contacts - if (data['data']['basic_information']['contact_information'] != null) { - data['data']['basic_information']['contact_information'] - .forEach((var contact) { - ContactInfo contactInfo = - ContactInfo.fromJson(contact['contact_info']); - contactInformation.add(contactInfo); - }); - } + // get all addresses + if (data['data']['basic_information']['addresses'] != null) { + data['data']['basic_information']['addresses'].forEach((var address) { + MainAdress mainAdress = MainAdress.fromJson(address); + addresses.add(mainAdress); + }); + } - // get all addresses - if (data['data']['basic_information']['addresses'] != null) { - data['data']['basic_information']['addresses'].forEach((var address) { - MainAdress mainAdress = MainAdress.fromJson(address); - addresses.add(mainAdress); - }); - } + // get all identifications + if (data['data']['basic_information']['identification_records'] != + null) { + data['data']['basic_information']['identification_records']! + .forEach((var identity) { + Identification identification = Identification.fromJson(identity); + identificationInformation.add(identification); + }); + } - // get all identifications - if (data['data']['basic_information']['identification_records'] != null) { - data['data']['basic_information']['identification_records']! - .forEach((var identity) { - Identification identification = Identification.fromJson(identity); - identificationInformation.add(identification); - }); - } + BasicInfo basicInfo = BasicInfo( + contactInformation: contactInformation, + identifications: identificationInformation, + citizenships: citizenships, + addresses: addresses); - // get all family background - // if(data['data']['family_background'] != null){ - // data['data']['family_background'].forEach((var family){ - // FamilyBackground familyBackground = FamilyBackground.fromJson(family); - // families.add(familyBackground); - // }); - // } - - //get all eligibilities - // if (data['data']['eligibilities'] != null) { - // data['data']['eligibilities']!.forEach((var cert) { - // EligibityCert eligibility = EligibityCert.fromJson(cert); - // eligibilities.add(eligibility); - // }); - // } - - // get all citizenships - // if (data['data']['citizenship'] != null) { - // data['data']['citizenships']!.forEach((var citizenship) { - // Citizenship person = Citizenship.fromJson(citizenship); - // citizenships.add(person); - // }); - // } - // get all references; - // if (data['data']['personal_references'] != null) { - // data['data']['personal_references'].forEach((var person) { - // PersonalReference reference = PersonalReference.fromJson(person); - // references.add(reference); - // }); - // } - - //get all learning and developments - // if (data['data']['learning_development'] != null) { - // data['data']['learning_development'].forEach((var training) { - // LearningDevelopement learnings = - // LearningDevelopement.fromJson(training); - // learningsDevelopments.add(learnings); - // }); - // } - - //get all educational background - // if (data['data']['education_background'] != null) { - // data['data']['education_background'].forEach((var education) { - // EducationalBackground educationalBackground = - // EducationalBackground.fromJson(education); - // educationalBackgrounds.add(educationalBackground); - // }); - // } - - // get all work history - // if (data['data']['work_experiences'] != null) { - // data['data']['work_experiences'].forEach((var work) { - // WorkHistory experience = WorkHistory.fromJson(work); - // workExperiences.add(experience); - // }); - // } - - // get all voluntary works - // if (data['data']['voluntary_works'] != null) { - // data['data']['voluntary_works'].forEach((var work) { - // VoluntaryWork vwork = VoluntaryWork.fromJson(work); - // voluntaryWorks.add(vwork); - // }); - // } - - // get all hobbies - // if (data['data']['other_information']['skills_hobbies'] != null) { - // data['data']['other_information']['skills_hobbies'] - // .forEach((var skills_hobbies) { - // SkillsHobbies skillsAndHobbies = - // SkillsHobbies.fromJson(skills_hobbies); - // skillsHobbies.add(skillsAndHobbies); - // }); - // } - - //get all organization memberships - // if (data['data']['other_information']['organization_memberships'] != - // null) { - // data['data']['other_information']['organization_memberships'] - // .forEach((var org) { - // OrganizationMembership organization = - // OrganizationMembership.fromJson(org); - // orgMemberships.add(organization); - // }); - // } - -//get all non academic recognition - // if (data['data']['other_information']['non_academic_records'] != null) { - // data['data']['other_information']['non_academic_records'] - // .forEach((var recognition) { - // NonAcademicRecognition nonAcademicRecognition = - // NonAcademicRecognition.fromJson(recognition); - // nonAcademicRecognitions.add(nonAcademicRecognition); - // }); - // } - - BasicInfo basicInfo = BasicInfo( - contactInformation: contactInformation, - primaryInformation: primaryInformation, - identifications: identificationInformation, - citizenships: citizenships, - addresses: addresses); - // OtherInformation otherInformation = OtherInformation( - // skillsAndHobbies: skillsHobbies, - // orgMemberships: orgMemberships, - // nonAcademicRecognition: nonAcademicRecognitions); - ProfileInformation profileInformation = ProfileInformation( - // families: families, - // otherInformation: otherInformation, - // workExperiences: workExperiences, + ProfileInformation profileInformation = ProfileInformation( basicInfo: basicInfo, - // eligibilities: eligibilities, - // references: references, - // learningsAndDevelopment: learningsDevelopments, - // educationalBackgrounds: educationalBackgrounds, - // voluntaryWorks: voluntaryWorks - ); - profileInformation0 = profileInformation; + ); + profileInformation0 = profileInformation; + } + } catch (e) { + throw (e.toString()); } - // }catch(e){ - // throw(e.toString()); - // } return profileInformation0; } + + ////Update Profile info + Future> updateBasicProfileInfo( + {required String token, + required int profileId, + required Profile profileInfo, + required int? genderId, + required int? indigencyId, + required int? disabilityId, + required int? ethnicityId, + required int? reqligionId}) async { + String authtoken = "Token $token"; + String path = Url.instance.updatePersonalInfor(); + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authtoken + }; + Map? statusResponse = {}; + Map body = { + "profile_id": profileId, + "first_name": profileInfo.firstName, + "middle_name": profileInfo.middleName, + "last_name": profileInfo.lastName, + "name_extension": profileInfo.nameExtension, + "birthdate": profileInfo.birthdate.toString(), + "sex": profileInfo.sex, + "blood_type": profileInfo.bloodType, + "civil_status": profileInfo.civilStatus, + "height": profileInfo.heightM, + "weight": profileInfo.weightKg, + "ethnicity_id": ethnicityId, + "disability_id": disabilityId, + "gender_id": genderId, + "religion_id": reqligionId, + "ip_id": indigencyId, + "title_prefix": profileInfo.titlePrefix, + "title_suffix": profileInfo.titleSuffix + }; + // try { + http.Response response = await Request.instance + .postRequest(path: path, headers: headers, body: body, param: {}); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + statusResponse = data; + } else { + statusResponse.addAll({'success': false}); + } + return statusResponse; + // } catch (e) { + // throw e.toString(); + // } + } } - - diff --git a/lib/utils/global.dart b/lib/utils/global.dart index 65b6eaf..a58ef4a 100644 --- a/lib/utils/global.dart +++ b/lib/utils/global.dart @@ -10,6 +10,8 @@ double safeBlockHorizontal = 0; double safeBlockVertical = 0; + + //// hive boxes Box? CREDENTIALS; Box? SOS; \ No newline at end of file diff --git a/lib/utils/request.dart b/lib/utils/request.dart index 329b3c4..3771b9a 100644 --- a/lib/utils/request.dart +++ b/lib/utils/request.dart @@ -10,7 +10,7 @@ import 'package:unit2/utils/urls.dart'; class Request { static final Request _instance = Request(); static Request get instance => _instance; - int requestTimeout = 25; + int requestTimeout = 30; String host = Url.instance.host(); Future getRequest( diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index 4f41d94..932de80 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -4,9 +4,10 @@ class Url { String host() { // return '192.168.10.183:3000'; - // return 'agusandelnorte.gov.ph'; - // return "192.168.10.219:3000"; - return "playweb.agusandelnorte.gov.ph"; + return 'agusandelnorte.gov.ph'; + // // return "192.168.10.219:3000"; + // return "192.168.10.241"; + // return "playweb.agusandelnorte.gov.ph"; // return 'devapi.agusandelnorte.gov.ph:3004'; } @@ -87,9 +88,18 @@ String getHonors(){ //// learning and development paths -String getLearningAndDevelopments(){ +String learningAndDevelopments(){ return "api/jobnet_app/profile/pds/learning_development/"; } +String conductedTrainings(){ + return "api/jobnet_app/conducted_trainings/"; +} +String learningAndDevelopmentType(){ + return "api/jobnet_app/learning_development/"; +} +String learningAndDevelopmentTopics(){ + return "api/jobnet_app/training_topics/"; +} //// references paths String reference(){ @@ -129,6 +139,9 @@ String addEmergency(){ String getRelationshipTypes(){ return "/api/jobnet_app/relationship_types"; } +String updatePersonalInfor(){ + return "/api/jobnet_app/profile/pds/basic/personal/"; +} //// contacts path @@ -150,8 +163,28 @@ String getCommunicationProvider(){ String deleteContact (){ return "/api/jobnet_app/profile/pds/basic/contact/"; } +////profile other info +String getReligions(){ + return "/api/profile_app/religion/"; +} +String getEthnicity(){ + return "/api/profile_app/ethnicity/"; +} - // location utils path +String getDisability(){ + return "api/profile_app/disability/"; +} + +String getIndigency(){ + return "/api/profile_app/indigenous/"; +} + +String getGenders(){ + return "/api/profile_app/gender/"; +} + + + //// location utils path String getCounties(){ return "/api/jobnet_app/countries/"; } diff --git a/pubspec.lock b/pubspec.lock index 1f2ef3f..b586442 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1101,6 +1101,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.3" + searchable_paginated_dropdown: + dependency: "direct main" + description: + name: searchable_paginated_dropdown + sha256: "289a40f7b0964f91b3d8123b4654a8397ec9df9fd1a6bc8ab545ec33c81a9eb2" + url: "https://pub.dev" + source: hosted + version: "1.2.0" searchfield: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 5a652da..4bcf5cc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -79,6 +79,7 @@ dependencies: location: ^4.3.0 platform_device_id: ^1.0.1 multi_dropdown: ^1.0.9 + searchable_paginated_dropdown: ^1.2.0 dev_dependencies: flutter_test: From e4aae209b7e3785fa8ecd9bbcc55b1e00c4fc805 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Tue, 6 Jun 2023 14:51:56 +0800 Subject: [PATCH 70/86] Finish Profile API --- ios/Runner.xcodeproj/project.pbxproj | 124 ++++++++++-------- .../contents.xcworkspacedata | 5 +- ios/Runner/Info.plist | 17 ++- 3 files changed, 80 insertions(+), 66 deletions(-) diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 230f8d3..db5f9c0 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -8,12 +8,12 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 28FEB91C01FFFF1F3AF03958 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2946B4F99A9B9A85D9A4DC4F /* Pods_Runner.framework */; }; 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 */; }; + EC92729992AEB29D54ECE4AD /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2E04E4456FC969F34935C233 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -32,14 +32,13 @@ /* 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 = ""; }; - 24CA4A0209E683A311335098 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - 2946B4F99A9B9A85D9A4DC4F /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 2E04E4456FC969F34935C233 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 74181747FCF55E0339265087 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - 7435055C04EA907D4DF9DEFB /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 4AF585336026D31B5D98E4A0 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; 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 = ""; }; + 9275E2137A20FFFC26EB3114 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.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; }; @@ -47,6 +46,7 @@ 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 = ""; }; + D017F5E329769A1D85398300 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -54,7 +54,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 28FEB91C01FFFF1F3AF03958 /* Pods_Runner.framework in Frameworks */, + EC92729992AEB29D54ECE4AD /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -64,13 +64,21 @@ 53E3B75A4BA5D03DDDC74903 /* Pods */ = { isa = PBXGroup; children = ( - 74181747FCF55E0339265087 /* Pods-Runner.debug.xcconfig */, - 7435055C04EA907D4DF9DEFB /* Pods-Runner.release.xcconfig */, - 24CA4A0209E683A311335098 /* Pods-Runner.profile.xcconfig */, + 9275E2137A20FFFC26EB3114 /* Pods-Runner.debug.xcconfig */, + 4AF585336026D31B5D98E4A0 /* Pods-Runner.release.xcconfig */, + D017F5E329769A1D85398300 /* Pods-Runner.profile.xcconfig */, ); path = Pods; sourceTree = ""; }; + 661D3BDF849DD3A816B38E4C /* Frameworks */ = { + isa = PBXGroup; + children = ( + 2E04E4456FC969F34935C233 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -89,7 +97,7 @@ 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, 53E3B75A4BA5D03DDDC74903 /* Pods */, - B81885F5B040F93E6F30E902 /* Frameworks */, + 661D3BDF849DD3A816B38E4C /* Frameworks */, ); sourceTree = ""; }; @@ -116,14 +124,6 @@ path = Runner; sourceTree = ""; }; - B81885F5B040F93E6F30E902 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 2946B4F99A9B9A85D9A4DC4F /* Pods_Runner.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -131,14 +131,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 961512EDE7FE1573CFAF92FB /* [CP] Check Pods Manifest.lock */, + DF69B491CB3FE4E427DD5727 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - CC94AAA4981E9E5E5B1BC9E4 /* [CP] Embed Pods Frameworks */, + D82405DC020597FCACED184E /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -212,7 +212,39 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 961512EDE7FE1573CFAF92FB /* [CP] Check Pods Manifest.lock */ = { + 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"; + }; + D82405DC020597FCACED184E /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + DF69B491CB3FE4E427DD5727 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -234,38 +266,6 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 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"; - }; - CC94AAA4981E9E5E5B1BC9E4 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -356,17 +356,21 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 2WLSMMLG6W; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); MARKETING_VERSION = "1.0.0 "; - PRODUCT_BUNDLE_IDENTIFIER = "uniT-App"; + PRODUCT_BUNDLE_IDENTIFIER = "unit-app"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; @@ -490,17 +494,21 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 2WLSMMLG6W; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); MARKETING_VERSION = "1.0.0 "; - PRODUCT_BUNDLE_IDENTIFIER = "uniT-App"; + PRODUCT_BUNDLE_IDENTIFIER = "unit-app"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; @@ -518,17 +526,21 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 2WLSMMLG6W; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); MARKETING_VERSION = "1.0.0 "; - PRODUCT_BUNDLE_IDENTIFIER = "uniT-App"; + PRODUCT_BUNDLE_IDENTIFIER = "unit-app"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata index 21a3cc1..9708f0a 100644 --- a/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -2,9 +2,12 @@ + location = "container:Runner.xcodeproj"> + + diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 3559a67..06ce33a 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -26,6 +26,14 @@ $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS + NSLocationAlwaysAndWhenInUseUsageDescription + Always and when in use! + NSLocationAlwaysUsageDescription + Can I have location always? + NSLocationUsageDescription + Older devices need location. + NSLocationWhenInUseUsageDescription + Need location when in use UIApplicationSupportsIndirectInputEvents UILaunchStoryboardName @@ -47,14 +55,5 @@ UIViewControllerBasedStatusBarAppearance - - NSLocationWhenInUseUsageDescription - Need location when in use - NSLocationAlwaysAndWhenInUseUsageDescription - Always and when in use! - NSLocationUsageDescription - Older devices need location. - NSLocationAlwaysUsageDescription - Can I have location always? From fce78f1bac616554ce4c442596d965eda8adc891 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Tue, 6 Jun 2023 14:54:51 +0800 Subject: [PATCH 71/86] finish Profile API --- .../profile/education/education_bloc.dart | 19 +- .../profile/eligibility/eligibility_bloc.dart | 51 +- .../eligibility/eligibility_event.dart | 5 +- lib/bloc/profile/family/family_bloc.dart | 24 +- .../learning_development_bloc.dart | 6 +- .../hobbies/hoobies_bloc.dart | 4 +- .../non_academic_recognition_bloc.dart | 10 +- .../non_academic_recognition_event.dart | 6 +- .../organization_membership_bloc.dart | 116 +- .../organization_membership_state.dart | 44 +- .../address/address_bloc.dart | 26 +- .../citizenship/citizenship_bloc.dart | 106 + .../citizenship/citizenship_event.dart | 42 + .../citizenship/citizenship_state.dart | 47 + .../contact/contact_state.dart | 5 - lib/bloc/profile/profile_bloc.dart | 5 +- .../profile/references/references_bloc.dart | 85 +- .../voluntary_works/voluntary_work_bloc.dart | 2 + .../profile/workHistory/workHistory_bloc.dart | 1 - lib/bloc/user/user_bloc.dart | 26 +- lib/bloc/user/user_event.dart | 3 + lib/main.dart | 5 + lib/model/profile/family_backround.dart | 4 +- .../basic_information/address/add_modal.dart | 768 ++--- .../basic_information/address/edit_modal.dart | 979 +++---- .../basic_information/address_screen.dart | 108 +- .../citizenship/add_modal.dart | 16 + .../citizenship/loading.dart | 37 + .../basic_information/citizenship_screen.dart | 454 ++- .../contact_information/add_modal.dart | 395 +-- .../contact_information/edit_modal.dart | 425 +-- .../contact_information_screen.dart | 240 +- .../edit_basic_info_modal.dart | 818 +++--- .../family/child_add_modal.dart | 34 +- .../family/child_edit_modal.dart | 38 +- .../family/father_add_modal.dart | 88 +- .../family/father_edit_modal.dart | 133 +- .../family/mother_add_modal.dart | 90 +- .../family/mother_edit_modal.dart | 36 +- .../family/related_add_modal.dart | 148 +- .../family/related_edit_modal.dart | 31 +- .../family/spouse_add_modal.dart | 275 +- .../family/spouse_edit_modal.dart | 147 +- .../identification/add_modal.dart | 1340 +++++---- .../identification/edit_modal.dart | 704 ++--- .../identification_information_screen.dart | 68 +- .../primary_information_screen.dart | 99 +- .../components/education/add_modal.dart | 835 +++--- .../components/education/edit_modal.dart | 912 +++--- .../profile/components/education_screen.dart | 39 +- .../components/eligibility/add_modal.dart | 701 ++--- .../components/eligibility/edit_modal.dart | 944 +++--- .../components/eligibility_screen.dart | 36 +- .../components/family_background_screen.dart | 35 +- .../learning_and_development_screen.dart | 40 +- .../learning_development/add_modal.dart | 2575 ++++++++-------- .../learning_development/edit_modal.dart | 2580 +++++++++-------- .../profile/components/loading_screen.dart | 46 +- .../non_academic/add_modal.dart | 636 ++-- .../non_academic/edit_modal.dart | 42 +- .../non_academic_recognition_screen.dart | 99 +- .../org_membership/add_modal.dart | 575 ++-- .../org_membership_screen.dart | 238 +- .../skills_and_hobbies_screen.dart | 106 +- .../skills_hobbies/add_modal.dart | 3 - .../components/reference/add_modal.dart | 716 ++--- .../components/reference/edit_modal.dart | 996 +++---- .../profile/components/references_screen.dart | 106 +- .../components/voluntary_works/add_modal.dart | 1180 ++++---- .../voluntary_works/edit_modal.dart | 1128 +++---- .../components/voluntary_works_screen.dart | 52 +- .../components/work_history/add_modal.dart | 1045 ++++--- .../components/work_history/edit_modal.dart | 1131 ++++---- .../components/work_history_screen.dart | 91 +- lib/screens/profile/profile.dart | 17 +- .../profile/shared/add_for_empty_search.dart | 4 + .../homepage.dart/components/dashboard.dart | 33 +- .../homepage.dart/components/menu-screen.dart | 87 +- .../unit2/homepage.dart/components/menu.dart | 1 - lib/screens/unit2/login/login.dart | 37 +- lib/screens/unit2/login/qr_login.dart | 52 +- .../qr_code_scanner.dart/settings_screen.dart | 24 +- lib/sevices/login_service/auth_service.dart | 64 +- lib/sevices/profile/address_service.dart | 41 +- lib/sevices/profile/citizenship_services.dart | 107 + lib/sevices/profile/family_services.dart | 4 +- .../profile/identification_services.dart | 2 +- lib/sevices/profile/profile_service.dart | 29 +- lib/sevices/profile/references_services.dart | 8 +- lib/sevices/profile/volunatary_services.dart | 8 +- .../profile/work_history_services.dart | 8 +- lib/utils/app_router.dart | 8 +- lib/utils/formatters.dart | 15 +- lib/utils/request.dart | 2 +- lib/utils/text_container.dart | 2 +- lib/utils/urls.dart | 13 +- lib/utils/validators.dart | 19 +- pubspec.yaml | 2 +- 98 files changed, 13985 insertions(+), 11602 deletions(-) create mode 100644 lib/bloc/profile/primary_information/citizenship/citizenship_bloc.dart create mode 100644 lib/bloc/profile/primary_information/citizenship/citizenship_event.dart create mode 100644 lib/bloc/profile/primary_information/citizenship/citizenship_state.dart create mode 100644 lib/screens/profile/components/basic_information/citizenship/add_modal.dart create mode 100644 lib/screens/profile/components/basic_information/citizenship/loading.dart create mode 100644 lib/sevices/profile/citizenship_services.dart diff --git a/lib/bloc/profile/education/education_bloc.dart b/lib/bloc/profile/education/education_bloc.dart index 73f08b3..e965101 100644 --- a/lib/bloc/profile/education/education_bloc.dart +++ b/lib/bloc/profile/education/education_bloc.dart @@ -15,11 +15,17 @@ class EducationBloc extends Bloc { on((event, emit) async { emit(EducationalBackgroundLoadingState()); try { - List educations = await EducationService.instace - .getEducationalBackground(event.profileId, event.token); - educationalBackgrounds = educations; - emit(EducationalBackgroundLoadedState( - educationalBackground: educationalBackgrounds)); + if (educationalBackgrounds.isEmpty) { + List educations = await EducationService + .instace + .getEducationalBackground(event.profileId, event.token); + educationalBackgrounds = educations; + emit(EducationalBackgroundLoadedState( + educationalBackground: educationalBackgrounds)); + } else { + emit(EducationalBackgroundLoadedState( + educationalBackground: educationalBackgrounds)); + } } catch (e) { emit(EducationalBackgroundErrorState(message: e.toString())); } @@ -63,7 +69,7 @@ class EducationBloc extends Bloc { emit(EducationAddedState(response: status)); } }); - ////Update + ////Update on((event, emit) async { Map status = await EducationService.instace.edit( honors: event.honors, @@ -71,6 +77,7 @@ class EducationBloc extends Bloc { token: event.token, profileId: event.profileId); if (status['success']) { + educationalBackgrounds.removeWhere((element) => event.educationalBackground.id == element.id); EducationalBackground educationalBackground = EducationalBackground.fromJson(status['data']); educationalBackgrounds.add(educationalBackground); diff --git a/lib/bloc/profile/eligibility/eligibility_bloc.dart b/lib/bloc/profile/eligibility/eligibility_bloc.dart index 4f03727..e405355 100644 --- a/lib/bloc/profile/eligibility/eligibility_bloc.dart +++ b/lib/bloc/profile/eligibility/eligibility_bloc.dart @@ -21,11 +21,7 @@ class EligibilityBloc extends Bloc { //// LOAD ELIGIBILTY on((event, emit) { emit(EligibilityLoadingState()); - if (eligibilities.isEmpty) { - GetEligibilities(profileId: event.profileId!, token: event.token!); - } else { - emit(EligibilityLoaded(eligibilities: eligibilities)); - } + emit(EligibilityLoaded(eligibilities: eligibilities)); }); //// DELETE @@ -36,7 +32,7 @@ class EligibilityBloc extends Bloc { profileId: int.parse(event.profileId), token: event.token); if (success) { - eligibilities.removeWhere( + eligibilities.removeWhere( ((EligibityCert element) => element.id == event.eligibilityId)); emit(DeletedState( success: success, @@ -84,9 +80,8 @@ class EligibilityBloc extends Bloc { (Eligibility eligibility) => event.eligibityCert.eligibility!.id == eligibility.id); bool? isOverseas = event.eligibityCert.overseas; - Country currentCountry = globalCountries.firstWhere( - (Country country) => - event.eligibityCert.examAddress!.country!.code == country.code); + Country currentCountry = globalCountries.firstWhere((Country country) => + event.eligibityCert.examAddress!.country!.code == country.code); if (event.eligibityCert.examAddress?.cityMunicipality?.province ?.region != null) { @@ -164,24 +159,28 @@ class EligibilityBloc extends Bloc { //// SHOW ADD FORM on((event, emit) async { emit(EligibilityLoadingState()); - if (globalRegions.isEmpty) { - List regions = await LocationUtils.instance.getRegions(); - globalRegions = regions; - } - if (globalEligibilities.isEmpty) { - List eligibilities = - await ProfileUtilities.instance.getEligibilities(); - globalEligibilities = eligibilities; - } - if (globalCountries.isEmpty) { - List countries = await LocationUtils.instance.getCountries(); - globalCountries = countries; - } + try { + if (globalRegions.isEmpty) { + List regions = await LocationUtils.instance.getRegions(); + globalRegions = regions; + } + if (globalEligibilities.isEmpty) { + List eligibilities = + await ProfileUtilities.instance.getEligibilities(); + globalEligibilities = eligibilities; + } + if (globalCountries.isEmpty) { + List countries = await LocationUtils.instance.getCountries(); + globalCountries = countries; + } - emit(AddEligibilityState( - eligibilities: globalEligibilities, - regions: globalRegions, - countries: globalCountries)); + emit(AddEligibilityState( + eligibilities: globalEligibilities, + regions: globalRegions, + countries: globalCountries)); + } catch (e) { + emit(EligibilityErrorState(message: e.toString())); + } }); //// ADD diff --git a/lib/bloc/profile/eligibility/eligibility_event.dart b/lib/bloc/profile/eligibility/eligibility_event.dart index 6b83a27..cd57562 100644 --- a/lib/bloc/profile/eligibility/eligibility_event.dart +++ b/lib/bloc/profile/eligibility/eligibility_event.dart @@ -38,9 +38,8 @@ class UpdateEligibility extends EligibilityEvent{ List get props =>[eligibityCert,profileId,token,oldEligibility]; } class LoadEligibility extends EligibilityEvent { - final int? profileId; - final String? token; - const LoadEligibility({ this.profileId, this.token}); + + const LoadEligibility(); @override List get props => []; } diff --git a/lib/bloc/profile/family/family_bloc.dart b/lib/bloc/profile/family/family_bloc.dart index e49737c..f948ab6 100644 --- a/lib/bloc/profile/family/family_bloc.dart +++ b/lib/bloc/profile/family/family_bloc.dart @@ -13,20 +13,24 @@ class FamilyBloc extends Bloc { on((event, emit) async { emit(FamilyLoadingState()); try { - emit(FamilyLoadingState()); - List family = await FamilyService.instance - .getFamilies(event.profileId, event.token); - families = family; + + if (families.isEmpty) { + List family = await FamilyService.instance + .getFamilies(event.profileId, event.token); + families = family; + } emit(FamilyLoaded(families: families)); } catch (e) { emit(FamilyErrorState(message: e.toString())); } + + + }); ////Load on((event, emit) { emit(FamilyLoaded(families: families)); }); - ////Add Family - }); + ////Add Family on((event, emit) async { try { emit(FamilyLoadingState()); @@ -53,7 +57,7 @@ class FamilyBloc extends Bloc { emit(FamilyLoadingState()); Map status = await FamilyService.instance .addEmergency( - requestType: event.requestType, + requestType: event.requestType, relatedPersonId: event.relatedPersonId, numberMail: event.numberMail, contactInfoId: event.contactInfoId, @@ -65,9 +69,9 @@ class FamilyBloc extends Bloc { FamilyBackground familyBackground = FamilyBackground.fromJson(status['data']); families.add(familyBackground); - emit(EmergencyContactEditedState(response: status)); + emit(EmergencyContactEditedState(response: status)); } else { - emit(EmergencyContactEditedState(response: status)); + emit(EmergencyContactEditedState(response: status)); } } catch (e) { emit(FamilyErrorState(message: e.toString())); @@ -76,7 +80,7 @@ class FamilyBloc extends Bloc { ////update on((event, emit) async { try { - emit(FamilyLoadingState()); + emit(FamilyLoadingState()); Map status = await FamilyService.instance.update( family: event.familyBackground, relationshipId: event.relationshipId, diff --git a/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart b/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart index b91e8ef..1857b85 100644 --- a/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart +++ b/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart @@ -40,9 +40,11 @@ class LearningDevelopmentBloc Province? currentProvince; CityMunicipality? currentCity; Barangay? currentBarangay; + on((event, emit) async { + emit(LearningDevelopmentLoadingState()); try { - emit(LearningDevelopmentLoadingState()); + List learnings = await LearningDevelopmentServices .instance .getLearningDevelopments(event.profileId, event.token); @@ -50,7 +52,7 @@ class LearningDevelopmentBloc emit(LearningDevelopmentLoadedState( learningsAndDevelopment: learningsAndDevelopments)); } catch (e) { - emit(LeaningDevelopmentErrorState(message: e.toString())); + emit(LearningDevelopmentErrorState(message: e.toString())); } }); ////load diff --git a/lib/bloc/profile/other_information/hobbies/hoobies_bloc.dart b/lib/bloc/profile/other_information/hobbies/hoobies_bloc.dart index fd8d923..f9034da 100644 --- a/lib/bloc/profile/other_information/hobbies/hoobies_bloc.dart +++ b/lib/bloc/profile/other_information/hobbies/hoobies_bloc.dart @@ -15,9 +15,11 @@ class HoobiesBloc extends Bloc { on((event, emit) async { emit(HobbiesLoadingState()); try { - List hobbies = await SkillsHobbiesServices.instance + if(skillsAndHobbies.isEmpty){ + List hobbies = await SkillsHobbiesServices.instance .getSkillsHobbies(event.profileId, event.token); skillsAndHobbies = hobbies; + } emit(HobbiesLoadedState(skillsAndHobbies: skillsAndHobbies)); } catch (e) { emit(HobbiesErrorState(message: e.toString())); diff --git a/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_bloc.dart b/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_bloc.dart index 83a5c1e..3806f7f 100644 --- a/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_bloc.dart +++ b/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_bloc.dart @@ -27,12 +27,10 @@ class NonAcademicRecognitionBloc await NonAcademicRecognitionServices.instance .getNonAcademicRecognition(event.profileId!, event.token!); nonAcademicRecognitions = recognitions; - emit(NonAcademicRecognitionLoadedState( - nonAcademicRecognition: nonAcademicRecognitions)); - } else { - emit(NonAcademicRecognitionLoadedState( - nonAcademicRecognition: nonAcademicRecognitions)); } + + emit(NonAcademicRecognitionLoadedState( + nonAcademicRecognition: nonAcademicRecognitions)); } catch (e) { emit(NonAcademicRecognitionErrorState(message: e.toString())); } @@ -40,7 +38,7 @@ class NonAcademicRecognitionBloc ////LOAD on((event,emit){ - nonAcademicRecognitions = event.nonAcademicRecognitions; + emit(NonAcademicRecognitionLoadedState(nonAcademicRecognition: nonAcademicRecognitions)); }); ////SHOW ADD FORM diff --git a/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_event.dart b/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_event.dart index fdb2c6d..27dee89 100644 --- a/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_event.dart +++ b/lib/bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_event.dart @@ -14,10 +14,10 @@ class GetNonAcademicRecognition extends NonAcademicRecognitionEvent{ } ////LOAD EVENT class LoadNonAcademeRecognition extends NonAcademicRecognitionEvent{ - final List nonAcademicRecognitions; - const LoadNonAcademeRecognition({required this.nonAcademicRecognitions}); + + const LoadNonAcademeRecognition(); @override - List get props => [nonAcademicRecognitions]; + List get props => []; } ////SHOW ADD FORM EVENT diff --git a/lib/bloc/profile/other_information/org_membership/organization_membership_bloc.dart b/lib/bloc/profile/other_information/org_membership/organization_membership_bloc.dart index fee8447..ce26bef 100644 --- a/lib/bloc/profile/other_information/org_membership/organization_membership_bloc.dart +++ b/lib/bloc/profile/other_information/org_membership/organization_membership_bloc.dart @@ -10,72 +10,104 @@ import '../../../../model/profile/other_information/organization_memberships.dar part 'organization_membership_event.dart'; part 'organization_membership_state.dart'; -class OrganizationMembershipBloc extends Bloc { - List agencies = []; - List agencyCategory = []; +class OrganizationMembershipBloc + extends Bloc { + List agencies = []; + List agencyCategory = []; OrganizationMembershipBloc() : super(OrganizationMembershipInitial()) { - List organizationMemberships=[]; - on((event, emit) async{ - emit(OrgmembershipLoadingState()); - try{ - if(organizationMemberships.isEmpty){ - List orgs = await OrganizationMembershipServices.instance.getOrgMemberships(event.profileId!, event.token!); - organizationMemberships = orgs; + List organizationMemberships = []; + on((event, emit) async { + emit(OrgmembershipLoadingState()); + try { + if (organizationMemberships.isEmpty) { + List orgs = + await OrganizationMembershipServices.instance + .getOrgMemberships(event.profileId!, event.token!); + organizationMemberships = orgs; + } + if (agencies.isEmpty) { + List newAgencies = + await ProfileUtilities.instance.getAgecies(); + agencies = newAgencies; + } + if (agencyCategory.isEmpty) { + List newAgencyCategories = + await ProfileUtilities.instance.agencyCategory(); + agencyCategory = newAgencyCategories; + } + emit(OrganizationMembershipLoaded( + orgMemberships: organizationMemberships, + agencies: agencies, + agencyCategory: agencyCategory)); + } catch (e) { + emit(OrganizationMembershipErrorState(message: e.toString())); } - - emit(OrganizationMembershipLoaded(orgMemberships: organizationMemberships)); - }catch(e){ - OrganizationMembershipErrorState(message: e.toString()); - } }); - on((event,emit){ - emit(OrganizationMembershipLoaded(orgMemberships: organizationMemberships)); + on((event, emit) { + emit(OrganizationMembershipLoaded( + orgMemberships: organizationMemberships, + agencies: agencies, + agencyCategory: agencyCategory)); }); ////SHOW ADD ORG MEMBERSHIP FORM - on((event,emit)async{ + on((event, emit) async { emit(OrgmembershipLoadingState()); - try{ - if(agencies.isEmpty){ - List newAgencies = await ProfileUtilities.instance.getAgecies(); + try { + if (agencies.isEmpty) { + List newAgencies = + await ProfileUtilities.instance.getAgecies(); agencies = newAgencies; } - if(agencyCategory.isEmpty){ - ListnewAgencyCategories = await ProfileUtilities.instance.agencyCategory(); + if (agencyCategory.isEmpty) { + List newAgencyCategories = + await ProfileUtilities.instance.agencyCategory(); agencyCategory = newAgencyCategories; } - emit(AddOrgMembershipState(agencies: agencies, agencyCategories: agencyCategory)); - }catch(e){ + emit(AddOrgMembershipState( + agencies: agencies, agencyCategories: agencyCategory)); + } catch (e) { emit(OrganizationMembershipErrorState(message: e.toString())); } }); //// ADD ORGMEMBERSHIP - on((event,emit)async{ - try{ - Map status= await OrganizationMembershipServices.instance.add(agency: event.agency, token: event.token, profileId: event.profileId.toString()); - if(status["success"]){ - OrganizationMembership organizationMembership = OrganizationMembership.fromJson(status["data"]); + on((event, emit) async { + try { + emit(OrgmembershipLoadingState()); + Map status = + await OrganizationMembershipServices.instance.add( + agency: event.agency, + token: event.token, + profileId: event.profileId.toString()); + if (status["success"]) { + OrganizationMembership organizationMembership = + OrganizationMembership.fromJson(status["data"]); organizationMemberships.add(organizationMembership); - emit(OrgMembershipAddedState( response: status)); - }else{ - emit(OrgMembershipAddedState( response: status)); + emit(OrgMembershipAddedState(response: status)); + } else { + emit(OrgMembershipAddedState(response: status)); } - }catch(e){ + } catch (e) { emit(OrganizationMembershipErrorState(message: e.toString())); } }); ////DELETE ORGMEMBERSHIP - on((event,emit)async{ + on((event, emit) async { emit(OrgmembershipLoadingState()); - try{ - final bool success = await OrganizationMembershipServices.instance.delete(agency: event.org.agency!, profileId: event.profileId, token: event.token); - if(success){ - organizationMemberships.removeWhere((element) => element.agency!.id == event.org.agency!.id ); + try { + final bool success = await OrganizationMembershipServices.instance + .delete( + agency: event.org.agency!, + profileId: event.profileId, + token: event.token); + if (success) { + organizationMemberships.removeWhere( + (element) => element.agency!.id == event.org.agency!.id); + emit(OrgMembershipDeletedState(success: success)); + } else { emit(OrgMembershipDeletedState(success: success)); - }else{ - emit(OrgMembershipDeletedState(success: success)); } - }catch(e){ + } catch (e) { emit(OrganizationMembershipErrorState(message: e.toString())); } }); diff --git a/lib/bloc/profile/other_information/org_membership/organization_membership_state.dart b/lib/bloc/profile/other_information/org_membership/organization_membership_state.dart index 0680c75..15d4ff5 100644 --- a/lib/bloc/profile/other_information/org_membership/organization_membership_state.dart +++ b/lib/bloc/profile/other_information/org_membership/organization_membership_state.dart @@ -2,45 +2,51 @@ part of 'organization_membership_bloc.dart'; abstract class OrganizationMembershipState extends Equatable { const OrganizationMembershipState(); - + @override List get props => []; } class OrganizationMembershipInitial extends OrganizationMembershipState {} -class OrganizationMembershipLoaded extends OrganizationMembershipState{ - final ListorgMemberships; - const OrganizationMembershipLoaded({required this.orgMemberships}); - @override +class OrganizationMembershipLoaded extends OrganizationMembershipState { + final List orgMemberships; + final List agencies; + final List agencyCategory; + const OrganizationMembershipLoaded( + {required this.orgMemberships, + required this.agencies, + required this.agencyCategory}); + @override List get props => [orgMemberships]; } -class OrganizationMembershipErrorState extends OrganizationMembershipState{ +class OrganizationMembershipErrorState extends OrganizationMembershipState { final String message; const OrganizationMembershipErrorState({required this.message}); - @override + @override List get props => [message]; } -class OrgmembershipLoadingState extends OrganizationMembershipState{ +class OrgmembershipLoadingState extends OrganizationMembershipState {} -} -class OrgMembershipDeletedState extends OrganizationMembershipState{ +class OrgMembershipDeletedState extends OrganizationMembershipState { final bool success; - const OrgMembershipDeletedState({ required this.success}); - @override + const OrgMembershipDeletedState({required this.success}); + @override List get props => [success]; } -class OrgMembershipAddedState extends OrganizationMembershipState{ - final Map response; + +class OrgMembershipAddedState extends OrganizationMembershipState { + final Map response; const OrgMembershipAddedState({required this.response}); - @override + @override List get props => [response]; } -class AddOrgMembershipState extends OrganizationMembershipState{ -final List agencies; -final List agencyCategories; -const AddOrgMembershipState({required this.agencies, required this.agencyCategories}); +class AddOrgMembershipState extends OrganizationMembershipState { + final List agencies; + final List agencyCategories; + const AddOrgMembershipState( + {required this.agencies, required this.agencyCategories}); } diff --git a/lib/bloc/profile/primary_information/address/address_bloc.dart b/lib/bloc/profile/primary_information/address/address_bloc.dart index c6022e1..6613fe8 100644 --- a/lib/bloc/profile/primary_information/address/address_bloc.dart +++ b/lib/bloc/profile/primary_information/address/address_bloc.dart @@ -123,7 +123,7 @@ class AddressBloc extends Bloc { }); ////Add on( - (event, emit) async { + (event, emit) async { try { Map status = await AddressService.instance.add( address: event.address, @@ -134,10 +134,9 @@ class AddressBloc extends Bloc { lotNumber: event.lotNumber, profileId: event.profileId); if (status['success']) { - AddressClass addressClass = AddressClass.fromJson(status['address']); - Subdivision? subdivision = status['subdivision'] !=null? Subdivision.fromJson(status['subdivision']):null; - - MainAdress address = MainAdress(address: addressClass,subdivision: subdivision,id: status['id'],details: status['details']); + AddressClass addressClass = AddressClass.fromJson(status['data']['address']); + Subdivision? subdivision = status['data']['subdivision'] !=null? Subdivision.fromJson(status['data']['subdivision']):null; + MainAdress address = MainAdress(address: addressClass,subdivision: subdivision,id: status['data']['id'],details: status['data']['details']); addresses.add(address); emit(AddressAddedState(response: status)); } else { @@ -151,7 +150,7 @@ class AddressBloc extends Bloc { ////update on( (event, emit) async { - // try { + try { Map status = await AddressService.instance.update( address: event.address, categoryId: event.categoryId, @@ -161,9 +160,11 @@ class AddressBloc extends Bloc { lotNumber: event.lotNumber, profileId: event.profileId); if (status['success']) { - AddressClass addressClass = AddressClass.fromJson(status['address']); - Subdivision? subdivision = status['subdivision'] !=null? Subdivision.fromJson(status['subdivision']):null; - MainAdress address = MainAdress(address: addressClass,subdivision: subdivision,id: status['id'],details: status['details']); + AddressClass addressClass = AddressClass.fromJson(status['data']['address']); + Subdivision? subdivision = status['data']['subdivision'] !=null? Subdivision.fromJson(status['data']['subdivision']):null; + MainAdress address = MainAdress(address: addressClass,subdivision: subdivision,id: status['data']['id'],details: status['data']['details']); + addresses.add(address); + addresses.removeWhere((address)=>address.id == event.address.id); addresses.add(address); @@ -171,12 +172,11 @@ class AddressBloc extends Bloc { } else { emit(AddressUpdatedState(response: status)); } - // } catch (e) { - // emit(AddressErrorState(message: e.toString())); - // } + } catch (e) { + emit(AddressErrorState(message: e.toString())); + } }, ); - ////Update ////Delete on((event, emit) async { diff --git a/lib/bloc/profile/primary_information/citizenship/citizenship_bloc.dart b/lib/bloc/profile/primary_information/citizenship/citizenship_bloc.dart new file mode 100644 index 0000000..b50c916 --- /dev/null +++ b/lib/bloc/profile/primary_information/citizenship/citizenship_bloc.dart @@ -0,0 +1,106 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/model/location/country.dart'; +import 'package:unit2/model/profile/basic_information/citizenship.dart'; +import 'package:unit2/sevices/profile/citizenship_services.dart'; +import 'package:unit2/utils/location_utilities.dart'; + +part 'citizenship_event.dart'; +part 'citizenship_state.dart'; + +class CitizenshipBloc extends Bloc { + List citizenships = []; + List countries = []; + CitizenshipBloc() : super(CitizenshipInitial()) { + on((event, emit) async { + emit(CitizenshipLoadingState()); + try { + countries = await LocationUtils.instance.getCountries(); + citizenships = event.citizenship; + emit(CitizenshipLoaded( + citizenships: event.citizenship, countries: countries)); + } catch (e) { + emit(CitizenshipErrorState(message: e.toString())); + } + }); + on((event, emit) { + emit(CitizenshipLoaded(citizenships: citizenships, countries: countries)); + }); + on((event, emit) async { + try { + emit(CitizenshipLoadingState()); + Map responseStatus = + await CitizenshipServices.instance.add( + profileId: event.profileId, + token: event.token, + countryId: event.coiuntryId, + naturalBorn: event.naturalBorn); + if (responseStatus['success']) { + Country newCountry = + Country.fromJson(responseStatus['data']['country']); + Citizenship citizenship = Citizenship( + country: newCountry, + naturalBorn: responseStatus['data']['natural_born']); + citizenships.add(citizenship); + emit(CitizenshipAddedState( + responseStatus: responseStatus, citizenships: citizenships)); + } else { + emit(CitizenshipAddedState( + responseStatus: responseStatus, citizenships: citizenships)); + } + } catch (e) { + emit(CitizenshipErrorState(message: e.toString())); + } + }); + + on((event, emit) async { + try { + emit(CitizenshipLoadingState()); + Map responseStatus = + await CitizenshipServices.instance.update( + profileId: event.profileId, + token: event.token, + citizenship: event.citizenship, + oldCountry: event.oldCountryId); + if (responseStatus['success'] != null && responseStatus['success']) { + citizenships.removeWhere((element) => + element.country!.id == event.citizenship.country!.id && + element.naturalBorn == event.citizenship.naturalBorn); + Country newCountry = + Country.fromJson(responseStatus['data']['country']); + Citizenship citizenship = Citizenship( + country: newCountry, + naturalBorn: responseStatus['data']['natural_born']); + citizenships.add(citizenship); + emit(CitizenshipEditedState( + responseStatus: responseStatus, citizenships: citizenships)); + } else { + emit(CitizenshipEditedState( + responseStatus: responseStatus, citizenships: citizenships)); + } + } catch (e) { + emit(CitizenshipErrorState(message: e.toString())); + } + }); + on((event, emit) async { + emit(CitizenshipLoadingState()); + try { + final bool success = await CitizenshipServices.instance.delete( + profileId: event.profileId, + token: event.token, + countryId: event.coiuntryId, + naturalBorn: event.naturalBorn); + if (success) { + citizenships.removeWhere((element) => + element.country!.id == event.coiuntryId && + element.naturalBorn == event.naturalBorn); + emit(CitizenshipDeleltedState(succcess: success)); + } else { + emit(CitizenshipDeleltedState(succcess: success)); + } + } catch (e) { + emit(CitizenshipErrorState(message: e.toString())); + } + }); + } +} diff --git a/lib/bloc/profile/primary_information/citizenship/citizenship_event.dart b/lib/bloc/profile/primary_information/citizenship/citizenship_event.dart new file mode 100644 index 0000000..a5c8cc4 --- /dev/null +++ b/lib/bloc/profile/primary_information/citizenship/citizenship_event.dart @@ -0,0 +1,42 @@ +part of 'citizenship_bloc.dart'; + +abstract class CitizenshipEvent extends Equatable { + const CitizenshipEvent(); + + @override + List get props => []; +} + +class GetCitizenship extends CitizenshipEvent{ + final List citizenship; + const GetCitizenship({required this.citizenship}); +} +class ShowAddCitizenshipForm extends CitizenshipEvent{ + const ShowAddCitizenshipForm(); +} +class LoadCitizenship extends CitizenshipEvent{ + const LoadCitizenship(); +} + +class EditCitizenship extends CitizenshipEvent{ + final Citizenship citizenship; + final int profileId; + final String token; + final int oldCountryId; + const EditCitizenship({required this.citizenship, required this.oldCountryId, required this.profileId, required this.token}); +} +class AddCitizenship extends CitizenshipEvent{ + final int profileId; + final String token; + final int coiuntryId; + final bool naturalBorn; + const AddCitizenship({required this.coiuntryId, required this.naturalBorn, required this.profileId, required this.token}); +} + +class DeleteCitizenship extends CitizenshipEvent{ + final int profileId; + final String token; + final int coiuntryId; + final bool naturalBorn; + const DeleteCitizenship({required this.coiuntryId, required this.naturalBorn, required this.profileId, required this.token}); +} diff --git a/lib/bloc/profile/primary_information/citizenship/citizenship_state.dart b/lib/bloc/profile/primary_information/citizenship/citizenship_state.dart new file mode 100644 index 0000000..e342492 --- /dev/null +++ b/lib/bloc/profile/primary_information/citizenship/citizenship_state.dart @@ -0,0 +1,47 @@ +part of 'citizenship_bloc.dart'; + +abstract class CitizenshipState extends Equatable { + const CitizenshipState(); + + @override + List get props => []; +} + +class CitizenshipInitial extends CitizenshipState {} + +class CitizenshipLoaded extends CitizenshipState{ + final List citizenships; + final List countries; + const CitizenshipLoaded({required this.citizenships,required this.countries}); +} +class CitizenshipAddingState extends CitizenshipState{ + final List countries; + const CitizenshipAddingState({required this.countries}); +} +class CitizenshipLoadingState extends CitizenshipState{ + +} +class CitizenshipAddedState extends CitizenshipState{ + final Map responseStatus; + final List citizenships; + const CitizenshipAddedState({required this.responseStatus, required this.citizenships}); + +} + +class CitizenshipEditedState extends CitizenshipState{ + final Map responseStatus; + final List citizenships; + const CitizenshipEditedState({required this.responseStatus, required this.citizenships}); + +} +class CitizenshipErrorState extends CitizenshipState{ + final String message; + const CitizenshipErrorState({required this.message}); +} + +class CitizenshipDeleltedState extends CitizenshipState{ + final bool succcess; + const CitizenshipDeleltedState({required this.succcess}); + @override + List get props => [succcess]; +} \ No newline at end of file diff --git a/lib/bloc/profile/primary_information/contact/contact_state.dart b/lib/bloc/profile/primary_information/contact/contact_state.dart index af38743..f8f7f2c 100644 --- a/lib/bloc/profile/primary_information/contact/contact_state.dart +++ b/lib/bloc/profile/primary_information/contact/contact_state.dart @@ -48,10 +48,6 @@ class ContactAddedState extends ContactState{ @override List get props => [response]; } - - - - //// edited state class ContactEditedState extends ContactState{ @@ -62,7 +58,6 @@ class ContactEditedState extends ContactState{ } ////deleted state class ContactDeletedState extends ContactState{ - final bool succcess; const ContactDeletedState({required this.succcess}); @override diff --git a/lib/bloc/profile/profile_bloc.dart b/lib/bloc/profile/profile_bloc.dart index eb2ca08..4278635 100644 --- a/lib/bloc/profile/profile_bloc.dart +++ b/lib/bloc/profile/profile_bloc.dart @@ -18,15 +18,14 @@ class ProfileBloc extends Bloc { List disabilities = []; List genders = []; List bloodType = [ - "NONE", "A+", "B+", "A-", "B-", "AB+", "AB-", - "0+", - "0-" + "O+", + "O-" ]; List nameExtensions = [ "NONE", diff --git a/lib/bloc/profile/references/references_bloc.dart b/lib/bloc/profile/references/references_bloc.dart index ba27258..ba9fe6c 100644 --- a/lib/bloc/profile/references/references_bloc.dart +++ b/lib/bloc/profile/references/references_bloc.dart @@ -23,19 +23,19 @@ class ReferencesBloc extends Bloc { on((event, emit) async { emit(ReferencesLoadingState()); try { - if(references.isEmpty){ + if (references.isEmpty) { List refs = await ReferencesServices.instace - .getRefences(event.profileId, event.token); - references = refs; - emit(ReferencesLoadedState(references: references)); - }else{ - emit(ReferencesLoadedState(references: references)); - } + .getRefences(event.profileId, event.token); + references = refs; + emit(ReferencesLoadedState(references: references)); + } + } catch (e) { - ReferencesErrorState(message: e.toString()); + emit( ReferencesErrorState(message: e.toString())); } + }); ////SHOW FORM FOR ADDING REFERENCES - }); + on((event, emit) async { emit(ReferencesLoadingState()); try { @@ -85,7 +85,10 @@ class ReferencesBloc extends Bloc { //// checck if address is overseas bool overseas = event.personalReference.address!.country!.id! != 175 ? true : false; - selectedCategory = globalCategories.firstWhere((AddressCategory element) => event.personalReference.address!.addressCategory!.id == element.id); + selectedCategory = globalCategories.firstWhere( + (AddressCategory element) => + event.personalReference.address!.addressCategory!.id == + element.id); ////if address is not overseas set initial values for address if (!overseas) { selectedRegion = globalRegions.firstWhere((Region element) => @@ -103,12 +106,15 @@ class ReferencesBloc extends Bloc { selectedCity = cities.firstWhere((CityMunicipality city) => event.personalReference.address!.cityMunicipality!.code == city.code); - List barangays = await LocationUtils.instance.getBarangay(code: selectedCity.code.toString()); - if(event.personalReference.address?.barangay!=null){ - selectedBarangay = barangays.firstWhere((Barangay barangay)=>event.personalReference.address?.barangay?.code == barangay.code); - }else{ - selectedBarangay = null; - } + List barangays = await LocationUtils.instance + .getBarangay(code: selectedCity.code.toString()); + if (event.personalReference.address?.barangay != null) { + selectedBarangay = barangays.firstWhere((Barangay barangay) => + event.personalReference.address?.barangay?.code == + barangay.code); + } else { + selectedBarangay = null; + } emit(EditReferenceState( selectedRegion: selectedRegion, ref: event.personalReference, @@ -122,14 +128,14 @@ class ReferencesBloc extends Bloc { cities: cities, selectedCity: selectedCity, selectedCategory: selectedCategory, - selectedBarangay: selectedBarangay - )); + selectedBarangay: selectedBarangay)); } else { //// if address is overseas set initial value for country - selectedCountry = globalCountries.firstWhere((Country element) => event.personalReference.address!.country!.id == element.id); + selectedCountry = globalCountries.firstWhere((Country element) => + event.personalReference.address!.country!.id == element.id); emit(EditReferenceState( - selectedCountry: selectedCountry, - selectedCategory: selectedCategory, + selectedCountry: selectedCountry, + selectedCategory: selectedCategory, selectedRegion: null, ref: event.personalReference, countries: globalCountries, @@ -147,19 +153,19 @@ class ReferencesBloc extends Bloc { emit(const ReferencesErrorState( message: "Something went wrong. Please try again")); //// EDIT REFERENCES EVENT - });on((event,emit)async{ - Map status =await ReferencesServices.instace.update(ref: event.reference, token: event.token, profileId: event.profileId); - if (status['success']) { - PersonalReference ref = PersonalReference.fromJson(status['data']); - references.removeWhere((PersonalReference element)=>element.id == event.reference.id); - references.add(ref); - emit( - ReferenceEditedState( response: status)); - } else { - emit( - ReferenceEditedState( response: status)); - } - + }); + on((event, emit) async { + Map status = await ReferencesServices.instace.update( + ref: event.reference, token: event.token, profileId: event.profileId); + if (status['success']) { + PersonalReference ref = PersonalReference.fromJson(status['data']); + references.removeWhere( + (PersonalReference element) => element.id == event.reference.id); + references.add(ref); + emit(ReferenceEditedState(response: status)); + } else { + emit(ReferenceEditedState(response: status)); + } }); //// add reference event @@ -173,11 +179,9 @@ class ReferencesBloc extends Bloc { if (status['success']) { PersonalReference ref = PersonalReference.fromJson(status['data']); references.add(ref); - emit( - ReferencesAddedState( response: status)); + emit(ReferencesAddedState(response: status)); } else { - emit( - ReferencesAddedState( response: status)); + emit(ReferencesAddedState(response: status)); } } catch (e) { emit(ReferencesErrorState(message: e.toString())); @@ -187,9 +191,8 @@ class ReferencesBloc extends Bloc { on((event, emit) { emit(ReferencesLoadingState()); emit(ReferencesLoadedState(references: references)); - }); - ////DELETE REFERENCE + ////DELETE REFERENCE on((event, emit) async { try { final bool success = await ReferencesServices.instace.delete( @@ -198,7 +201,7 @@ class ReferencesBloc extends Bloc { event.references.removeWhere( (PersonalReference element) => element.id == event.refId); references = event.references; - emit(DeleteReferenceState( success: success)); + emit(DeleteReferenceState(success: success)); } else { emit(DeleteReferenceState(success: success)); } diff --git a/lib/bloc/profile/voluntary_works/voluntary_work_bloc.dart b/lib/bloc/profile/voluntary_works/voluntary_work_bloc.dart index 5c5afa2..b9e68e3 100644 --- a/lib/bloc/profile/voluntary_works/voluntary_work_bloc.dart +++ b/lib/bloc/profile/voluntary_works/voluntary_work_bloc.dart @@ -39,9 +39,11 @@ class VoluntaryWorkBloc extends Bloc { on((event, emit) async { emit(VoluntaryWorkLoadingState()); try { + if(voluntaryWorks.isEmpty){ List works = await VoluntaryService.instance .getVoluntaryWorks(event.profileId, event.token); voluntaryWorks = works; + } emit(VoluntaryWorkLoadedState(voluntaryWorks: voluntaryWorks)); } catch (e) { emit(VoluntaryWorkErrorState(message: e.toString())); diff --git a/lib/bloc/profile/workHistory/workHistory_bloc.dart b/lib/bloc/profile/workHistory/workHistory_bloc.dart index a3c9f4b..2f8a28e 100644 --- a/lib/bloc/profile/workHistory/workHistory_bloc.dart +++ b/lib/bloc/profile/workHistory/workHistory_bloc.dart @@ -29,7 +29,6 @@ class WorkHistoryBloc extends Bloc { .getWorkExperiences(event.profileId, event.token); workExperiences = works; } - emit(WorkHistoryLoaded(workExperiences: workExperiences)); } catch (e) { emit(WorkHistoryErrorState(message: e.toString())); diff --git a/lib/bloc/user/user_bloc.dart b/lib/bloc/user/user_bloc.dart index 3bec84b..801930b 100644 --- a/lib/bloc/user/user_bloc.dart +++ b/lib/bloc/user/user_bloc.dart @@ -21,12 +21,14 @@ class UserBloc extends Bloc { VersionInfo? _versionInfo; String? _apkVersion; bool save = false; + String? uuid; UserBloc() : super(UserInitial()) { //// this event is called when opening the app to check if //// there is new app version on((event, emit) async { try { emit(SplashScreen()); + save = false; if (_versionInfo == null) { VersionInfo versionInfo = await AuthService.instance.getVersionInfo(); _versionInfo = versionInfo; @@ -36,8 +38,6 @@ class UserBloc extends Bloc { final String? saved = CREDENTIALS?.get('saved'); final String? username = CREDENTIALS?.get('username'); final String? password = CREDENTIALS?.get('password'); - debugPrint(username); - debugPrint(password); if (saved != null) { save = true; add(UserLogin(username: username, password: password)); @@ -89,9 +89,22 @@ class UserBloc extends Bloc { }); on((event, emit) async { try { - UserData? userData = await AuthService.instance + Map response = await AuthService.instance .qrLogin(uuid: event.uuid, password: event.password); - _userData = userData; + if (response['status'] == true) { + UserData userData = UserData.fromJson(response['data']); + emit(UserLoggedIn( + userData: userData, + success: true, + message: response['message'], + savedCredentials: save)); + } else { + emit(UserLoggedIn( + userData: null, + success: false, + message: response['message'], + savedCredentials: save)); + } emit(UserLoggedIn(userData: _userData, savedCredentials: save)); } on TimeoutException catch (_) { emit(InternetTimeout(message: timeoutError)); @@ -107,8 +120,11 @@ class UserBloc extends Bloc { on((event, emit) async { ScanResult result = await QRCodeBarCodeScanner.instance.scanner(); if (result.rawContent.toString().isNotEmpty) { - emit(UuidLoaded(uuid: result.rawContent.toString())); + uuid = result.rawContent.toString(); + emit(UuidLoaded(uuid: uuid!)); } + });on((event,emit){ + emit(UuidLoaded(uuid: uuid!)); }); } } diff --git a/lib/bloc/user/user_event.dart b/lib/bloc/user/user_event.dart index a0cbd10..002410e 100644 --- a/lib/bloc/user/user_event.dart +++ b/lib/bloc/user/user_event.dart @@ -26,6 +26,9 @@ class LoadLoggedInUser extends UserEvent { class GetUuid extends UserEvent { GetUuid(); } +class LoadUuid extends UserEvent{ + LoadUuid(); +} class LoadVersion extends UserEvent { diff --git a/lib/main.dart b/lib/main.dart index 20b079b..5144a73 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:device_preview/device_preview.dart'; @@ -15,6 +17,8 @@ import 'package:path_provider/path_provider.dart' as path_provider; import './utils/router.dart'; import './utils/global.dart'; + + Future main() async { WidgetsFlutterBinding.ensureInitialized(); var appDirectory = await path_provider.getApplicationDocumentsDirectory(); @@ -88,3 +92,4 @@ class MyApp extends StatelessWidget { ); } } + diff --git a/lib/model/profile/family_backround.dart b/lib/model/profile/family_backround.dart index 9d0d443..3afb910 100644 --- a/lib/model/profile/family_backround.dart +++ b/lib/model/profile/family_backround.dart @@ -272,8 +272,8 @@ class Relationship { } class MaidenName { - final String lastName; - final String middleName; + final String? lastName; + final String? middleName; MaidenName({ required this.lastName, diff --git a/lib/screens/profile/components/basic_information/address/add_modal.dart b/lib/screens/profile/components/basic_information/address/add_modal.dart index 21acda2..fd8514e 100644 --- a/lib/screens/profile/components/basic_information/address/add_modal.dart +++ b/lib/screens/profile/components/basic_information/address/add_modal.dart @@ -1,6 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; @@ -9,10 +7,7 @@ import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; import 'package:unit2/bloc/profile/primary_information/address/address_bloc.dart'; import 'package:unit2/model/location/address_category.dart'; import 'package:unit2/model/profile/basic_information/adress.dart'; -import 'package:unit2/model/profile/voluntary_works.dart'; -import 'package:unit2/model/utils/category.dart'; import 'package:unit2/utils/global.dart'; - import '../../../../../model/location/barangay.dart'; import '../../../../../model/location/city.dart'; import '../../../../../model/location/country.dart'; @@ -75,399 +70,404 @@ class _AddAddressScreenState extends State { return BlocBuilder( builder: (context, state) { if (state is AddAddressState) { - return SingleChildScrollView( - child: SizedBox( - height: screenHeight * .90, - child: FormBuilder( - key: formKey, - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 25, horizontal: 18), - child: Column( - children: [ - //// category - FormBuilderDropdown( - validator: FormBuilderValidators.required( - errorText: "This field is required"), - decoration: - normalTextFieldStyle("Category*", "Category"), - name: "category", - onChanged: (AddressCategory? category) { - selectedAddressCategory = category; - }, - items: category - .map>( - (AddressCategory category) { - return DropdownMenuItem( - value: category, child: Text(category.name!)); - }).toList()), - const SizedBox( - height: 12, - ), - ////Area Class - FormBuilderDropdown( - validator: FormBuilderValidators.required( - errorText: "This field is required"), - decoration: normalTextFieldStyle( - "Area class *", "Area class"), - name: "area_class", - onChanged: (Area? area) { - selectedAreaClass = area!.value; - }, - items: areaClass - .map>((Area area) { - return area.group == 0 - ? DropdownMenuItem( - enabled: false, - value: area, - child: Text(area.value.toUpperCase(), - style: const TextStyle( - color: Colors.black38))) - : DropdownMenuItem( - value: area, - enabled: true, - child: Text( - " ${area.value.toUpperCase()}")); - }).toList()), - const SizedBox( - height: 12, - ), - ////stateful builder - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - ////with block & Lot Switch - FormBuilderSwitch( - initialValue: hasLotandBlock, - activeColor: second, - onChanged: (value) { - setState(() { - hasLotandBlock = value!; - }); + return FormBuilder( + key: formKey, + child: Padding( + padding: + const EdgeInsets.symmetric(vertical: 32, horizontal: 24), + child: SizedBox( + height: screenHeight * 88, + child: Column( + children: [ + Flexible( + child: ListView( + children: [ + //// category + FormBuilderDropdown( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: + normalTextFieldStyle("Category*", "Category"), + name: "category", + onChanged: (AddressCategory? category) { + selectedAddressCategory = category; }, + items: category + .map>( + (AddressCategory category) { + return DropdownMenuItem( + value: category, child: Text(category.name!)); + }).toList()), + const SizedBox( + height: 12, + ), + ////Area Class + FormBuilderDropdown( decoration: normalTextFieldStyle( - "With Lot and Block?", 'Graduated?'), - name: 'graudated', - title: Text(hasLotandBlock ? "YES" : "NO"), - ), - SizedBox( - height: hasLotandBlock ? 12 : 0, - ), - SizedBox( - width: screenWidth, - child: hasLotandBlock - ? Row( - children: [ - ////block # - Flexible( - flex: 1, - child: FormBuilderTextField( - validator: FormBuilderValidators.compose([numericRequired]), - keyboardType: TextInputType.number, - name: "block_number", - decoration: - normalTextFieldStyle( - "Block #*", "Block #"), - )), - const SizedBox( - width: 8, - ), - //// lot # - Flexible( - flex: 1, - child: FormBuilderTextField( - validator: FormBuilderValidators.compose([numericRequired]), - name: "lot_number", - keyboardType: TextInputType.number, - decoration: - normalTextFieldStyle( - "Lot #*", "Lot #"), - )), - ], - ) - : Container(), - ), - ], - ); - }), - const SizedBox( - height: 12, - ), - //// Address Line - FormBuilderTextField( - - name: "address_line", - decoration: normalTextFieldStyle( - "Address Line *", "Address Line"), - ), - const SizedBox( - height: 12, - ), - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - setState(() { - overseas = value!; - }); + "Area class *", "Area class"), + name: "area_class", + onChanged: (Area? area) { + selectedAreaClass = area!.value; }, - decoration: normalTextFieldStyle( - "Overseas Address?", ''), - name: 'overseas', - title: Text(overseas ? "YES" : "NO"), - ), - SizedBox( - height: overseas == true ? 8 : 0, - ), - SizedBox( - child: overseas == false - ? Column( - children: [ - const SizedBox( - height: 12, - ), - ////REGION DROPDOWN - FormBuilderDropdown( - autovalidateMode: AutovalidateMode - .onUserInteraction, - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - onChanged: (Region? region) async { - if (selectedRegion != region) { - setState(() { - provinceCall = true; - }); - selectedRegion = region; - getProvinces(); - } - }, - initialValue: null, - decoration: normalTextFieldStyle( - "Region*", "Region"), - name: 'region', - items: state.regions - .map>( - (Region region) { - return DropdownMenuItem( - value: region, - child: Text( - region.description!)); - }).toList(), - ), - const SizedBox( - height: 8, - ), - //// PROVINCE DROPDOWN - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: provinceCall, - child: DropdownButtonFormField< - Province?>( - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - value: selectedProvince, - onChanged: - (Province? province) { - if (selectedProvince != - province) { - setState(() { - cityCall = true; - }); - selectedProvince = - province; - getCities(); - } - }, - items: provinces == null - ? [] - : provinces!.map< - DropdownMenuItem< - Province>>( - (Province province) { - return DropdownMenuItem( - value: province, - child: FittedBox( - child: Text(province - .description!), - )); - }).toList(), - decoration: - normalTextFieldStyle( - "Province*", - "Province")), - ), - ), - ////CITY MUNICIPALITY - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: cityCall, - child: DropdownButtonFormField< - CityMunicipality>( - validator: FormBuilderValidators - .required( + items: areaClass + .map>((Area area) { + return area.group == 0 + ? DropdownMenuItem( + enabled: false, + value: area, + child: Text(area.value.toUpperCase(), + style: const TextStyle( + color: Colors.black38))) + : DropdownMenuItem( + value: area, + enabled: true, + child: Text( + " ${area.value.toUpperCase()}")); + }).toList()), + const SizedBox( + height: 12, + ), + ////stateful builder + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + ////with block & Lot Switch + FormBuilderSwitch( + initialValue: hasLotandBlock, + activeColor: second, + onChanged: (value) { + setState(() { + hasLotandBlock = value!; + }); + }, + decoration: normalTextFieldStyle( + "With Lot and Block?", 'Graduated?'), + name: 'graudated', + title: Text(hasLotandBlock ? "YES" : "NO"), + ), + SizedBox( + height: hasLotandBlock ? 12 : 0, + ), + SizedBox( + width: screenWidth, + child: hasLotandBlock + ? Row( + children: [ + ////block # + Flexible( + flex: 1, + child: FormBuilderTextField( + validator: FormBuilderValidators + .compose([numericRequired]), + keyboardType: + TextInputType.number, + name: "block_number", + decoration: + normalTextFieldStyle( + "Block #*", "Block #"), + )), + const SizedBox( + width: 8, + ), + //// lot # + Flexible( + flex: 1, + child: FormBuilderTextField( + validator: FormBuilderValidators + .compose([numericRequired]), + name: "lot_number", + keyboardType: + TextInputType.number, + decoration: + normalTextFieldStyle( + "Lot #*", "Lot #"), + )), + ], + ) + : Container(), + ), + ], + ); + }), + const SizedBox( + height: 12, + ), + //// Address Line + FormBuilderTextField( + name: "address_line", + decoration: normalTextFieldStyle( + "Address Line ", "Address Line"), + ), + const SizedBox( + height: 12, + ), + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: normalTextFieldStyle( + "Overseas Address?", ''), + name: 'overseas', + title: Text(overseas ? "YES" : "NO"), + ), + SizedBox( + height: overseas == true ? 8 : 0, + ), + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + FormBuilderDropdown( + autovalidateMode: AutovalidateMode + .onUserInteraction, + validator: + FormBuilderValidators.required( errorText: "This field is required"), - isExpanded: true, - onChanged: - (CityMunicipality? city) { - if (selectedMunicipality != - city) { + onChanged: (Region? region) async { + if (selectedRegion != region) { setState(() { - barangayCall = true; + provinceCall = true; }); - selectedMunicipality = city; - getBarangays(); + selectedRegion = region; + getProvinces(); } }, - decoration: - normalTextFieldStyle( - "Municipality*", - "Municipality"), - value: selectedMunicipality, - items: citymuns == null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality c) { - return DropdownMenuItem( - value: c, - child: Text(c - .description!)); - }).toList(), + initialValue: null, + decoration: normalTextFieldStyle( + "Region*", "Region"), + name: 'region', + items: state.regions + .map>( + (Region region) { + return DropdownMenuItem( + value: region, + child: Text( + region.description!)); + }).toList(), ), - ), - ), - //// BARANGAY - SizedBox( + const SizedBox( + height: 8, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + value: selectedProvince, + onChanged: + (Province? province) { + if (selectedProvince != + province) { + setState(() { + cityCall = true; + }); + selectedProvince = + province; + getCities(); + } + }, + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province province) { + return DropdownMenuItem( + value: province, + child: FittedBox( + child: Text(province + .description!), + )); + }).toList(), + decoration: + normalTextFieldStyle( + "Province*", + "Province")), + ), + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: DropdownButtonFormField< + CityMunicipality>( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + isExpanded: true, + onChanged: + (CityMunicipality? city) { + if (selectedMunicipality != + city) { + setState(() { + barangayCall = true; + }); + selectedMunicipality = city; + getBarangays(); + } + }, + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), + ), + ), + ), + //// BARANGAY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: barangayCall, + child: DropdownButtonFormField< + Barangay>( + isExpanded: true, + onChanged: (Barangay? baragay) { + selectedBarangay = baragay; + }, + decoration: + normalTextFieldStyle( + "Barangay*", + "Barangay"), + value: selectedBarangay, + items: barangays == null + ? [] + : barangays!.map< + DropdownMenuItem< + Barangay>>( + (Barangay barangay) { + return DropdownMenuItem( + value: barangay, + child: Text(barangay + .description!)); + }).toList(), + ), + ), + ), + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: barangayCall, - child: DropdownButtonFormField< - Barangay>( - isExpanded: true, - onChanged: (Barangay? baragay) { - selectedBarangay = baragay; - }, - decoration: - normalTextFieldStyle( - "Barangay*", - "Barangay"), - value: selectedBarangay, - items: barangays == null - ? [] - : barangays!.map< - DropdownMenuItem< - Barangay>>( - (Barangay barangay) { - return DropdownMenuItem( - value: barangay, - child: Text(barangay - .description!)); - }).toList(), - ), + child: FormBuilderDropdown( + initialValue: null, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + items: state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox( + child: + Text(country.name!))); + }).toList(), + name: 'country', + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) { + selectedCountry = value; + }, ), ), - ], - ) - //// COUNTRY DROPDOWN - : SizedBox( - height: 60, - child: FormBuilderDropdown( - initialValue: null, - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - items: state.countries - .map>( - (Country country) { - return DropdownMenuItem( - value: country, - child: FittedBox( - child: - Text(country.name!))); - }).toList(), - name: 'country', - decoration: normalTextFieldStyle( - "Country*", "Country"), - onChanged: (Country? value) { - selectedCountry = value; - }, - ), - ), - ), - ], - ); - }), - ////sumit button - const Expanded(child: SizedBox()), - SizedBox( - width: double.infinity, - height: 60, - child: ElevatedButton( - style: mainBtnStyle( - primary, Colors.transparent, second), - onPressed: () { - if (formKey.currentState!.saveAndValidate()) { - String? lotNumber; - String? blockNumber; - String? addressLine; - Country country = selectedCountry ??= Country( - id: 175, name: 'Philippines', code: 'PH'); - AddressClass address = AddressClass( - barangay: selectedBarangay, - id: null, - category: null, - areaClass: selectedAreaClass, - cityMunicipality: selectedMunicipality, - country: country); - if (hasLotandBlock) { - lotNumber = formKey - .currentState!.value['lot_number']; - blockNumber = formKey - .currentState!.value['block_number']; - } - addressLine = formKey - .currentState?.value['address_line']; - final progress = ProgressHUD.of(context); - progress!.showWithText("Loading..."); - context.read().add(AddAddress( - address: address, - profileId: widget.profileId, - token: widget.token, - blockNumber: blockNumber != null?int.parse(blockNumber):null, - categoryId: selectedAddressCategory!.id!, - details: addressLine, - lotNumber: lotNumber != null?int.parse(lotNumber):null)); + ), + ], + ); + }), + ////sumit button + ], + ), + ), + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: + mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState!.saveAndValidate()) { + String? lotNumber; + String? blockNumber; + String? addressLine; + Country country = selectedCountry ??= Country( + id: 175, name: 'Philippines', code: 'PH'); + AddressClass address = AddressClass( + barangay: selectedBarangay, + id: null, + category: null, + areaClass: selectedAreaClass, + cityMunicipality: selectedMunicipality, + country: country); + if (hasLotandBlock) { + lotNumber = + formKey.currentState!.value['lot_number']; + blockNumber = + formKey.currentState!.value['block_number']; } - }, - child: const Text(submit)), - ), - const SizedBox( - height: 20, - ), - ], - ), - )), - ), - ); + addressLine = + formKey.currentState?.value['address_line']; + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add(AddAddress( + address: address, + profileId: widget.profileId, + token: widget.token, + blockNumber: blockNumber != null + ? int.parse(blockNumber) + : null, + categoryId: selectedAddressCategory!.id!, + details: addressLine, + lotNumber: lotNumber != null + ? int.parse(lotNumber) + : null)); + } + }, + child: const Text(submit)), + ), + ], + ), + ), + )); } return Placeholder(); }, diff --git a/lib/screens/profile/components/basic_information/address/edit_modal.dart b/lib/screens/profile/components/basic_information/address/edit_modal.dart index 9eb828f..29b3325 100644 --- a/lib/screens/profile/components/basic_information/address/edit_modal.dart +++ b/lib/screens/profile/components/basic_information/address/edit_modal.dart @@ -1,6 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; @@ -9,11 +7,7 @@ import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; import 'package:unit2/bloc/profile/primary_information/address/address_bloc.dart'; import 'package:unit2/model/location/address_category.dart'; import 'package:unit2/model/profile/basic_information/adress.dart'; -import 'package:unit2/model/profile/voluntary_works.dart'; -import 'package:unit2/model/utils/category.dart'; -import 'package:unit2/screens/profile/components/other_information/org_membership/add_modal.dart'; import 'package:unit2/utils/global.dart'; - import '../../../../../model/location/barangay.dart'; import '../../../../../model/location/city.dart'; import '../../../../../model/location/country.dart'; @@ -85,6 +79,7 @@ class _EditAddressScreenState extends State { selectedMunicipality = state.currentCity; barangays = state.baragays; selectedBarangay = state.currentBarangay; + selectedCountry = state.currentCountry; } else { selectedCountry = state.currentCountry; } @@ -103,356 +98,237 @@ class _EditAddressScreenState extends State { } else { hasLotandBlock = false; } - return SingleChildScrollView( - child: SizedBox( - height: screenHeight * .90, - child: FormBuilder( - key: formKey, - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 25, horizontal: 18), - child: ListView( - children: [ - //// category - FormBuilderDropdown( - initialValue: selectedAddressCategory, - validator: FormBuilderValidators.required( - errorText: "This field is required"), - decoration: - normalTextFieldStyle("Category*", "Category"), - name: "category", - onChanged: (AddressCategory? category) { - selectedAddressCategory = category; - }, - items: category - .map>( - (AddressCategory category) { - return DropdownMenuItem( - value: category, child: Text(category.name!)); - }).toList()), - const SizedBox( - height: 12, - ), - ////Area Class - FormBuilderDropdown( - initialValue: selectedAreaClass, - validator: FormBuilderValidators.required( - errorText: "This field is required"), - decoration: normalTextFieldStyle( - "Area class *", "Area class"), - name: "area_class", - onChanged: (Area? area) { - selectedAreaClass = area; - }, - items: areaClass - .map>((Area area) { - return area.group == 0 - ? DropdownMenuItem( - enabled: false, - value: area, - child: Text(area.value.toUpperCase(), - style: const TextStyle( - color: Colors.black38))) - : DropdownMenuItem( - value: area, - enabled: true, - child: Text( - " ${area.value.toUpperCase()}")); - }).toList()), - const SizedBox( - height: 12, - ), - ////stateful builder - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - ////with block & Lot Switch - FormBuilderSwitch( - initialValue: hasLotandBlock, - activeColor: second, - onChanged: (value) { - setState(() { - hasLotandBlock = value!; - }); + return FormBuilder( + key: formKey, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 32, horizontal: 24), + child: SizedBox( + height: screenHeight * 88, + child: Column( + children: [ + Flexible( + child: ListView( + children: [ + //// category + FormBuilderDropdown( + initialValue: selectedAddressCategory, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: + normalTextFieldStyle("Category*", "Category"), + name: "category", + onChanged: (AddressCategory? category) { + selectedAddressCategory = category; }, + items: category + .map>( + (AddressCategory category) { + return DropdownMenuItem( + value: category, child: Text(category.name!)); + }).toList()), + const SizedBox( + height: 12, + ), + ////Area Class + FormBuilderDropdown( + initialValue: selectedAreaClass, decoration: normalTextFieldStyle( - "With Lot and Block?", 'Graduated?'), - name: 'graudated', - title: Text(hasLotandBlock ? "YES" : "NO"), - ), - SizedBox( - height: hasLotandBlock ? 12 : 0, - ), - SizedBox( - width: screenWidth, - child: hasLotandBlock - ? Row( - children: [ - ////block # - Flexible( - flex: 1, - child: FormBuilderTextField( - initialValue: state.address.subdivision?.blockNo?.toString(), - validator: FormBuilderValidators - .compose([numericRequired]), - keyboardType: - TextInputType.number, - name: "block_number", - decoration: - normalTextFieldStyle( - "Block #*", "Block #"), - )), - const SizedBox( - width: 8, - ), - //// lot # - Flexible( - flex: 1, - child: FormBuilderTextField( - initialValue: state.address.subdivision?.lotNo?.toString(), - validator: FormBuilderValidators - .compose([numericRequired]), - name: "lot_number", - keyboardType: - TextInputType.number, - decoration: - normalTextFieldStyle( - "Lot #*", "Lot #"), - )), - ], - ) - : Container(), - ), - ], - ); - }), - const SizedBox( - height: 12, - ), - //// Address Line - FormBuilderTextField( - initialValue: state.address.details, - name: "address_line", - decoration: normalTextFieldStyle( - "Address Line *", "Address Line"), - ), - const SizedBox( - height: 12, - ), - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - setState(() { - overseas = value!; - }); + "Area class *", "Area class"), + name: "area_class", + onChanged: (Area? area) { + selectedAreaClass = area; }, - decoration: normalTextFieldStyle( - "Overseas Address?", ''), - name: 'overseas', - title: Text(overseas ? "YES" : "NO"), - ), - SizedBox( - height: overseas == true ? 8 : 0, - ), - SizedBox( - child: overseas == false - ? Column( - children: [ - const SizedBox( - height: 12, - ), - ////REGION DROPDOWN - DropdownButtonFormField( - isExpanded: true, - value: selectedRegion, - autovalidateMode: AutovalidateMode - .onUserInteraction, - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - onChanged: (Region? region) async { - if (selectedRegion != region) { - setState(() { - provinceCall = true; - }); - selectedRegion = region; - //// GET PROVINCES - provinces = await LocationUtils - .instance - .getProvinces( - regionCode: - selectedRegion!.code - .toString()); - selectedProvince = - provinces![0]; - setState(() { - provinceCall = false; - cityCall = true; - }); - //// GET CITIES - citymuns = await LocationUtils - .instance - .getCities( - code: selectedProvince! - .code!); - selectedMunicipality = - citymuns![0]; - setState(() { - cityCall = false; - barangayCall = true; - }); - //// GET BARANGAY - barangays = await LocationUtils - .instance - .getBarangay( - code: - selectedMunicipality! - .code!); - selectedBarangay = - barangays![0]; - setState(() { - barangayCall = false; - }); - ////GET CITY MUNICIPALITY - citymuns = await LocationUtils - .instance - .getCities( - code: selectedProvince! - .code!); - selectedMunicipality = - citymuns![0]; - setState(() { - cityCall = false; - barangayCall = true; - }); - //// GET BARANGAYS - barangays = await LocationUtils - .instance - .getBarangay( - code: - selectedMunicipality! - .code!); - selectedBarangay = - barangays![0]; - setState(() { - barangayCall = false; - }); - } - }, - decoration: normalTextFieldStyle( - "Region*", "Region"), - items: state.regions - .map>( - (Region region) { - return DropdownMenuItem( - value: region, - child: Text( - region.description!)); - }).toList(), - ), - const SizedBox( - height: 8, - ), - //// PROVINCE DROPDOWN - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: provinceCall, - child: DropdownButtonFormField< - Province?>( - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - value: selectedProvince, - onChanged: (Province? - province) async { - if (selectedProvince != - province) { - selectedProvince = - province; - setState(() { - cityCall = true; - }); - - //// GET CITIES - citymuns = await LocationUtils - .instance - .getCities( - code: - selectedProvince! - .code!); - selectedMunicipality = - citymuns![0]; - setState(() { - cityCall = false; - barangayCall = true; - }); - //// GET BARANGAY - barangays = await LocationUtils - .instance - .getBarangay( - code: - selectedMunicipality! - .code!); - selectedBarangay = - barangays![0]; - setState(() { - barangayCall = false; - }); - } - }, - items: provinces == null - ? [] - : provinces!.map< - DropdownMenuItem< - Province>>( - (Province province) { - return DropdownMenuItem( - value: province, - child: FittedBox( - child: Text(province - .description!), - )); - }).toList(), - decoration: - normalTextFieldStyle( - "Province*", - "Province")), - ), - ), - ////CITY MUNICIPALITY - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: cityCall, - child: DropdownButtonFormField< - CityMunicipality>( - validator: FormBuilderValidators - .required( + items: areaClass + .map>((Area area) { + return area.group == 0 + ? DropdownMenuItem( + enabled: false, + value: area, + child: Text(area.value.toUpperCase(), + style: const TextStyle( + color: Colors.black38))) + : DropdownMenuItem( + value: area, + enabled: true, + child: Text( + " ${area.value.toUpperCase()}")); + }).toList()), + const SizedBox( + height: 12, + ), + ////stateful builder + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + ////with block & Lot Switch + FormBuilderSwitch( + initialValue: hasLotandBlock, + activeColor: second, + onChanged: (value) { + setState(() { + hasLotandBlock = value!; + }); + }, + decoration: normalTextFieldStyle( + "With Lot and Block?", 'Graduated?'), + name: 'graudated', + title: Text(hasLotandBlock ? "YES" : "NO"), + ), + SizedBox( + height: hasLotandBlock ? 12 : 0, + ), + SizedBox( + width: screenWidth, + child: hasLotandBlock + ? Row( + children: [ + ////block # + Flexible( + flex: 1, + child: FormBuilderTextField( + initialValue: state.address + .subdivision?.blockNo + ?.toString(), + validator: FormBuilderValidators + .compose([numericRequired]), + keyboardType: + TextInputType.number, + name: "block_number", + decoration: + normalTextFieldStyle( + "Block #*", "Block #"), + )), + const SizedBox( + width: 8, + ), + //// lot # + Flexible( + flex: 1, + child: FormBuilderTextField( + initialValue: state + .address.subdivision?.lotNo + ?.toString(), + validator: FormBuilderValidators + .compose([numericRequired]), + name: "lot_number", + keyboardType: + TextInputType.number, + decoration: + normalTextFieldStyle( + "Lot #*", "Lot #"), + )), + ], + ) + : Container(), + ), + ], + ); + }), + const SizedBox( + height: 12, + ), + //// Address Line + FormBuilderTextField( + initialValue: state.address.details, + name: "address_line", + decoration: normalTextFieldStyle( + "Address Line *", "Address Line"), + ), + const SizedBox( + height: 12, + ), + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: normalTextFieldStyle( + "Overseas Address?", ''), + name: 'overseas', + title: Text(overseas ? "YES" : "NO"), + ), + SizedBox( + height: overseas == true ? 8 : 0, + ), + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + DropdownButtonFormField( + isExpanded: true, + value: selectedRegion, + autovalidateMode: AutovalidateMode + .onUserInteraction, + validator: + FormBuilderValidators.required( errorText: "This field is required"), - isExpanded: true, - onChanged: (CityMunicipality? - city) async { - if (selectedMunicipality != - city) { + onChanged: (Region? region) async { + if (selectedRegion != region) { setState(() { + provinceCall = true; + }); + selectedRegion = region; + //// GET PROVINCES + provinces = await LocationUtils + .instance + .getProvinces( + regionCode: + selectedRegion!.code + .toString()); + selectedProvince = + provinces![0]; + setState(() { + provinceCall = false; + cityCall = true; + }); + //// GET CITIES + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code!); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + barangayCall = true; + }); + //// GET BARANGAY + barangays = await LocationUtils + .instance + .getBarangay( + code: + selectedMunicipality! + .code!); + selectedBarangay = + barangays![0]; + setState(() { + barangayCall = false; + }); + ////GET CITY MUNICIPALITY + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code!); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; barangayCall = true; }); - selectedMunicipality = city; - selectedMunicipality = city; //// GET BARANGAYS barangays = await LocationUtils .instance @@ -467,149 +343,280 @@ class _EditAddressScreenState extends State { }); } }, - decoration: - normalTextFieldStyle( - "Municipality*", - "Municipality"), - value: selectedMunicipality, - items: citymuns == null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality c) { - return DropdownMenuItem( - value: c, - child: Text(c - .description!)); - }).toList(), + decoration: normalTextFieldStyle( + "Region*", "Region"), + items: state.regions + .map>( + (Region region) { + return DropdownMenuItem( + value: region, + child: Text( + region.description!)); + }).toList(), ), - ), - ), - //// BARANGAY - SizedBox( + const SizedBox( + height: 8, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + value: selectedProvince, + onChanged: (Province? + province) async { + if (selectedProvince != + province) { + selectedProvince = + province; + setState(() { + cityCall = true; + }); + + //// GET CITIES + citymuns = await LocationUtils + .instance + .getCities( + code: + selectedProvince! + .code!); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + barangayCall = true; + }); + //// GET BARANGAY + barangays = await LocationUtils + .instance + .getBarangay( + code: + selectedMunicipality! + .code!); + selectedBarangay = + barangays![0]; + setState(() { + barangayCall = false; + }); + } + }, + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province province) { + return DropdownMenuItem( + value: province, + child: FittedBox( + child: Text(province + .description!), + )); + }).toList(), + decoration: + normalTextFieldStyle( + "Province*", + "Province")), + ), + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: DropdownButtonFormField< + CityMunicipality>( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + isExpanded: true, + onChanged: (CityMunicipality? + city) async { + if (selectedMunicipality != + city) { + setState(() { + barangayCall = true; + }); + selectedMunicipality = city; + selectedMunicipality = city; + //// GET BARANGAYS + barangays = await LocationUtils + .instance + .getBarangay( + code: + selectedMunicipality! + .code!); + selectedBarangay = + barangays![0]; + setState(() { + barangayCall = false; + }); + } + }, + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), + ), + ), + ), + //// BARANGAY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: barangayCall, + child: DropdownButtonFormField< + Barangay>( + isExpanded: true, + onChanged: (Barangay? baragay) { + selectedBarangay = baragay; + }, + decoration: + normalTextFieldStyle( + "Barangay*", + "Barangay"), + value: selectedBarangay, + items: barangays == null + ? [] + : barangays!.map< + DropdownMenuItem< + Barangay>>( + (Barangay barangay) { + return DropdownMenuItem( + value: barangay, + child: Text(barangay + .description!)); + }).toList(), + ), + ), + ), + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: barangayCall, - child: DropdownButtonFormField< - Barangay>( - isExpanded: true, - onChanged: (Barangay? baragay) { - selectedBarangay = baragay; - }, - decoration: - normalTextFieldStyle( - "Barangay*", - "Barangay"), - value: selectedBarangay, - items: barangays == null - ? [] - : barangays!.map< - DropdownMenuItem< - Barangay>>( - (Barangay barangay) { - return DropdownMenuItem( - value: barangay, - child: Text(barangay - .description!)); - }).toList(), - ), + child: FormBuilderDropdown( + initialValue:selectedCountry!.id == 175?null:selectedCountry, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + items: state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox( + child: + Text(country.name!))); + }).toList(), + name: 'country', + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) { + setState((){ + selectedCountry = value; + }); + }, ), ), - ], - ) - //// COUNTRY DROPDOWN - : SizedBox( - height: 60, - child: FormBuilderDropdown( - initialValue: selectedCountry, - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - items: state.countries - .map>( - (Country country) { - return DropdownMenuItem( - value: country, - child: FittedBox( - child: - Text(country.name!))); - }).toList(), - name: 'country', - decoration: normalTextFieldStyle( - "Country*", "Country"), - onChanged: (Country? value) { - selectedCountry = value; - }, - ), - ), - ), - ], - ); - }), - ////sumit button - const Expanded(child: SizedBox()), - SizedBox( - width: double.infinity, - height: 60, - child: ElevatedButton( - style: mainBtnStyle( - primary, Colors.transparent, second), - onPressed: () { - if (formKey.currentState!.saveAndValidate()) { - String? lotNumber; - String? blockNumber; - String? addressLine; - if(overseas){ - selectedCountry = selectedCountry; - }else{ - selectedCountry = Country( - id: 175, name: 'Philippines', code: 'PH'); - } - - AddressClass address = AddressClass( - barangay: overseas?null:selectedBarangay, - id: state.address.id, - category: null, - areaClass: selectedAreaClass!.value, - cityMunicipality: overseas?null:selectedMunicipality, - country: selectedCountry); - if (hasLotandBlock) { - lotNumber = formKey - .currentState!.value['lot_number']; - blockNumber = formKey - .currentState!.value['block_number']; - } - addressLine = formKey - .currentState?.value['address_line']; - final progress = ProgressHUD.of(context); - progress!.showWithText("Loading..."); - context.read().add(UpdateAddress( - address: address, - profileId: widget.profileId, - token: widget.token, - blockNumber: blockNumber != null - ? int.parse(blockNumber) - : null, - categoryId: selectedAddressCategory!.id!, - details: addressLine, - lotNumber: lotNumber != null - ? int.parse(lotNumber) - : null)); - } - }, - child: const Text(submit)), + ), + ], + ); + }), + + + ], ), - const SizedBox( - height: 20, - ), - ], - ), - )), - ), - ); + ), + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState!.saveAndValidate()) { + String? lotNumber; + String? blockNumber; + String? addressLine; + + if (overseas) { + selectedCountry = selectedCountry; + } else { + selectedCountry = Country( + id: 175, + name: 'Philippines', + code: 'PH'); + } + + AddressClass address = AddressClass( + barangay: + overseas ? null : selectedBarangay, + id: state.address.id, + category: null, + areaClass: selectedAreaClass?.value, + cityMunicipality: overseas + ? null + : selectedMunicipality, + country: selectedCountry); + if (hasLotandBlock) { + lotNumber = formKey + .currentState!.value['lot_number']; + blockNumber = formKey + .currentState!.value['block_number']; + } + addressLine = formKey + .currentState?.value['address_line']; + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add(UpdateAddress( + address: address, + profileId: widget.profileId, + token: widget.token, + blockNumber: blockNumber != null + ? int.parse(blockNumber) + : null, + categoryId: selectedAddressCategory!.id!, + details: addressLine, + lotNumber: lotNumber != null + ? int.parse(lotNumber) + : null)); + } + }, + child: const Text(submit)), + ), + + ], + ), + ), + )); } return Placeholder(); }, diff --git a/lib/screens/profile/components/basic_information/address_screen.dart b/lib/screens/profile/components/basic_information/address_screen.dart index f025239..9a0d844 100644 --- a/lib/screens/profile/components/basic_information/address_screen.dart +++ b/lib/screens/profile/components/basic_information/address_screen.dart @@ -3,18 +3,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; -import 'package:fluttericon/font_awesome_icons.dart'; import 'package:unit2/bloc/profile/primary_information/address/address_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; -import 'package:unit2/model/profile/basic_information/adress.dart'; import 'package:unit2/screens/profile/components/basic_information/address/add_modal.dart'; import 'package:unit2/screens/profile/components/basic_information/address/edit_modal.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; -import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; +import 'package:unit2/widgets/Leadings/close_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; @@ -29,14 +27,18 @@ class AddressScreen extends StatelessWidget { String? token; return Scaffold( appBar: AppBar( - title: const Text(adressScreenTitle), + title: context.watch().state is AddAddressState?const Text("Add Address"):context.watch().state is EditAddressState?const Text("Edit Address"):const Text("Addresses"), centerTitle: true, backgroundColor: primary, - actions: [ + actions:(context.watch().state is AddressLoadedState)?[ AddLeading(onPressed: () { context.read().add(ShowAddAddressForm()); }) - ], + ]:(context.watch().state is AddAddressState || context.watch().state is EditAddressState)?[ + CloseLeading(onPressed:() { + context.read().add(LoadAddress()); + }) + ]:[] ), body: ProgressHUD( padding: const EdgeInsets.all(24), @@ -86,13 +88,13 @@ class AddressScreen extends StatelessWidget { if (state is AddressUpdatedState) { if (state.response['success']) { successAlert(context, "Update Successfull!", - state.response['message'], () { + state.response['message'], () { Navigator.of(context).pop(); context.read().add(LoadAddress()); }); } else { errorAlert(context, "Update Failed", - state.response['message'], () { + state.response['message'], () { Navigator.of(context).pop(); context.read().add(LoadAddress()); }); @@ -101,7 +103,7 @@ class AddressScreen extends StatelessWidget { //// Deleted if (state is AddressDeletedState) { if (state.success) { - successAlert(context, "Deletion Successfull", + successAlert(context, "Delete Successfull", "Address has been deleted successfully", () { Navigator.of(context).pop(); context.read().add(LoadAddress()); @@ -131,11 +133,10 @@ class AddressScreen extends StatelessWidget { (BuildContext context, int index) { String? subdivision = state.addresses[index].details ?? ''; - String category = state.addresses[index] - .address!.category!.name!; - if (state.addresses[index].address!.country! - .id == - 175) { + String? category = state.addresses[index] + .address?.category?.name??''; + + if (state.addresses[index].address?.cityMunicipality != null) { barangay = state.addresses[index].address! .barangay != null @@ -182,49 +183,57 @@ class AddressScreen extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - children: [ - Text( - subdivision, - style: - Theme.of(context) - .textTheme - .titleMedium, - ), - const SizedBox( - width: 5, - ), - Text( - category, - style: - Theme.of(context) - .textTheme - .bodySmall, - ) - ], + SizedBox( + width: screenWidth, + child: Row( + + children: [ + Flexible( + child: Text( + subdivision, + style: + Theme.of(context) + .textTheme + .titleMedium!.copyWith(color: primary,fontWeight: FontWeight.w600), + ), + ), + SizedBox( + width: subdivision.isEmpty? 0: 24, + ), + Flexible( + child: Text( + category, + style: + Theme.of(context) + .textTheme + .bodySmall, + ), + ) + ], + ), ), - const Divider(), + const SizedBox( - height: 5, + height: 8, ), Container( child: overseas ? Text( - "COUNTRY: ${state.addresses[index].address!.country!.name!.toUpperCase()}", + "COUNTRY: ${state.addresses[index].address?.country?.name?.toUpperCase()}", textAlign: TextAlign .start, style: Theme.of( context) .textTheme - .labelLarge, + .titleSmall, ) : Text( "$barangay $cityMunicipality, $province, $region", style: Theme.of( context) .textTheme - .labelLarge, + .titleSmall, )), ], )), @@ -241,9 +250,7 @@ class AddressScreen extends StatelessWidget { context); progress!.showWithText( "Loading..."); - }, "Delete?", - "Confirm Delete?"); - context + context .read() .add(DeleteAddress( id: state @@ -253,6 +260,9 @@ class AddressScreen extends StatelessWidget { profileId: profileId .toString(), token: token!)); + }, "Delete?", + "Confirm Delete?"); + } if (value == 1) { bool overseas; @@ -284,18 +294,14 @@ class AddressScreen extends StatelessWidget { }, menuItems: [ popMenuItem( - text: "Edit", + text: "Update", value: 1, icon: Icons.edit), popMenuItem( - text: "Delete", + text: "Remove", value: 2, icon: Icons.delete), - popMenuItem( - text: "Attachment", - value: 3, - icon: - FontAwesome.attach) + ], icon: const Icon( Icons.more_vert, @@ -314,14 +320,14 @@ class AddressScreen extends StatelessWidget { ); }); } else { - const EmptyData( + return const EmptyData( message: "You don't have address added. Please click + to add."); } } if (state is AddressErrorState) { return SomethingWentWrong( - message: state.message, onpressed: () {}); + message: state.message, onpressed: () {context.read().add(LoadAddress());}); } if (state is AddAddressState) { return AddAddressScreen( diff --git a/lib/screens/profile/components/basic_information/citizenship/add_modal.dart b/lib/screens/profile/components/basic_information/citizenship/add_modal.dart new file mode 100644 index 0000000..d1a75f0 --- /dev/null +++ b/lib/screens/profile/components/basic_information/citizenship/add_modal.dart @@ -0,0 +1,16 @@ +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; + +class MyWidget extends StatefulWidget { + const MyWidget({super.key}); + + @override + State createState() => _MyWidgetState(); +} + +class _MyWidgetState extends State { + @override + Widget build(BuildContext context) { + return const Placeholder(); + } +} \ No newline at end of file diff --git a/lib/screens/profile/components/basic_information/citizenship/loading.dart b/lib/screens/profile/components/basic_information/citizenship/loading.dart new file mode 100644 index 0000000..7ae534a --- /dev/null +++ b/lib/screens/profile/components/basic_information/citizenship/loading.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; + +class CitizenshipLoading extends StatelessWidget { + const CitizenshipLoading({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: Container( + height: 120, + width: 120, + decoration: const BoxDecoration( + color: Colors.black87, + borderRadius: BorderRadius.all(Radius.circular(8))), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: const [ + SpinKitFadingCircle(size: 42, color: Colors.white), + SizedBox( + height: 12, + ), + Text( + "Please wait..", + style: TextStyle(color: Colors.white), + ), + ], + ), + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/screens/profile/components/basic_information/citizenship_screen.dart b/lib/screens/profile/components/basic_information/citizenship_screen.dart index f826806..3980950 100644 --- a/lib/screens/profile/components/basic_information/citizenship_screen.dart +++ b/lib/screens/profile/components/basic_information/citizenship_screen.dart @@ -1,46 +1,450 @@ +import 'package:app_popup_menu/app_popup_menu.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:fluttericon/font_awesome5_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:unit2/bloc/profile/primary_information/citizenship/citizenship_bloc.dart'; import 'package:unit2/model/profile/basic_information/citizenship.dart'; +import 'package:unit2/screens/profile/components/basic_information/citizenship/loading.dart'; +import 'package:unit2/theme-data.dart/btn-style.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; +import 'package:unit2/widgets/Leadings/add_leading.dart'; +import 'package:unit2/widgets/error_state.dart'; + +import '../../../../model/location/country.dart'; +import '../../../../theme-data.dart/form-style.dart'; +import '../../../../utils/alerts.dart'; class CitizenShipScreen extends StatefulWidget { - final List citizenships; - const CitizenShipScreen({super.key, required this.citizenships}); + final int profileId; + final String token; + + const CitizenShipScreen( + {super.key, required this.profileId, required this.token}); @override State createState() => _CitizenShipScreenState(); } +List countries = []; +bool naturalBorn = false; +Country? selectedCountry; +final formKey = GlobalKey(); + class _CitizenShipScreenState extends State { @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text(citizenshipScreenTitle),centerTitle: true, - backgroundColor: primary, + final bloc = BlocProvider.of(context); + return ProgressHUD( + padding: const EdgeInsets.all(24), + indicatorWidget: const SpinKitFadingCircle( + color: Colors.white, ), - body: widget.citizenships.isEmpty? - Container( - width: screenWidth, - decoration: BoxDecoration( - color: Colors.grey[200], - borderRadius: const BorderRadius.all(Radius.circular(12))), + backgroundColor: Colors.black87, + child: BlocConsumer( + listener: (context, state) { + if (state is CitizenshipAddedState) { + if (state.responseStatus['success']) { + successAlert( + context, "Add Successfull!", state.responseStatus['message'], + () { + Navigator.of(context).pop(); + context.read().add(const LoadCitizenship()); + }); + } else { + errorAlert(context, "Adding Failed", + "Something went wrong. Please try again.", () { + Navigator.of(context).pop(); + context.read().add(const LoadCitizenship()); + }); + } + } - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("Philippines",style: Theme.of(context).textTheme.titleLarge,), - Text("Filipino",style: Theme.of(context).textTheme.titleSmall,) - ], + if (state is CitizenshipEditedState) { + if (state.responseStatus['success']) { + successAlert(context, "Update Successfull!", + state.responseStatus['message'], () { + Navigator.of(context).pop(); + context.read().add(const LoadCitizenship()); + }); + } else { + errorAlert(context, "Update Failed", + state.responseStatus['message'], () { + Navigator.of(context).pop(); + context.read().add(const LoadCitizenship()); + }); + } + } + ////DELETED STATE + if (state is CitizenshipDeleltedState) { + if (state.succcess) { + successAlert(context, "Deletion Successfull", + "Contact Info has been deleted successfully", () { + Navigator.of(context).pop(); + context.read().add(const LoadCitizenship()); + }); + } else { + errorAlert( + context, "Deletion Failed", "Error deleting Contact Info", + () { + Navigator.of(context).pop(); + context.read().add(const LoadCitizenship()); + }); + } + } + }, + builder: (context, state) { + if (state is CitizenshipLoaded) { + return Scaffold( + appBar: AppBar( + title: const Text(citizenshipScreenTitle), + centerTitle: true, + backgroundColor: primary, + actions: [ + AddLeading(onPressed: () { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text("Add Citizenship"), + content: FormBuilder( + key: formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + FormBuilderDropdown( + initialValue: null, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + items: state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox( + child: Text(country.name!))); + }).toList(), + name: 'country', + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) { + selectedCountry = value; + }, + ), + const SizedBox( + height: 12, + ), + FormBuilderSwitch( + initialValue: naturalBorn, + activeColor: second, + onChanged: (value) { + setState(() { + naturalBorn = value!; + }); + }, + decoration: normalTextFieldStyle( + "Natural Born?", 'Natural Born?'), + name: 'graudated', + title: Text(naturalBorn ? "YES" : "NO"), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: mainBtnStyle(primary, + Colors.transparent, second), + onPressed: () { + if (formKey.currentState! + .saveAndValidate()) { + Navigator.pop(context); + bloc.add(AddCitizenship( + coiuntryId: + selectedCountry!.id!, + naturalBorn: naturalBorn, + profileId: widget.profileId, + token: widget.token)); + } + }, + child: const Text(submit)), + ) + ]), + ), + ); + }); + }) + ], + ), + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric( + vertical: 10, + horizontal: 24, + ), + child: SizedBox( + width: screenWidth, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Philippines", + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: FontWeight.w600, + color: primary), + ), + const SizedBox( + height: 3, + ), + Text( + "Filipino", + style: Theme.of(context).textTheme.bodyMedium, + ), + ]), + ), + ), + ListTile( + leading: const Icon(FontAwesome5.exclamation_circle), + title: Text( + "Your Filipino citizenship is added by default", + style: Theme.of(context).textTheme.bodySmall, + ), + ), + const Divider( + height: 2, + thickness: 1, + ), + SizedBox( + child: state.citizenships.isEmpty + ? Padding( + padding: const EdgeInsets.all(24), + child: Text( + "If you have citizenships other than Filipino, kindly add in this section", + textAlign: TextAlign.center, + style: + Theme.of(context).textTheme.bodySmall, + ), + ) + : SizedBox( + height: screenHeight * .70, + width: double.infinity, + child: ListView( + children: state.citizenships + .map((e) => Card( + child: Row( + children: [ + Expanded( + child: ListTile( + dense: true, + title: Text( + e.country!.name!, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight + .w600, + color: primary), + ), + subtitle: Text( + e.naturalBorn! + ? "Natural Born" + : "Naturalized"), + ), + ), + AppPopupMenu( + offset: + const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + ////delete contact-= = = = = = = = =>> + if (value == 2) { + confirmAlert(context, + () { + context + .read< + CitizenshipBloc>() + .add(DeleteCitizenship( + coiuntryId: e + .country! + .id!, + naturalBorn: + e + .naturalBorn!, + profileId: widget + .profileId, + token: widget + .token)); + }, "Delete?", + "Are you sure you want to delete this contact info?"); + } + if (value == 1) { + ////edit contact-= = = = = = = = =>> - ), - ):Container() - , + showDialog( + context: context, + builder: (context) { + selectedCountry = state + .countries + .firstWhere((element) => + element + .id == + e.country! + .id); + return AlertDialog( + title: const Text( + "Add Citizenship"), + content: + FormBuilder( + key: formKey, + child: Column( + mainAxisSize: + MainAxisSize + .min, + children: [ + DropdownButtonFormField< + Country>( + isExpanded: + true, + value: + selectedCountry, + validator: + FormBuilderValidators.required(errorText: "This field is required"), + items: state + .countries + .map>((Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox(child: Text(country.name!))); + }).toList(), + decoration: normalTextFieldStyle( + "Country*", + "Country"), + onChanged: + (Country? value) { + selectedCountry = + value; + }, + ), + const SizedBox( + height: + 12, + ), + FormBuilderSwitch( + initialValue: + e.naturalBorn, + activeColor: + second, + onChanged: + (value) { + setState(() { + naturalBorn = value!; + }); + }, + decoration: normalTextFieldStyle( + "Natural Born?", + 'Natural Born?'), + name: + 'graudated', + title: Text(naturalBorn + ? "YES" + : "NO"), + ), + const SizedBox( + height: + 12, + ), + SizedBox( + width: + double.infinity, + height: + 60, + child: ElevatedButton( + style: mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + Citizenship newCitizenship = Citizenship(country: selectedCountry, naturalBorn: naturalBorn); + if (formKey.currentState!.saveAndValidate()) { + Navigator.pop(context); + bloc.add(EditCitizenship(citizenship: newCitizenship, profileId: widget.profileId, token: widget.token,oldCountryId: e.country!.id!)); + } + }, + child: const Text(submit)), + ) + ]), + ), + ); + }); + } + }, + menuItems: [ + popMenuItem( + text: "Update", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Remove", + value: 2, + icon: Icons.delete), + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ) + ], + ), + )) + .toList(), + ), + )) + ])); + } + if (state is CitizenshipLoadingState) { + return const Scaffold(body: CitizenshipLoading()); + } + if (state is CitizenshipErrorState) { + return Scaffold( + body: SomethingWentWrong( + message: state.message, onpressed: () {})); + } + return Container(); + }, + ), ); } -} \ No newline at end of file + + PopupMenuItem popMenuItem({String? text, int? value, IconData? icon}) { + return PopupMenuItem( + value: value, + child: Row( + children: [ + Icon( + icon, + ), + const SizedBox( + width: 10, + ), + Text( + text!, + ), + ], + ), + ); + } +} diff --git a/lib/screens/profile/components/basic_information/contact_information/add_modal.dart b/lib/screens/profile/components/basic_information/contact_information/add_modal.dart index 7dc10ff..03f75da 100644 --- a/lib/screens/profile/components/basic_information/contact_information/add_modal.dart +++ b/lib/screens/profile/components/basic_information/contact_information/add_modal.dart @@ -2,16 +2,14 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; -import 'package:fluttericon/entypo_icons.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; -import 'package:mask_text_input_formatter/mask_text_input_formatter.dart'; import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; import 'package:unit2/bloc/profile/primary_information/contact/contact_bloc.dart'; import 'package:unit2/model/profile/basic_information/contact_information.dart'; -import 'package:unit2/screens/unit2/roles/qr_code_scanner.dart/components/custom_switch.dart'; import 'package:unit2/sevices/profile/contact_services.dart'; import 'package:unit2/theme-data.dart/btn-style.dart'; import 'package:unit2/theme-data.dart/form-style.dart'; +import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; import '../../../../../theme-data.dart/colors.dart'; @@ -38,194 +36,227 @@ class _AddContactInformationScreenState bool primaryaContact = false; bool active = false; String? numberMail; + final numberMailController = TextEditingController(); + @override + void dispose() { + numberMailController.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { return BlocBuilder( builder: (context, state) { if (state is ContactAddingState) { - return FormBuilder( - key: formKey, - child: Padding( - padding: - const EdgeInsets.symmetric(vertical: 24, horizontal: 12), - child: Column( - children: [ - const SizedBox( - height: 24, - ), - ////Service Type - FormBuilderDropdown( - validator: FormBuilderValidators.required( - errorText: "This field is required"), - name: "service_type", - items: state.serviceTypes - .map>( - (ServiceType e) { - return DropdownMenuItem( - value: e, child: Text(e.name!)); - }).toList(), - decoration: normalTextFieldStyle("Service Type*", ""), - onChanged: (var service) async { - if (selectedServiceType != service) { - selectedServiceType = service; - setState(() { - callServiceType = true; - }); - commServiceProviders = await ContactService.instance - .getServiceProvider( - serviceTypeId: selectedServiceType!.id!); - setState(() { - setState(() { - callServiceType = false; - }); - }); - } - }), - const SizedBox( - height: 12, - ), - ////Service Provider - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: callServiceType, - child: FormBuilderDropdown( - validator: FormBuilderValidators.required( - errorText: "This field is required"), - name: "Service Provider", - items: commServiceProviders.isEmpty - ? [] - : commServiceProviders - .map>( - (CommService e) { - return DropdownMenuItem( - value: e, child: Text(e.serviceProvider!.agency!.name!)); + return Padding( + padding: const EdgeInsets.symmetric(vertical: 32, horizontal: 24), + child: FormBuilder( + key: formKey, + child: StatefulBuilder(builder: (context, setState) { + return SizedBox( + height: screenHeight * 90, + child: Column( + children: [ + Flexible( + child: ListView( + children: [ + + ////Service Type + FormBuilderDropdown( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "service_type", + items: state.serviceTypes + .map>( + (ServiceType e) { + return DropdownMenuItem( + value: e, child: Text(e.name!)); }).toList(), - decoration: normalTextFieldStyle( - "Communication Service *", ""), - onChanged: (var serviceProvider) { - selectedCommServiceProvider = serviceProvider; - }), - ), + decoration: normalTextFieldStyle("Service Type*", ""), + onChanged: (var service) async { + if (selectedServiceType != service) { + selectedServiceType = service; + setState(() { + callServiceType = true; + selectedCommServiceProvider = null; + numberMailController.text = ""; + }); + + commServiceProviders = await ContactService + .instance + .getServiceProvider( + serviceTypeId: selectedServiceType!.id!); + setState(() { + setState(() { + callServiceType = false; + }); + }); + } + }), + const SizedBox( + height: 12, + ), + ////Service Provider + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: callServiceType, + child: DropdownButtonFormField( + isExpanded: true, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + items: commServiceProviders.isEmpty + ? [] + : commServiceProviders + .map>( + (CommService e) { + return DropdownMenuItem( + value: e, + child: Text(e + .serviceProvider!.agency!.name!)); + }).toList(), + decoration: normalTextFieldStyle( + "Communication Service *", ""), + onChanged: (var serviceProvider) { + selectedCommServiceProvider = serviceProvider; + }), + ), + ), + selectedServiceType != null + ? selectedServiceType?.id == 2 + //// Landline + ? FormBuilderTextField( + controller: numberMailController, + inputFormatters: [landLineFormatter], + name: 'number-mail', + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: normalTextFieldStyle( + "Landline number *", + "(area code) xxx - xxxx"), + ) + : selectedServiceType!.id == 1 || + selectedServiceType!.id == 19 + //// Mobile number + ? FormBuilderTextField( + keyboardType: TextInputType.number, + controller: numberMailController, + name: 'number-mail', + inputFormatters: [mobileFormatter], + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: normalTextFieldStyle( + "Mobile number *", + "+63 (9xx) xxx - xxxx"), + ) + : selectedServiceType!.id == 4 + ////Social Media + ? FormBuilderTextField( + controller: numberMailController, + name: 'number-mail', + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + decoration: normalTextFieldStyle( + "Account ID / Username *", ""), + ) + : selectedServiceType!.id == 3 + ////Email Address + ? FormBuilderTextField( + controller: numberMailController, + name: 'number-mail', + validator: FormBuilderValidators + .compose([ + FormBuilderValidators.email( + errorText: + "Input vaild email"), + FormBuilderValidators.required( + errorText: + "This field is required") + ]), + decoration: normalTextFieldStyle( + "Email Address*", ""), + ) + : Container() + : const SizedBox(), + SizedBox( + height: selectedServiceType != null ? 12 : 0, + ), + //// Primary + FormBuilderSwitch( + initialValue: primaryaContact, + activeColor: second, + onChanged: (value) { + setState(() { + primaryaContact = value!; + }); + }, + decoration: + normalTextFieldStyle("Primary?", 'Primary?'), + name: 'overseas', + title: Text(primaryaContact ? "YES" : "NO"), + ), + //// Active + const SizedBox( + height: 12, + ), + FormBuilderSwitch( + initialValue: primaryaContact, + activeColor: second, + onChanged: (value) { + setState(() { + active = value!; + }); + }, + decoration: normalTextFieldStyle("Active?", ''), + name: 'overseas', + title: Text(active ? "YES" : "NO"), + ), + + + ], + ), + + ), + SizedBox( + height: 60, + width: double.infinity, + child: ElevatedButton( + onPressed: () { + if (formKey.currentState!.saveAndValidate()) { + numberMail = + formKey.currentState!.value['number-mail']; + CommService commService = + selectedCommServiceProvider!; + ContactInfo contactInfo = ContactInfo( + id: null, + active: active, + primary: primaryaContact, + numbermail: numberMail, + commService: commService); + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add( + AddContactInformation( + contactInfo: contactInfo, + profileId: widget.profileId, + token: widget.token)); + } + }, + style: + mainBtnStyle(primary, Colors.transparent, second), + child: const Text(submit), + ), + ), + + ], ), - selectedServiceType != null - ? selectedServiceType?.id == 2 - //// Landline - ? FormBuilderTextField( - inputFormatters: [landLineFormatter], - name: 'number-mail', - validator: FormBuilderValidators.required( - errorText: "This field is required"), - decoration: normalTextFieldStyle( - "Landline number *", - "(area code) xxx - xxxx"), - ) - : selectedServiceType!.id == 1 || - selectedServiceType!.id == 19 - //// Mobile number - ? FormBuilderTextField( - name: 'number-mail', - inputFormatters: [mobileFormatter], - validator: FormBuilderValidators.required( - errorText: "This field is required"), - decoration: normalTextFieldStyle( - "Mobile number *", - "+63 (9xx) xxx - xxxx"), - ) - : selectedServiceType!.id == 4 - ////Social Media - ? FormBuilderTextField( - name: 'number-mail', - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - decoration: normalTextFieldStyle( - "Account ID / Username *", ""), - ) - : selectedServiceType!.id == 3 - ////Email Address - ? FormBuilderTextField( - name: 'number-mail', - validator: - FormBuilderValidators.compose([ - FormBuilderValidators.email( - errorText: - "Input vaild email"), - FormBuilderValidators.required( - errorText: - "This field is required") - ]), - decoration: normalTextFieldStyle( - "Email Address*", ""), - ) - : Container() - : const SizedBox(), - SizedBox( - height: selectedServiceType != null ? 12 : 0, - ), - //// Primary - FormBuilderSwitch( - initialValue: primaryaContact, - activeColor: second, - onChanged: (value) { - setState(() { - primaryaContact = value!; - }); - }, - decoration: normalTextFieldStyle("Primary?", 'Primary?'), - name: 'overseas', - title: Text(primaryaContact ? "YES" : "NO"), - ), - //// Active - const SizedBox( - height: 12, - ), - FormBuilderSwitch( - initialValue: primaryaContact, - activeColor: second, - onChanged: (value) { - setState(() { - active = value!; - }); - }, - decoration: normalTextFieldStyle("Active?", ''), - name: 'overseas', - title: Text(active ? "YES" : "NO"), - ), - const Expanded(child: SizedBox()), - SizedBox( - height: 60, - width: double.infinity, - child: ElevatedButton( - onPressed: () { - if (formKey.currentState!.saveAndValidate()) { - - numberMail = - formKey.currentState!.value['number-mail']; - CommService commService = selectedCommServiceProvider!; - ContactInfo contactInfo = ContactInfo( - id: null, - active: active, - primary: primaryaContact, - numbermail: numberMail, - commService: commService); - final progress = ProgressHUD.of(context); - progress!.showWithText("Loading..."); - context.read().add(AddContactInformation(contactInfo: contactInfo, profileId: widget.profileId, token: widget.token)); - } - }, - style: - mainBtnStyle(primary, Colors.transparent, second), - child: const Text(submit), - ), - ) - ], - ), - )); + ); + })), + ); } return Container(); }, diff --git a/lib/screens/profile/components/basic_information/contact_information/edit_modal.dart b/lib/screens/profile/components/basic_information/contact_information/edit_modal.dart index e2c10a0..2d8166b 100644 --- a/lib/screens/profile/components/basic_information/contact_information/edit_modal.dart +++ b/lib/screens/profile/components/basic_information/contact_information/edit_modal.dart @@ -2,18 +2,15 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; -import 'package:fluttericon/entypo_icons.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:mask_text_input_formatter/mask_text_input_formatter.dart'; import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; import 'package:unit2/bloc/profile/primary_information/contact/contact_bloc.dart'; import 'package:unit2/model/profile/basic_information/contact_information.dart'; -import 'package:unit2/screens/unit2/roles/qr_code_scanner.dart/components/custom_switch.dart'; import 'package:unit2/sevices/profile/contact_services.dart'; import 'package:unit2/theme-data.dart/btn-style.dart'; import 'package:unit2/theme-data.dart/form-style.dart'; import 'package:unit2/utils/text_container.dart'; - import '../../../../../theme-data.dart/colors.dart'; class EditContactInformationScreen extends StatefulWidget { @@ -38,18 +35,25 @@ class _EditContactInformationScreenState bool? primaryaContact; bool? active; + var mobileFormatter = MaskTextInputFormatter( mask: "+63 (###) ###-####", filter: {"#": RegExp(r"^[1-9][0-9]*$")}, type: MaskAutoCompletionType.lazy, initialText: "0"); - var landLineFormatter = MaskTextInputFormatter( + var landLineFormatter = MaskTextInputFormatter( mask: "(###) ###-###", filter: {"#": RegExp(r"^[0-9]")}, type: MaskAutoCompletionType.lazy, initialText: "0"); + final numberMailController = TextEditingController(); + @override + void dispose() { + numberMailController.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { return BlocBuilder( @@ -60,213 +64,224 @@ class _EditContactInformationScreenState commServiceProviders = state.commServiceProviders; primaryaContact = state.contactInfo.primary!; active = state.contactInfo.active!; - return FormBuilder( - key: formKey, - child: Padding( - padding: - const EdgeInsets.symmetric(vertical: 24, horizontal: 12), + numberMailController.text = state.contactInfo.numbermail!; + return Padding( + padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 24), + child: FormBuilder( + key: formKey, child: Column( children: [ - const SizedBox( - height: 24, - ), - StatefulBuilder(builder: (context, setState) { - return Column(children: [ - ////Service Type - DropdownButtonFormField( - validator: FormBuilderValidators.required( - errorText: "This field is required"), - value: selectedServiceType, - items: state.serviceTypes - .map>( - (ServiceType e) { - return DropdownMenuItem( - value: e, child: Text(e.name!)); - }).toList(), - decoration: - normalTextFieldStyle("Service Type*", ""), - onChanged: (var service) async { - if (selectedServiceType!.id != service!.id) { - selectedServiceType = service; - setState(() { - callServiceType = true; - }); - commServiceProviders = await ContactService.instance - .getServiceProvider( - serviceTypeId: - selectedServiceType!.id!); - selectedCommProvider = null; - setState(() { - setState(() { - callServiceType = false; - }); - }); - } - }), - const SizedBox( - height: 12, - ), - ////Service Provider - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: callServiceType, - child: DropdownButtonFormField( - value: selectedCommProvider, - validator: FormBuilderValidators.required( - errorText: "This field is required"), - items: commServiceProviders.isEmpty - ? [] - : commServiceProviders - .map>( - (CommService e) { - return DropdownMenuItem< - CommService>( - value: e, - child: Text(e.serviceProvider!.agency!.name!)); - }).toList(), - decoration: normalTextFieldStyle( - "Communication Service *", ""), - onChanged: (var commServiceProvider) { - selectedCommProvider = commServiceProvider; - }), - ), - ), - selectedServiceType != null - ? selectedServiceType?.id == 2 - //// Landline - ? FormBuilderTextField( - name: 'number-mail', - initialValue: state.contactInfo.numbermail, - inputFormatters: [landLineFormatter], - validator: FormBuilderValidators.required( - errorText: "This field is required"), - decoration: normalTextFieldStyle( - "Landline number *", - "(area code) xxx - xxxx"), - ) - : selectedServiceType!.id == 1 || - selectedServiceType!.id == 19 - //// Mobile number - ? FormBuilderTextField( - name: 'number-mail', - inputFormatters: [mobileFormatter], - initialValue: - state.contactInfo.numbermail, - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - decoration: normalTextFieldStyle( - "Mobile number *", - "+63 (9xx) xxx - xxxx"), - ) - : selectedServiceType!.id == 4 - ////Social Media - ? FormBuilderTextField( - name: 'number-mail', - initialValue: - state.contactInfo.numbermail, - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - decoration: normalTextFieldStyle( - "Account ID / Username *", ""), - ) - : selectedServiceType!.id == 3 - ////Email Address - ? FormBuilderTextField( - name: 'number-mail', - initialValue: state - .contactInfo.numbermail, - validator: FormBuilderValidators - .compose([ - FormBuilderValidators.email( - errorText: - "Input vaild email"), + Flexible( + child: ListView( + children: [ + StatefulBuilder(builder: (context, setState) { + return Column(children: [ + ////Service Type + DropdownButtonFormField( + isExpanded: true, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + value: selectedServiceType, + items: state.serviceTypes + .map>( + (ServiceType e) { + return DropdownMenuItem( + value: e, child: Text(e.name!)); + }).toList(), + decoration: + normalTextFieldStyle("Service Type*", ""), + onChanged: (var service) async { + if (selectedServiceType!.id != service!.id) { + selectedServiceType = service; + setState(() { + callServiceType = true; + callServiceType = true; + + numberMailController.text = ""; + }); + commServiceProviders = await ContactService + .instance + .getServiceProvider( + serviceTypeId: + selectedServiceType!.id!); + selectedCommProvider = null; + setState(() { + setState(() { + callServiceType = false; + }); + }); + } + }), + const SizedBox( + height: 12, + ), + ////Service Provider + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: callServiceType, + child: DropdownButtonFormField( + isExpanded: true, + value: selectedCommProvider, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + items: commServiceProviders.isEmpty + ? [] + : commServiceProviders + .map>( + (CommService e) { + return DropdownMenuItem( + value: e, + child: Text(e.serviceProvider! + .agency!.name!)); + }).toList(), + decoration: normalTextFieldStyle( + "Communication Service *", ""), + onChanged: (var commServiceProvider) { + selectedCommProvider = commServiceProvider; + }), + ), + ), + selectedServiceType != null + ? selectedServiceType?.id == 2 + //// Landline + ? FormBuilderTextField( + controller: numberMailController, + name: 'number-mail', + + inputFormatters: [landLineFormatter], + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: normalTextFieldStyle( + "Landline number *", + "(area code) xxx - xxxx"), + ) + : selectedServiceType!.id == 1 || + selectedServiceType!.id == 19 + //// Mobile number + ? FormBuilderTextField( + controller: numberMailController, + name: 'number-mail', + inputFormatters: [mobileFormatter], + + validator: FormBuilderValidators.required( errorText: - "This field is required") - ]), - decoration: - normalTextFieldStyle( - "Email Address*", ""), - ) - : Container() - : const SizedBox(), - ]); - }), - SizedBox( - height: selectedServiceType != null ? 12 : 0, - ), - //// Primary - StatefulBuilder(builder: (context, setState) { - return FormBuilderSwitch( - initialValue: primaryaContact, - activeColor: second, - onChanged: (value) { - setState(() { - primaryaContact = value; - }); - }, - decoration: normalTextFieldStyle("", ''), - name: 'overseas', - title: const Text("Primary ?"), - ); - }), - //// Active - const SizedBox( - height: 12, - ), - StatefulBuilder(builder: (context, setState) { - return FormBuilderSwitch( - initialValue: active, - activeColor: second, - onChanged: (value) { - setState(() { - active = value; - }); - }, - decoration: normalTextFieldStyle("", ''), - name: 'overseas', - title: const Text("Active ?"), - ); - }), - const Expanded(child: SizedBox()), - SizedBox( - height: 60, - width: double.infinity, - child: ElevatedButton( - onPressed: () { - if (formKey.currentState!.saveAndValidate()) { - numberMail = - formKey.currentState!.value['number-mail']; - CommService commService = selectedCommProvider!; - ContactInfo contactInfo = ContactInfo( - id: state.contactInfo.id, - active: active, - primary: primaryaContact, - numbermail: numberMail, - commService: commService); - final progress = ProgressHUD.of(context); - progress!.showWithText("Loading..."); - context.read().add( - EditContactInformation( - contactInfo: contactInfo, - profileId: widget.profileId, - token: widget.token)); - } - }, - style: - mainBtnStyle(primary, Colors.transparent, second), - child: const Text(submit), + "This field is required"), + decoration: normalTextFieldStyle( + "Mobile number *", + "+63 (9xx) xxx - xxxx"), + ) + : selectedServiceType!.id == 4 + ////Social Media + ? FormBuilderTextField( + controller: numberMailController, + name: 'number-mail', + + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + decoration: normalTextFieldStyle( + "Account ID / Username *", ""), + ) + : selectedServiceType!.id == 3 + ////Email Address + ? FormBuilderTextField( + controller: numberMailController, + name: 'number-mail', + + validator: FormBuilderValidators + .compose([ + FormBuilderValidators.email( + errorText: + "Input vaild email"), + FormBuilderValidators.required( + errorText: + "This field is required") + ]), + decoration: + normalTextFieldStyle( + "Email Address*", ""), + ) + : Container() + : const SizedBox(), + ]); + }), + SizedBox( + height: selectedServiceType != null ? 12 : 0, + ), + //// Primary + StatefulBuilder(builder: (context, setState) { + return FormBuilderSwitch( + initialValue: primaryaContact, + activeColor: second, + onChanged: (value) { + setState(() { + primaryaContact = value; + }); + }, + decoration: normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Primary ?"), + ); + }), + //// Active + const SizedBox( + height: 12, + ), + StatefulBuilder(builder: (context, setState) { + return FormBuilderSwitch( + initialValue: active, + activeColor: second, + onChanged: (value) { + setState(() { + active = value; + }); + }, + decoration: normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Active ?"), + ); + }), + + + ], ), - ) + ), + SizedBox( + height: 60, + width: double.infinity, + child: ElevatedButton( + onPressed: () { + if (formKey.currentState!.saveAndValidate()) { + numberMail = + formKey.currentState!.value['number-mail']; + CommService commService = selectedCommProvider!; + ContactInfo contactInfo = ContactInfo( + id: state.contactInfo.id, + active: active, + primary: primaryaContact, + numbermail: numberMail, + commService: commService); + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add( + EditContactInformation( + contactInfo: contactInfo, + profileId: widget.profileId, + token: widget.token)); + } + }, + style: + mainBtnStyle(primary, Colors.transparent, second), + child: const Text(submit), + ), + ) ], - ), - )); + )), + ); } return Container(); }, diff --git a/lib/screens/profile/components/basic_information/contact_information_screen.dart b/lib/screens/profile/components/basic_information/contact_information_screen.dart index b572c86..4f4e8bd 100644 --- a/lib/screens/profile/components/basic_information/contact_information_screen.dart +++ b/lib/screens/profile/components/basic_information/contact_information_screen.dart @@ -11,7 +11,7 @@ import 'package:unit2/screens/profile/components/basic_information/contact_infor import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/alerts.dart'; -import 'package:unit2/utils/text_container.dart'; +import 'package:unit2/utils/global.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/Leadings/close_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; @@ -29,21 +29,30 @@ class ContactInformationScreen extends StatelessWidget { return SafeArea( child: Scaffold( appBar: AppBar( - title: const Text(contactScreenTitle), - centerTitle: true, - backgroundColor: primary, - actions: context.watch().state is ContactLoadedState? [ - AddLeading(onPressed: () { - context.read().add(ShowAddForm()); - }) - ]:( context.watch().state is ContactAddingState || context.watch().state is ContactEditingState)?[ - CloseLeading(onPressed: (){ - context.read().add(LoadContacts()); - }) - ]:[] - ), + title: context.watch().state is ContactAddingState + ? const Text("Add Contact") + : context.watch().state is ContactEditingState + ? const Text("Edit Contact") + : const Text("Contact Information"), + centerTitle: true, + backgroundColor: primary, + actions: context.watch().state is ContactLoadedState + ? [ + AddLeading(onPressed: () { + context.read().add(ShowAddForm()); + }) + ] + : (context.watch().state is ContactAddingState || + context.watch().state + is ContactEditingState) + ? [ + CloseLeading(onPressed: () { + context.read().add(LoadContacts()); + }) + ] + : []), body: ProgressHUD( - padding: const EdgeInsets.all(24), + padding: const EdgeInsets.symmetric(vertical: 32, horizontal: 24), backgroundColor: Colors.black87, indicatorWidget: const SpinKitFadingCircle(color: Colors.white), child: BlocBuilder( @@ -73,19 +82,21 @@ class ContactInformationScreen extends StatelessWidget { ////ADDED CONTACT STATE if (state is ContactAddedState) { if (state.response['success']) { - successAlert(context, "Update Successfull!", + successAlert(context, "Adding Successfull!", state.response['message'], () { Navigator.of(context).pop(); - context.read().add(LoadContacts( - )); + context + .read() + .add(LoadContacts()); }); } else { errorAlert(context, "Update Failed", "Something went wrong. Please try again.", () { Navigator.of(context).pop(); - context.read().add(LoadContacts( - )); + context + .read() + .add(LoadContacts()); }); } } @@ -96,16 +107,18 @@ class ContactInformationScreen extends StatelessWidget { successAlert(context, "Update Successfull!", state.response['message'], () { Navigator.of(context).pop(); - context.read().add(LoadContacts( - )); + context + .read() + .add(LoadContacts()); }); } else { errorAlert(context, "Update Failed", "Something went wrong. Please try again.", () { Navigator.of(context).pop(); - context.read().add(LoadContacts( - )); + context + .read() + .add(LoadContacts()); }); } } @@ -117,15 +130,17 @@ class ContactInformationScreen extends StatelessWidget { "Contact Info has been deleted successfully", () { Navigator.of(context).pop(); - context.read().add(LoadContacts( - )); + context + .read() + .add(LoadContacts()); }); } else { errorAlert(context, "Deletion Failed", "Error deleting Contact Info", () { Navigator.of(context).pop(); - context.read().add(LoadContacts( - )); + context + .read() + .add(LoadContacts()); }); } } @@ -155,7 +170,8 @@ class ContactInformationScreen extends StatelessWidget { children: [ Container( decoration: box1(), - padding: const EdgeInsets.fromLTRB(12, 12, 0, 12), + padding: const EdgeInsets.fromLTRB( + 12, 12, 0, 12), child: Row( children: [ Expanded( @@ -177,70 +193,76 @@ class ContactInformationScreen extends StatelessWidget { .textTheme .titleMedium! .copyWith( - fontWeight: - FontWeight.w500)), + fontWeight: FontWeight + .w500, + color: + primary)), ), - state.contactInformation[index] - .active == - true - ? const Badge( - backgroundColor: - Colors - .green, - label: Text( - "Active", - ), - ) - : const SizedBox(), - const SizedBox( - width: 5), - state.contactInformation[index] - .primary == - true - ? const Badge( - backgroundColor: - Colors - .blue, - label: Text( - "Primary"), - ) - : const SizedBox() ], ), - const Divider(), const SizedBox( - height: 5, + height: 8, ), - Row( - children: [ - Flexible( - flex: 2, - child: Text( - commService - .toString().toUpperCase(), - style: Theme.of( - context) - .textTheme - .titleSmall, + SizedBox( + width: screenWidth, + child: Row( + children: [ + Expanded( + child: Text( + commService + .toString() + .toUpperCase(), + style: Theme.of( + context) + .textTheme + .titleSmall, + ), ), - ), - const SizedBox( - - child: Text(" - "), - ), - Flexible( - flex: 1, - child: Text(state - .contactInformation[ - index] - .commService! - .serviceProvider! - .agency! - .name - .toString()), - ), - ], + state.contactInformation[index] + .active == + true + ? const Badge( + backgroundColor: + Colors + .green, + label: + Text( + "Active", + ), + ) + : const Badge( + backgroundColor: + Colors + .red, + label: + Text( + "Inactive", + ), + ), + const SizedBox( + width: 5), + state.contactInformation[index] + .primary == + true + ? const Badge( + backgroundColor: + Colors + .blue, + label: Text( + "Primary"), + ) + : const SizedBox() + ], + ), ), + Text(state + .contactInformation[ + index] + .commService! + .serviceProvider! + .agency! + .name + .toString()), ]), ), AppPopupMenu( @@ -250,25 +272,22 @@ class ContactInformationScreen extends StatelessWidget { onSelected: (value) { ////delete contact-= = = = = = = = =>> if (value == 2) { - final progress = - ProgressHUD.of( - context); - progress!.showWithText( - "Loading..."); - confirmAlert( - context, - () => context - .read< - ContactBloc>() - .add(DeleteContactInformation( - contactInfo: - state.contactInformation[ - index], - profileId: - profileId, - token: - token)), - "Delete?", + confirmAlert(context, () { + final progress = + ProgressHUD.of( + context); + progress!.showWithText( + "Loading..."); + context + .read() + .add(DeleteContactInformation( + contactInfo: + state.contactInformation[ + index], + profileId: + profileId, + token: token)); + }, "Delete?", "Are you sure you want to delete this contact info?"); } if (value == 1) { @@ -288,11 +307,11 @@ class ContactInformationScreen extends StatelessWidget { }, menuItems: [ popMenuItem( - text: "Edit", + text: "Update", value: 1, icon: Icons.edit), popMenuItem( - text: "Delete", + text: "Remove", value: 2, icon: Icons.delete), ], @@ -329,8 +348,11 @@ class ContactInformationScreen extends StatelessWidget { } if (state is ContactErrorState) { return SomethingWentWrong( - message: state.message, onpressed: () { - context.read().add(LoadContacts()); + message: state.message, + onpressed: () { + context + .read() + .add(LoadContacts()); }); } return Container(); diff --git a/lib/screens/profile/components/basic_information/edit_basic_info_modal.dart b/lib/screens/profile/components/basic_information/edit_basic_info_modal.dart index 2cb6160..6d33c67 100644 --- a/lib/screens/profile/components/basic_information/edit_basic_info_modal.dart +++ b/lib/screens/profile/components/basic_information/edit_basic_info_modal.dart @@ -1,7 +1,5 @@ import 'package:date_time_picker/date_time_picker.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; @@ -9,12 +7,12 @@ import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/model/profile/basic_information/primary-information.dart'; import 'package:unit2/screens/profile/components/basic_information/profile_other_info.dart'; -import 'package:unit2/screens/unit2/roles/qr_code_scanner.dart/components/save_settings.dart'; import 'package:unit2/theme-data.dart/btn-style.dart'; - +import 'package:unit2/utils/formatters.dart'; import '../../../../theme-data.dart/colors.dart'; import '../../../../theme-data.dart/form-style.dart'; import '../../../../utils/global.dart'; +import '../../../../utils/validators.dart'; class EditBasicProfileInfoScreen extends StatefulWidget { final int profileId; @@ -26,7 +24,6 @@ class EditBasicProfileInfoScreen extends StatefulWidget { State createState() => _EditBasicProfileInfoScreenState(); } - final bdayController = TextEditingController(); ProfileOtherInfo? selectedIndigency; ProfileOtherInfo? selectedEthnicity; @@ -39,8 +36,13 @@ String? selectedStatus; String? selectedExtension; final _formKey = GlobalKey(); + class _EditBasicProfileInfoScreenState extends State { + @override + void dispose() { + super.dispose(); + } @override Widget build(BuildContext context) { return BlocBuilder( @@ -50,7 +52,7 @@ class _EditBasicProfileInfoScreenState selectedSex = state.sexes.firstWhere((element) => element.toLowerCase() == state.primaryInformation.sex!.toLowerCase()); - if (state.primaryInformation.bloodType != null) { + if (state.primaryInformation.bloodType != null && state.primaryInformation.bloodType != "N/A") { selectedBloodType = state.bloodTypes.firstWhere((element) => element.toLowerCase() == state.primaryInformation.bloodType?.toLowerCase()); @@ -113,410 +115,434 @@ class _EditBasicProfileInfoScreenState key: _formKey, child: Column( children: [ - const SizedBox( - height: 28, - ), - FormBuilderTextField( - name: "lastname", - initialValue: state.primaryInformation.lastName, - decoration: normalTextFieldStyle("Last name", ""), - validator: FormBuilderValidators.required( - errorText: "This field is required"), - ), - const SizedBox( - height: 15, - ), - FormBuilderTextField( - name: "firstname", - initialValue: state.primaryInformation.firstName, - decoration: normalTextFieldStyle("First name", ""), - validator: FormBuilderValidators.required( - errorText: "This field is required"), - ), - const SizedBox( - height: 15, - ), - SizedBox( - width: screenWidth, - child: Row(children: [ - Flexible( - flex: 2, - child: FormBuilderTextField( - name: "middlename", - initialValue: state.primaryInformation.middleName!, - decoration: normalTextFieldStyle("Middle name", ""), + Flexible( + child: ListView( + children: [ + const SizedBox( + height: 8, ), - ), - const SizedBox( - width: 10, - ), - Flexible( - flex: 1, - //// name extension - child: DropdownButtonFormField( - value: selectedExtension, - decoration: - normalTextFieldStyle("Name Extension", ""), + FormBuilderTextField( + name: "lastname", + inputFormatters: [ + UpperCaseTextFormatter() + ], + initialValue: state.primaryInformation.lastName, + decoration: normalTextFieldStyle("Last name *", ""), validator: FormBuilderValidators.required( errorText: "This field is required"), - items: state.extensions - .map((element) => DropdownMenuItem( - value: element, child: Text(element))) - .toList(), - onChanged: (e) { - print(e); - print(selectedExtension); - selectedExtension = e; - }, ), - ) - ]), - ), - const SizedBox( - height: 15, - ), - SizedBox( - width: screenWidth, - child: Row(children: [ - ////Bday - Flexible( - flex: 1, - child: DateTimePicker( - controller: bdayController, - use24HourFormat: false, + const SizedBox( + height: 15, + ), + FormBuilderTextField( + name: "firstname", + inputFormatters: [ + UpperCaseTextFormatter() + ], + initialValue: state.primaryInformation.firstName, + decoration: normalTextFieldStyle("First name *", ""), validator: FormBuilderValidators.required( errorText: "This field is required"), - timeHintText: "Birthdate", - decoration: - normalTextFieldStyle("Birthdate *", "*").copyWith( - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - )), - firstDate: DateTime(1970), - lastDate: DateTime(2100), - icon: const Icon(Icons.date_range), ), - ), - const SizedBox( - width: 10, - ), - - ////sex - Flexible( - flex: 1, - child: FormBuilderDropdown( - initialValue: selectedSex, - decoration: normalTextFieldStyle("Sex", ""), - name: "sex", - validator: FormBuilderValidators.required( - errorText: "This field is required"), - items: state.sexes - .map((element) => DropdownMenuItem( - value: element, child: Text(element))) - .toList(), - onChanged: (e) {}, + const SizedBox( + height: 15, ), - ) - ]), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 2, + child: FormBuilderTextField( + inputFormatters: [ + UpperCaseTextFormatter() + ], + name: "middlename", + initialValue: state.primaryInformation.middleName!, + decoration: normalTextFieldStyle("Middle name", ""), + ), + ), + const SizedBox( + width: 10, + ), + Flexible( + flex: 1, + //// name extension + child: DropdownButtonFormField( + value: selectedExtension, + decoration: + normalTextFieldStyle("Name Extension", ""), + + items: state.extensions + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + + selectedExtension = e; + }, + ), + ) + ]), + ), + const SizedBox( + height: 15, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + ////Bday + Flexible( + flex: 1, + child: DateTimePicker( + controller: bdayController, + use24HourFormat: false, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + timeHintText: "Birthdate", + decoration: + normalTextFieldStyle("Birthdate *", "*").copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + firstDate: DateTime(1970), + lastDate: DateTime(2100), + icon: const Icon(Icons.date_range), + ), + ), + const SizedBox( + width: 10, + ), + + ////sex + Flexible( + flex: 1, + child: FormBuilderDropdown( + initialValue: selectedSex, + decoration: normalTextFieldStyle("Sex *", ""), + name: "sex", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + items: state.sexes + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedSex= e; + }, + ), + ) + ]), + ), + const SizedBox( + height: 15, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + ////blood type + Flexible( + flex: 1, + child: FormBuilderDropdown( + initialValue: selectedBloodType, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: normalTextFieldStyle("Blood type *", ""), + name: "bloodtype", + items: state.bloodTypes + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedBloodType = e; + }, + ), + ), + const SizedBox( + width: 10, + ), + //// civil status + Flexible( + flex: 1, + child: FormBuilderDropdown( + initialValue: selectedStatus, + decoration: normalTextFieldStyle("Civil status *", ""), + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "extension", + items: state.civilStatus + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedStatus = e; + }, + ), + ), + const SizedBox( + width: 10, + ), + //// gender + Flexible( + flex: 1, + child: DropdownButtonFormField( + isExpanded: true, + value: selectedGender, + decoration: normalTextFieldStyle("Gender", ""), + items: state.genders + .map((element) => + DropdownMenuItem( + value: element, + child: Text(element.name!))) + .toList(), + onChanged: (e) { + selectedGender = e; + }, + ), + ), + ]), + ), + const SizedBox( + height: 15, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + name: "height", + validator: numericRequired, + initialValue: state.primaryInformation.heightM + .toString() + .toString(), + decoration: normalTextFieldStyle("Height (Meter) *", ""), + ), + ), + const SizedBox( + width: 10, + ), + Flexible( + flex: 1, + + child: FormBuilderTextField( + validator: numericRequired, + name: "weigth", + initialValue: + state.primaryInformation.weightKg!.toString(), + decoration: normalTextFieldStyle("Weight (Kg) *", ""), + ), + ), + ]), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + name: "prefix", + initialValue: state.primaryInformation.titlePrefix + .toString() + .toString(), + decoration: normalTextFieldStyle( + "Title Prefix", "Dr.,Atty.,Engr."), + ), + ), + const SizedBox( + width: 10, + ), + Flexible( + flex: 1, + child: FormBuilderTextField( + name: "suffix", + initialValue: state.primaryInformation.titleSuffix, + decoration: normalTextFieldStyle( + "Title Suffix", "PhD.,MD.,MS.,CE"), + ), + ), + ]), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + ////Indigency + Flexible( + flex: 1, + child: DropdownButtonFormField( + isExpanded: true, + value: selectedIndigency, + decoration: normalTextFieldStyle("Indigency", ""), + items: state.indigenous + .map((element) => + DropdownMenuItem( + value: element, + child: Text(element.name!))) + .toList(), + onChanged: (e) { + selectedIndigency = e; + }, + ), + ), + const SizedBox( + width: 10, + ), + ////Ethnicity + Flexible( + flex: 1, + child: DropdownButtonFormField( + isExpanded: true, + value: selectedEthnicity, + decoration: normalTextFieldStyle("Ethnicity", ""), + items: state.ethnicity + .map((element) => + DropdownMenuItem( + value: element, + child: Text(element.name!))) + .toList(), + onChanged: (e) { + selectedEthnicity = e; + }, + ), + ), + ]), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + child: Row(children: [ + ////religion + Flexible( + flex: 1, + child: DropdownButtonFormField( + isExpanded: true, + value: selectedReligion, + decoration: normalTextFieldStyle("Religion", ""), + items: state.religion + .map((element) => + DropdownMenuItem( + value: element, + child: Text(element.name!))) + .toList(), + onChanged: (e) { + selectedReligion = e; + }, + ), + ), + const SizedBox( + width: 10, + ), + ////disabilty + Flexible( + flex: 1, + child: DropdownButtonFormField( + isExpanded: true, + value: selectedDisability, + decoration: normalTextFieldStyle("Disability", ""), + items: state.disability + .map((element) => + DropdownMenuItem( + value: element, + child: Text(element.name!))) + .toList(), + onChanged: (e) { + selectedDisability = e; + }, + ), + ), + ]), + ), + const SizedBox( + height: 24, + ), + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: + mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + if (_formKey.currentState!.saveAndValidate()) { + String lastName = + _formKey.currentState!.value['lastname']; + String firstName = + _formKey.currentState!.value['firstname']; + String? middleName = + _formKey.currentState?.value['middlename']; + String? pref = + _formKey.currentState?.value['prefix']; + String suf = _formKey.currentState?.value['suffix']; + DateTime birthdate = + DateTime.parse(bdayController.text); + double? hM = + _formKey.currentState!.value['height'] == null + ? null + : double.tryParse( + _formKey.currentState?.value['height']); + double? wKg = + _formKey.currentState!.value['weigth'] == null + ? null + : double.tryParse( + _formKey.currentState?.value['weigth']); + Profile primaryInformation = Profile( + id: state.primaryInformation.id, + lastName: lastName, + firstName: firstName, + middleName: middleName, + nameExtension: selectedExtension, + sex: selectedSex, + birthdate: birthdate, + civilStatus: selectedStatus, + bloodType: selectedBloodType =="NONE"?null:selectedBloodType, + heightM: hM, + weightKg: wKg, + photoPath: state.primaryInformation.photoPath, + esigPath: state.primaryInformation.esigPath, + maidenName: state.primaryInformation.maidenName, + deceased: state.primaryInformation.deceased, + uuidQrcode: state.primaryInformation.uuidQrcode, + titlePrefix: pref, + titleSuffix: suf, + showTitleId: + state.primaryInformation.showTitleId, + ethnicity: selectedEthnicity?.name, + disability: selectedDisability?.name, + gender: selectedGender?.name, + religion: selectedReligion?.name, + ip: selectedIndigency?.name); + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add( + EditBasicProfileInformation( + disabilityId: selectedDisability?.id, + ethnicityId: selectedDisability?.id, + genderId: selectedGender?.id, + indigencyId: selectedIndigency?.id, + profileId: widget.profileId, + profileInformation: primaryInformation, + religionId: selectedReligion?.id, + token: widget.token)); + } + }, + child: const Text("Submit")), + ) + ], + ), ), - const SizedBox( - height: 15, - ), - SizedBox( - width: screenWidth, - child: Row(children: [ - ////blood type - Flexible( - flex: 1, - child: FormBuilderDropdown( - initialValue: selectedBloodType, - decoration: normalTextFieldStyle("Blood type", ""), - name: "bloodtype", - items: state.bloodTypes - .map((element) => DropdownMenuItem( - value: element, child: Text(element))) - .toList(), - onChanged: (e) {}, - ), - ), - const SizedBox( - width: 10, - ), - //// civil status - Flexible( - flex: 1, - child: FormBuilderDropdown( - initialValue: selectedStatus, - decoration: normalTextFieldStyle("Civil status", ""), - name: "extension", - items: state.civilStatus - .map((element) => DropdownMenuItem( - value: element, child: Text(element))) - .toList(), - onChanged: (e) { - selectedStatus = e; - }, - ), - ), - const SizedBox( - width: 10, - ), - //// gender - Flexible( - flex: 1, - child: DropdownButtonFormField( - isExpanded: true, - value: selectedGender, - decoration: normalTextFieldStyle("Gender", ""), - items: state.genders - .map((element) => - DropdownMenuItem( - value: element, - child: Text(element.name!))) - .toList(), - onChanged: (e) { - selectedGender = e; - }, - ), - ), - ]), - ), - const SizedBox( - height: 15, - ), - SizedBox( - width: screenWidth, - child: Row(children: [ - Flexible( - flex: 1, - child: FormBuilderTextField( - name: "height", - initialValue: state.primaryInformation.heightM - .toString() - .toString(), - decoration: normalTextFieldStyle("Height", ""), - ), - ), - const SizedBox( - width: 10, - ), - Flexible( - flex: 1, - child: FormBuilderTextField( - name: "weigth", - initialValue: - state.primaryInformation.weightKg!.toString(), - decoration: normalTextFieldStyle("Weight", ""), - ), - ), - ]), - ), - const SizedBox( - height: 12, - ), - SizedBox( - width: screenWidth, - child: Row(children: [ - Flexible( - flex: 1, - child: FormBuilderTextField( - name: "prefix", - initialValue: state.primaryInformation.titlePrefix - .toString() - .toString(), - decoration: normalTextFieldStyle( - "Title Prefix", "Dr.,Atty.,Engr."), - ), - ), - const SizedBox( - width: 10, - ), - Flexible( - flex: 1, - child: FormBuilderTextField( - name: "suffix", - initialValue: state.primaryInformation.titleSuffix, - decoration: normalTextFieldStyle( - "Title Suffix", "PhD.,MD.,MS.,CE"), - ), - ), - ]), - ), - const SizedBox( - height: 12, - ), - SizedBox( - width: screenWidth, - child: Row(children: [ - ////Indigency - Flexible( - flex: 1, - child: DropdownButtonFormField( - isExpanded: true, - value: selectedIndigency, - decoration: normalTextFieldStyle("Indigency", ""), - items: state.indigenous - .map((element) => - DropdownMenuItem( - value: element, - child: Text(element.name!))) - .toList(), - onChanged: (e) { - selectedIndigency = e; - }, - ), - ), - const SizedBox( - width: 10, - ), - ////Ethnicity - Flexible( - flex: 1, - child: DropdownButtonFormField( - isExpanded: true, - value: selectedEthnicity, - decoration: normalTextFieldStyle("Ethnicity", ""), - items: state.ethnicity - .map((element) => - DropdownMenuItem( - value: element, - child: Text(element.name!))) - .toList(), - onChanged: (e) { - selectedEthnicity = e; - }, - ), - ), - ]), - ), - const SizedBox( - height: 12, - ), - SizedBox( - width: screenWidth, - child: Row(children: [ - ////religion - Flexible( - flex: 1, - child: DropdownButtonFormField( - isExpanded: true, - value: selectedReligion, - decoration: normalTextFieldStyle("Religion", ""), - items: state.religion - .map((element) => - DropdownMenuItem( - value: element, - child: Text(element.name!))) - .toList(), - onChanged: (e) { - selectedReligion = e; - }, - ), - ), - const SizedBox( - width: 10, - ), - ////disabilty - Flexible( - flex: 1, - child: DropdownButtonFormField( - isExpanded: true, - value: selectedDisability, - decoration: normalTextFieldStyle("Disability", ""), - items: state.disability - .map((element) => - DropdownMenuItem( - value: element, - child: Text(element.name!))) - .toList(), - onChanged: (e) { - selectedDisability = e; - }, - ), - ), - ]), - ), - const SizedBox( - height: 32, - ), - SizedBox( - width: double.infinity, - height: 60, - child: ElevatedButton( - style: - mainBtnStyle(primary, Colors.transparent, second), - onPressed: () { - if (_formKey.currentState!.saveAndValidate()) { - String lastName = - _formKey.currentState!.value['lastname']; - String firstName = - _formKey.currentState!.value['firstname']; - String? middleName = - _formKey.currentState?.value['middlename']; - String? pref = - _formKey.currentState?.value['prefix']; - String suf = _formKey.currentState?.value['suffix']; - DateTime birthdate = - DateTime.parse(bdayController.text); - double? hM = - _formKey.currentState!.value['height'] == null - ? null - : double.parse( - _formKey.currentState?.value['height']); - double? wKg = - _formKey.currentState!.value['weigth'] == null - ? null - : double.parse( - _formKey.currentState?.value['weigth']); - Profile primaryInformation = Profile( - id: state.primaryInformation.id, - lastName: lastName, - firstName: firstName, - middleName: middleName, - nameExtension: selectedExtension, - sex: selectedSex, - birthdate: birthdate, - civilStatus: selectedStatus, - bloodType: selectedBloodType, - heightM: hM, - weightKg: wKg, - photoPath: state.primaryInformation.photoPath, - esigPath: state.primaryInformation.esigPath, - maidenName: state.primaryInformation.maidenName, - deceased: state.primaryInformation.deceased, - uuidQrcode: state.primaryInformation.uuidQrcode, - titlePrefix: pref, - titleSuffix: suf, - showTitleId: - state.primaryInformation.showTitleId, - ethnicity: selectedEthnicity?.name, - disability: selectedDisability?.name, - gender: selectedGender?.name, - religion: selectedReligion?.name, - ip: selectedIndigency?.name); - final progress = ProgressHUD.of(context); - progress!.showWithText("Loading..."); - context.read().add( - EditBasicProfileInformation( - disabilityId: selectedDisability?.id, - ethnicityId: selectedDisability?.id, - genderId: selectedGender?.id, - indigencyId: selectedIndigency?.id, - profileId: widget.profileId, - profileInformation: primaryInformation, - religionId: selectedReligion?.id, - token: widget.token)); - } - }, - child: const Text("Submit")), - ) ], ), ), ); } - return Placeholder(); + return Container(); }, ); } diff --git a/lib/screens/profile/components/basic_information/family/child_add_modal.dart b/lib/screens/profile/components/basic_information/family/child_add_modal.dart index d54a375..29247b9 100644 --- a/lib/screens/profile/components/basic_information/family/child_add_modal.dart +++ b/lib/screens/profile/components/basic_information/family/child_add_modal.dart @@ -9,6 +9,7 @@ import '../../../../../model/utils/position.dart'; import '../../../../../theme-data.dart/btn-style.dart'; import '../../../../../theme-data.dart/colors.dart'; import '../../../../../theme-data.dart/form-style.dart'; +import '../../../../../utils/formatters.dart'; import '../../../../../utils/global.dart'; import '../../../../../utils/text_container.dart'; import '../../../../../utils/validators.dart'; @@ -46,6 +47,11 @@ class _ChildAlertState extends State { String? selectedCivilStatus; bool deceased = false; @override + void dispose() { + bdayController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return AlertDialog( title: const Text("Family - Child"), @@ -58,26 +64,33 @@ class _ChildAlertState extends State { children: [ ////name FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], validator: FormBuilderValidators.required( errorText: "This field is required"), name: "lastname", - decoration: normalTextFieldStyle("Last name", ""), + decoration: normalTextFieldStyle("Last name *", ""), ), const SizedBox( height: 8, ), //// firstname FormBuilderTextField( + inputFormatters: [ + UpperCaseTextFormatter() + ], name: "firstname", validator: FormBuilderValidators.required( errorText: "This field is required"), - decoration: normalTextFieldStyle("First name", ""), + decoration: normalTextFieldStyle("First name *", ""), ), const SizedBox( height: 8, ), ////middle name FormBuilderTextField( + inputFormatters: [ + UpperCaseTextFormatter() + ], name: "middlename", decoration: normalTextFieldStyle("Middle name", ""), ), @@ -87,6 +100,7 @@ class _ChildAlertState extends State { ), //// extension FormBuilderDropdown( + decoration: normalTextFieldStyle("Name extension", ""), name: "extension", items: widget.nameExtensions @@ -112,7 +126,7 @@ class _ChildAlertState extends State { errorText: "This field is required"), timeHintText: "Birthdate", decoration: - normalTextFieldStyle("Birthdate *", "*").copyWith( + normalTextFieldStyle("Birthdate *", "").copyWith( prefixIcon: const Icon( Icons.date_range, color: Colors.black87, @@ -131,7 +145,7 @@ class _ChildAlertState extends State { flex: 1, //// sex child: FormBuilderDropdown( - decoration: normalTextFieldStyle("Sex", ""), + decoration: normalTextFieldStyle("Sex *", ""), name: "sex", validator: FormBuilderValidators.required( errorText: "This field is required"), @@ -216,9 +230,9 @@ class _ChildAlertState extends State { ////height child: FormBuilderTextField( name: "height", - validator: integerAndNumeric, + validator:FormBuilderValidators.numeric(errorText: "Enter a number"), keyboardType: TextInputType.number, - decoration: normalTextFieldStyle("Height", ""), + decoration: normalTextFieldStyle("Height (Meter)", ""), ), ), const SizedBox( @@ -229,9 +243,9 @@ class _ChildAlertState extends State { //// weight child: FormBuilderTextField( name: "weight", - validator: integerAndNumeric, + validator: FormBuilderValidators.numeric(errorText: "Enter a number"), keyboardType: TextInputType.number, - decoration: normalTextFieldStyle("Weight", ""), + decoration: normalTextFieldStyle("Weight (Kg.)", ""), ), ), const SizedBox( @@ -288,11 +302,11 @@ class _ChildAlertState extends State { Position? position; double? height = _formKey.currentState?.value['height']==null? null: - double.parse( + double.tryParse( _formKey.currentState?.value['height']); double? weight = _formKey.currentState?.value['weight']==null?null: - double.parse( + double.tryParse( _formKey.currentState?.value['weight']); Relationship relationship = Relationship( id: 1, diff --git a/lib/screens/profile/components/basic_information/family/child_edit_modal.dart b/lib/screens/profile/components/basic_information/family/child_edit_modal.dart index efd6bac..1321d9b 100644 --- a/lib/screens/profile/components/basic_information/family/child_edit_modal.dart +++ b/lib/screens/profile/components/basic_information/family/child_edit_modal.dart @@ -12,6 +12,7 @@ import '../../../../../model/utils/position.dart'; import '../../../../../theme-data.dart/btn-style.dart'; import '../../../../../theme-data.dart/colors.dart'; import '../../../../../theme-data.dart/form-style.dart'; +import '../../../../../utils/formatters.dart'; import '../../../../../utils/global.dart'; import '../../../../../utils/text_container.dart'; import '../../../../../utils/validators.dart'; @@ -51,6 +52,12 @@ class _ChildEditAlertState extends State { String? selectedBloodType; String? selectedCivilStatus; bool deceased = false; + + @override + void dispose() { + bdayController.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { selectedExtension = widget.familyBackground.relatedPerson?.nameExtension; @@ -61,7 +68,7 @@ class _ChildEditAlertState extends State { bdayController.text = widget.familyBackground.relatedPerson!.birthdate!.toString(); deceased = widget.familyBackground.relatedPerson!.deceased!; return AlertDialog( - title: const Text("Family - Parental Parent"), + title: const Text("Family - Child"), contentPadding: const EdgeInsets.all(24), content: SingleChildScrollView( child: FormBuilder( @@ -71,28 +78,37 @@ class _ChildEditAlertState extends State { children: [ ////name FormBuilderTextField( + inputFormatters: [ + UpperCaseTextFormatter() + ], initialValue: widget.familyBackground.relatedPerson!.lastName, validator: FormBuilderValidators.required( errorText: "This field is required"), name: "lastname", - decoration: normalTextFieldStyle("Last name", ""), + decoration: normalTextFieldStyle("Last name *", ""), ), const SizedBox( height: 8, ), //// firstname FormBuilderTextField( + inputFormatters: [ + UpperCaseTextFormatter() + ], initialValue: widget.familyBackground.relatedPerson!.firstName, name: "firstname", validator: FormBuilderValidators.required( errorText: "This field is required"), - decoration: normalTextFieldStyle("First name", ""), + decoration: normalTextFieldStyle("First name *", ""), ), const SizedBox( height: 8, ), ////middle name FormBuilderTextField( + inputFormatters: [ + UpperCaseTextFormatter() + ], initialValue: widget.familyBackground.relatedPerson?.middleName, name: "middlename", @@ -129,7 +145,7 @@ class _ChildEditAlertState extends State { errorText: "This field is required"), timeHintText: "Birthdate", decoration: - normalTextFieldStyle("Birthdate *", "*").copyWith( + normalTextFieldStyle("Birthdate *", "").copyWith( prefixIcon: const Icon( Icons.date_range, color: Colors.black87, @@ -148,7 +164,7 @@ class _ChildEditAlertState extends State { flex: 1, //// sex child: DropdownButtonFormField( - decoration: normalTextFieldStyle("Sex", ""), + decoration: normalTextFieldStyle("Sex *", ""), value: selectedSex, validator: FormBuilderValidators.required( errorText: "This field is required"), @@ -237,9 +253,9 @@ class _ChildEditAlertState extends State { child: FormBuilderTextField( initialValue: widget.familyBackground.relatedPerson?.heightM == null? null:widget.familyBackground.relatedPerson?.heightM.toString(), name: "height", - validator: integerAndNumeric, + validator: FormBuilderValidators.numeric(errorText: "Enter a number"), keyboardType: TextInputType.number, - decoration: normalTextFieldStyle("Height", ""), + decoration: normalTextFieldStyle("Height (Meter)", ""), ), ), const SizedBox( @@ -251,9 +267,9 @@ class _ChildEditAlertState extends State { child: FormBuilderTextField( initialValue: widget.familyBackground.relatedPerson?.weightKg == null? null:widget.familyBackground.relatedPerson?.weightKg.toString(), name: "weight", - validator: integerAndNumeric, + validator:FormBuilderValidators.numeric(errorText: "Enter a number"), keyboardType: TextInputType.number, - decoration: normalTextFieldStyle("Weight", ""), + decoration: normalTextFieldStyle("Weight (Kg)", ""), ), ), const SizedBox( @@ -311,11 +327,11 @@ class _ChildEditAlertState extends State { Position? position; double? height = _formKey.currentState?.value['height']==null? null: - double.parse( + double.tryParse( _formKey.currentState?.value['height']); double? weight = _formKey.currentState?.value['weight']==null?null: - double.parse( + double.tryParse( _formKey.currentState?.value['weight']); Relationship relationship = Relationship( id: 1, diff --git a/lib/screens/profile/components/basic_information/family/father_add_modal.dart b/lib/screens/profile/components/basic_information/family/father_add_modal.dart index da8c4e9..9dbcc41 100644 --- a/lib/screens/profile/components/basic_information/family/father_add_modal.dart +++ b/lib/screens/profile/components/basic_information/family/father_add_modal.dart @@ -1,13 +1,10 @@ import 'package:date_time_picker/date_time_picker.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; -import 'package:flutter_progress_hud/flutter_progress_hud.dart'; -import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:unit2/bloc/profile/family/family_bloc.dart'; import 'package:unit2/model/profile/family_backround.dart'; - +import 'package:unit2/utils/formatters.dart'; import '../../../../../model/utils/position.dart'; import '../../../../../theme-data.dart/btn-style.dart'; import '../../../../../theme-data.dart/colors.dart'; @@ -32,7 +29,10 @@ class FatherAlert extends StatefulWidget { required this.civilStatus, required this.gender, required this.nameExtensions, - required this.sexes, required this.familyBloc, required this.profileId, required this.token}); + required this.sexes, + required this.familyBloc, + required this.profileId, + required this.token}); @override State createState() => _FatherAlertState(); @@ -49,6 +49,12 @@ class _FatherAlertState extends State { String? selectedBloodType; String? selectedCivilStatus; bool deceased = false; + + @override + void dispose() { + bdayController.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { return AlertDialog( @@ -62,31 +68,33 @@ class _FatherAlertState extends State { children: [ ////name FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], validator: FormBuilderValidators.required( errorText: "This field is required"), name: "lastname", - decoration: normalTextFieldStyle("Last name", ""), + decoration: normalTextFieldStyle("Last name *", ""), ), const SizedBox( height: 8, ), //// firstname FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], name: "firstname", validator: FormBuilderValidators.required( errorText: "This field is required"), - decoration: normalTextFieldStyle("First name", ""), + decoration: normalTextFieldStyle("First name *", ""), ), const SizedBox( height: 8, ), ////middle name FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], name: "middlename", - decoration: normalTextFieldStyle("Middle name", ""), ), - + const SizedBox( height: 8, ), @@ -104,11 +112,11 @@ class _FatherAlertState extends State { selectedExtension = e; }, ), - + const SizedBox( height: 8, ), - ////Bday + ////Bday DateTimePicker( controller: bdayController, use24HourFormat: false, @@ -135,7 +143,7 @@ class _FatherAlertState extends State { flex: 1, //// sex child: FormBuilderDropdown( - decoration: normalTextFieldStyle("Sex", ""), + decoration: normalTextFieldStyle("Sex *", ""), name: "sex", validator: FormBuilderValidators.required( errorText: "This field is required"), @@ -220,9 +228,9 @@ class _FatherAlertState extends State { ////height child: FormBuilderTextField( name: "height", - validator: integerAndNumeric, + validator: FormBuilderValidators.numeric(errorText: "Enter a number"), keyboardType: TextInputType.number, - decoration: normalTextFieldStyle("Height", ""), + decoration: normalTextFieldStyle("Height (Meter)", ""), ), ), const SizedBox( @@ -233,9 +241,9 @@ class _FatherAlertState extends State { //// weight child: FormBuilderTextField( name: "weight", - validator: integerAndNumeric, + validator: FormBuilderValidators.numeric(errorText: "Enter a number"), keyboardType: TextInputType.number, - decoration: normalTextFieldStyle("Weight", ""), + decoration: normalTextFieldStyle("Weight (Kg)", ""), ), ), const SizedBox( @@ -260,7 +268,7 @@ class _FatherAlertState extends State { deceased = value!; }); }, - + name: 'deceased', validator: FormBuilderValidators.required(), ); @@ -284,20 +292,30 @@ class _FatherAlertState extends State { String? mname = _formKey.currentState?.value['middlename']; String bday = bdayController.text; - String? gender = selectedGender =="NONE"?null:selectedGender; - String? extension = selectedExtension=="NONE"?null:selectedExtension; - String? blood = selectedBloodType =="NONE"?null:selectedBloodType; - String? civilStatus = selectedCivilStatus =="NONE"?null:selectedCivilStatus; + String? gender = selectedGender == "NONE" + ? null + : selectedGender; + String? extension = selectedExtension == "NONE" + ? null + : selectedExtension; + String? blood = selectedBloodType == "NONE" + ? null + : selectedBloodType; + String? civilStatus = selectedCivilStatus == "NONE" + ? null + : selectedCivilStatus; String? sex = selectedSex; Company? company; Position? position; double? height = - _formKey.currentState?.value['height']==null? null: - double.parse( + _formKey.currentState?.value['height'] == null + ? null + : double.tryParse( _formKey.currentState?.value['height']); double? weight = - _formKey.currentState?.value['weight']==null?null: - double.parse( + _formKey.currentState?.value['weight'] == null + ? null + : double.tryParse( _formKey.currentState?.value['weight']); Relationship relationship = Relationship( id: 1, @@ -329,10 +347,22 @@ class _FatherAlertState extends State { showTitleId: false, ); FamilyBackground familyBackground = - FamilyBackground(company: company,position: position,relatedPerson: person,relationship: relationship,companyAddress: companyAddress,emergencyContact: emergnecyContacts,incaseOfEmergency: incaseOfEmergency,companyContactNumber: null); - Navigator.of(context).pop(); - - widget.familyBloc.add(AddFamily(familyBackground: familyBackground, profileId: widget.profileId, token: widget.token,relationshipId: 1)); + FamilyBackground( + company: company, + position: position, + relatedPerson: person, + relationship: relationship, + companyAddress: companyAddress, + emergencyContact: emergnecyContacts, + incaseOfEmergency: incaseOfEmergency, + companyContactNumber: null); + Navigator.of(context).pop(); + + widget.familyBloc.add(AddFamily( + familyBackground: familyBackground, + profileId: widget.profileId, + token: widget.token, + relationshipId: 1)); } }, child: const Text(submit)), diff --git a/lib/screens/profile/components/basic_information/family/father_edit_modal.dart b/lib/screens/profile/components/basic_information/family/father_edit_modal.dart index ff95fe3..9ae8848 100644 --- a/lib/screens/profile/components/basic_information/family/father_edit_modal.dart +++ b/lib/screens/profile/components/basic_information/family/father_edit_modal.dart @@ -12,6 +12,7 @@ import '../../../../../model/utils/position.dart'; import '../../../../../theme-data.dart/btn-style.dart'; import '../../../../../theme-data.dart/colors.dart'; import '../../../../../theme-data.dart/form-style.dart'; +import '../../../../../utils/formatters.dart'; import '../../../../../utils/global.dart'; import '../../../../../utils/text_container.dart'; import '../../../../../utils/validators.dart'; @@ -34,7 +35,10 @@ class FatherEditAlert extends StatefulWidget { required this.civilStatus, required this.gender, required this.nameExtensions, - required this.sexes, required this.familyBloc, required this.profileId, required this.token}); + required this.sexes, + required this.familyBloc, + required this.profileId, + required this.token}); @override State createState() => _FatherEditAlertState(); @@ -51,14 +55,21 @@ class _FatherEditAlertState extends State { String? selectedBloodType; String? selectedCivilStatus; bool deceased = false; + + @override + void dispose() { + bdayController.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { - selectedExtension = widget.familyBackground.relatedPerson?.nameExtension; - selectedGender = widget.familyBackground.relatedPerson?.gender; - selectedSex = widget.familyBackground.relatedPerson?.sex; - selectedBloodType = widget.familyBackground.relatedPerson?.bloodType; - selectedCivilStatus = widget.familyBackground.relatedPerson?.civilStatus; - bdayController.text = widget.familyBackground.relatedPerson!.birthdate!.toString(); + selectedExtension = widget.familyBackground.relatedPerson?.nameExtension; + selectedGender = widget.familyBackground.relatedPerson?.gender; + selectedSex = widget.familyBackground.relatedPerson?.sex; + selectedBloodType = widget.familyBackground.relatedPerson?.bloodType; + selectedCivilStatus = widget.familyBackground.relatedPerson?.civilStatus; + bdayController.text = + widget.familyBackground.relatedPerson!.birthdate!.toString(); deceased = widget.familyBackground.relatedPerson!.deceased!; return AlertDialog( title: const Text("Family - Parental Parent"), @@ -71,42 +82,46 @@ class _FatherEditAlertState extends State { children: [ ////name FormBuilderTextField( - initialValue: widget.familyBackground.relatedPerson!.lastName, + inputFormatters: [UpperCaseTextFormatter()], + initialValue: + widget.familyBackground.relatedPerson!.lastName, validator: FormBuilderValidators.required( errorText: "This field is required"), name: "lastname", - decoration: normalTextFieldStyle("Last name", ""), + decoration: normalTextFieldStyle("Last name *", ""), ), const SizedBox( height: 8, ), //// firstname FormBuilderTextField( - initialValue: widget.familyBackground.relatedPerson!.firstName, + inputFormatters: [UpperCaseTextFormatter()], + initialValue: + widget.familyBackground.relatedPerson!.firstName, name: "firstname", validator: FormBuilderValidators.required( errorText: "This field is required"), - decoration: normalTextFieldStyle("First name", ""), + decoration: normalTextFieldStyle("First name *", ""), ), const SizedBox( height: 8, ), ////middle name FormBuilderTextField( - initialValue: widget.familyBackground.relatedPerson?.middleName, + inputFormatters: [UpperCaseTextFormatter()], + initialValue: + widget.familyBackground.relatedPerson?.middleName, name: "middlename", - decoration: normalTextFieldStyle("Middle name", ""), ), - + const SizedBox( height: 8, ), //// extension DropdownButtonFormField( - value: selectedExtension, + value: selectedExtension, decoration: normalTextFieldStyle("Name extension", ""), - items: widget.nameExtensions .map((element) => DropdownMenuItem( value: element, @@ -117,11 +132,11 @@ class _FatherEditAlertState extends State { selectedExtension = e; }, ), - + const SizedBox( height: 8, ), - ////Bday + ////Bday DateTimePicker( controller: bdayController, use24HourFormat: false, @@ -129,7 +144,7 @@ class _FatherEditAlertState extends State { errorText: "This field is required"), timeHintText: "Birthdate", decoration: - normalTextFieldStyle("Birthdate *", "*").copyWith( + normalTextFieldStyle("Birthdate *", "").copyWith( prefixIcon: const Icon( Icons.date_range, color: Colors.black87, @@ -148,8 +163,8 @@ class _FatherEditAlertState extends State { flex: 1, //// sex child: DropdownButtonFormField( - decoration: normalTextFieldStyle("Sex", ""), - value: selectedSex, + decoration: normalTextFieldStyle("Sex *", ""), + value: selectedSex, validator: FormBuilderValidators.required( errorText: "This field is required"), items: widget.sexes @@ -170,7 +185,7 @@ class _FatherEditAlertState extends State { child: DropdownButtonFormField( isExpanded: true, decoration: normalTextFieldStyle("Gender", ""), - value: selectedGender, + value: selectedGender, items: widget.gender .map((element) => DropdownMenuItem( value: element, child: Text(element))) @@ -194,7 +209,7 @@ class _FatherEditAlertState extends State { child: DropdownButtonFormField( isExpanded: true, decoration: normalTextFieldStyle("Blood type", ""), - value: selectedBloodType, + value: selectedBloodType, items: widget.bloodType .map((element) => DropdownMenuItem( value: element, child: Text(element))) @@ -235,11 +250,16 @@ class _FatherEditAlertState extends State { flex: 1, ////height child: FormBuilderTextField( - initialValue: widget.familyBackground.relatedPerson?.heightM == null? null:widget.familyBackground.relatedPerson?.heightM.toString(), + initialValue: widget.familyBackground.relatedPerson + ?.heightM == + null + ? null + : widget.familyBackground.relatedPerson?.heightM + .toString(), name: "height", - validator: integerAndNumeric, + validator: FormBuilderValidators.numeric(errorText: "Number only"), keyboardType: TextInputType.number, - decoration: normalTextFieldStyle("Height", ""), + decoration: normalTextFieldStyle("Height (Meter)", ""), ), ), const SizedBox( @@ -249,11 +269,16 @@ class _FatherEditAlertState extends State { flex: 1, //// weight child: FormBuilderTextField( - initialValue: widget.familyBackground.relatedPerson?.weightKg == null? null:widget.familyBackground.relatedPerson?.weightKg.toString(), + initialValue: widget.familyBackground.relatedPerson + ?.weightKg == + null + ? null + : widget.familyBackground.relatedPerson?.weightKg + .toString(), name: "weight", - validator: integerAndNumeric, + validator: FormBuilderValidators.numeric(errorText: "Number only"), keyboardType: TextInputType.number, - decoration: normalTextFieldStyle("Weight", ""), + decoration: normalTextFieldStyle("Weight (Kg.)", ""), ), ), const SizedBox( @@ -272,13 +297,11 @@ class _FatherEditAlertState extends State { initialValue: deceased, title: Text(deceased ? "YES" : "NO"), decoration: normalTextFieldStyle("Deceased?", ""), - onChanged: (value) { setState(() { deceased = value!; }); }, - name: 'deceased', validator: FormBuilderValidators.required(), ); @@ -294,6 +317,7 @@ class _FatherEditAlertState extends State { style: mainBtnStyle(second, Colors.transparent, primary), onPressed: () { + if (_formKey.currentState!.saveAndValidate()) { String fname = _formKey.currentState!.value['firstname']; @@ -301,21 +325,32 @@ class _FatherEditAlertState extends State { _formKey.currentState!.value['lastname']; String? mname = _formKey.currentState?.value['middlename']; + String bday = bdayController.text; - String? gender = selectedGender =="NONE"?null:selectedGender; - String? extension = selectedExtension=="NONE"?null:selectedExtension; - String? blood = selectedBloodType =="NONE"?null:selectedBloodType; - String? civilStatus = selectedCivilStatus =="NONE"?null:selectedCivilStatus; + String? gender = selectedGender == "NONE" + ? null + : selectedGender; + String? extension = selectedExtension == "NONE" + ? null + : selectedExtension; + String? blood = selectedBloodType == "NONE" + ? null + : selectedBloodType; + String? civilStatus = selectedCivilStatus == "NONE" + ? null + : selectedCivilStatus; String? sex = selectedSex; Company? company; Position? position; double? height = - _formKey.currentState?.value['height']==null? null: - double.parse( + _formKey.currentState?.value['height'] == null + ? null + : double.tryParse( _formKey.currentState?.value['height']); double? weight = - _formKey.currentState?.value['weight']==null?null: - double.parse( + _formKey.currentState?.value['weight'] == null + ? null + : double.tryParse( _formKey.currentState?.value['weight']); Relationship relationship = Relationship( id: 1, @@ -347,10 +382,22 @@ class _FatherEditAlertState extends State { showTitleId: false, ); FamilyBackground familyBackground = - FamilyBackground(company: company,position: position,relatedPerson: person,relationship: relationship,companyAddress: companyAddress,emergencyContact: emergnecyContacts,incaseOfEmergency: incaseOfEmergency,companyContactNumber: null); - Navigator.of(context).pop(); - - widget.familyBloc.add(Updatefamily(familyBackground: familyBackground, profileId: widget.profileId, token: widget.token,relationshipId: 1)); + FamilyBackground( + company: company, + position: position, + relatedPerson: person, + relationship: relationship, + companyAddress: companyAddress, + emergencyContact: emergnecyContacts, + incaseOfEmergency: incaseOfEmergency, + companyContactNumber: null); + Navigator.of(context).pop(); + + widget.familyBloc.add(Updatefamily( + familyBackground: familyBackground, + profileId: widget.profileId, + token: widget.token, + relationshipId: 1)); } }, child: const Text(submit)), diff --git a/lib/screens/profile/components/basic_information/family/mother_add_modal.dart b/lib/screens/profile/components/basic_information/family/mother_add_modal.dart index 36f9c85..7b56e20 100644 --- a/lib/screens/profile/components/basic_information/family/mother_add_modal.dart +++ b/lib/screens/profile/components/basic_information/family/mother_add_modal.dart @@ -9,6 +9,7 @@ import '../../../../../model/utils/position.dart'; import '../../../../../theme-data.dart/btn-style.dart'; import '../../../../../theme-data.dart/colors.dart'; import '../../../../../theme-data.dart/form-style.dart'; +import '../../../../../utils/formatters.dart'; import '../../../../../utils/global.dart'; import '../../../../../utils/text_container.dart'; import '../../../../../utils/validators.dart'; @@ -47,6 +48,13 @@ class _MotherAlertState extends State { String? selectedBloodType; String? selectedCivilStatus; bool deceased = false; + @override + void dispose() { + bdayController.dispose(); + super.dispose(); + } + + @override Widget build(BuildContext context) { return AlertDialog( title: const Text("Family - Maternal Parent"), @@ -60,28 +68,30 @@ class _MotherAlertState extends State { children: [ ////name FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], validator: FormBuilderValidators.required( errorText: "This field is required"), name: "lastname", - decoration: normalTextFieldStyle("Last name", ""), + decoration: normalTextFieldStyle("Last name *", ""), ), const SizedBox( height: 8, ), //// firstname FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], name: "firstname", validator: FormBuilderValidators.required( errorText: "This field is required"), - decoration: normalTextFieldStyle("First name", ""), + decoration: normalTextFieldStyle("First name *", ""), ), const SizedBox( height: 8, ), ////middle name FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], name: "middlename", - decoration: normalTextFieldStyle("Middle name", ""), ), @@ -133,7 +143,7 @@ class _MotherAlertState extends State { flex: 1, //// sex child: FormBuilderDropdown( - decoration: normalTextFieldStyle("Sex", ""), + decoration: normalTextFieldStyle("Sex *", ""), name: "sex", validator: FormBuilderValidators.required( errorText: "This field is required"), @@ -215,13 +225,13 @@ class _MotherAlertState extends State { child: Row(children: [ Flexible( flex: 1, - + ////height child: FormBuilderTextField( name: "height", - validator: integerAndNumeric, + validator: FormBuilderValidators.numeric(errorText: "Enter a number"), keyboardType: TextInputType.number, - decoration: normalTextFieldStyle("Height", ""), + decoration: normalTextFieldStyle("Height (Meter)", ""), ), ), const SizedBox( @@ -232,9 +242,9 @@ class _MotherAlertState extends State { //// weight child: FormBuilderTextField( name: "weight", - validator: integerAndNumeric, + validator: FormBuilderValidators.numeric(errorText: "Enter a number"), keyboardType: TextInputType.number, - decoration: normalTextFieldStyle("Weight", ""), + decoration: normalTextFieldStyle("Weight (Kg.)", ""), ), ), const SizedBox( @@ -278,10 +288,11 @@ class _MotherAlertState extends State { ), ////maiden lastname FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], validator: FormBuilderValidators.required( errorText: "This field is required"), name: "maiden_lastname", - decoration: normalTextFieldStyle("Last name", ""), + decoration: normalTextFieldStyle("Last name *", ""), ), const SizedBox( @@ -289,6 +300,7 @@ class _MotherAlertState extends State { ), ////maiden middle name FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], name: "maiden_middlename", decoration: normalTextFieldStyle("Middle name", ""), ), @@ -301,7 +313,7 @@ class _MotherAlertState extends State { child: ElevatedButton( style: mainBtnStyle(second, Colors.transparent, primary), - onPressed: () { + onPressed: () { if (_formKey.currentState!.saveAndValidate()) { String fname = _formKey.currentState!.value['firstname']; @@ -309,23 +321,35 @@ class _MotherAlertState extends State { _formKey.currentState!.value['lastname']; String? mname = _formKey.currentState?.value['middlename']; - String maidenMiddleName = _formKey.currentState!.value['maiden_middlename']; - String maidenLastName = _formKey.currentState!.value['maiden_lastname']; + String? maidenMiddleName = _formKey + .currentState?.value['maiden_middlename']; + String maidenLastName = + _formKey.currentState!.value['maiden_lastname']; String bday = bdayController.text; - String? gender = selectedGender =="NONE"?null:selectedGender; - String? extension = selectedExtension=="NONE"?null:selectedExtension; - String? blood = selectedBloodType =="NONE"?null:selectedBloodType; - String? civilStatus = selectedCivilStatus =="NONE"?null:selectedCivilStatus; + String? gender = selectedGender == "NONE" + ? null + : selectedGender; + String? extension = selectedExtension == "NONE" + ? null + : selectedExtension; + String? blood = selectedBloodType == "NONE" + ? null + : selectedBloodType; + String? civilStatus = selectedCivilStatus == "NONE" + ? null + : selectedCivilStatus; String? sex = selectedSex; Company? company; Position? position; double? height = - _formKey.currentState?.value['height']==null? null: - double.parse( + _formKey.currentState?.value['height'] == null + ? null + : double.tryParse( _formKey.currentState?.value['height']); double? weight = - _formKey.currentState?.value['weight']==null?null: - double.parse( + _formKey.currentState?.value['weight'] == null + ? null + : double.tryParse( _formKey.currentState?.value['weight']); Relationship relationship = Relationship( id: 1, @@ -337,7 +361,9 @@ class _MotherAlertState extends State { RelatedPerson person = RelatedPerson( titlePrefix: null, firstName: fname, - maidenName: MaidenName(lastName: maidenLastName, middleName: maidenMiddleName), + maidenName: MaidenName( + lastName: maidenLastName, + middleName: maidenMiddleName), middleName: mname, lastName: lastName, birthdate: DateTime.parse(bday), @@ -357,10 +383,22 @@ class _MotherAlertState extends State { showTitleId: false, ); FamilyBackground familyBackground = - FamilyBackground(company: company,position: position,relatedPerson: person,relationship: relationship,companyAddress: companyAddress,emergencyContact: emergnecyContacts,incaseOfEmergency: incaseOfEmergency,companyContactNumber: null); - Navigator.of(context).pop(); - - widget.familyBloc.add(AddFamily(familyBackground: familyBackground, profileId: widget.profileId, token: widget.token,relationshipId: 2)); + FamilyBackground( + company: company, + position: position, + relatedPerson: person, + relationship: relationship, + companyAddress: companyAddress, + emergencyContact: emergnecyContacts, + incaseOfEmergency: incaseOfEmergency, + companyContactNumber: null); + Navigator.of(context).pop(); + + widget.familyBloc.add(AddFamily( + familyBackground: familyBackground, + profileId: widget.profileId, + token: widget.token, + relationshipId: 2)); } }, child: const Text(submit)), diff --git a/lib/screens/profile/components/basic_information/family/mother_edit_modal.dart b/lib/screens/profile/components/basic_information/family/mother_edit_modal.dart index 7eb9351..22c0ce6 100644 --- a/lib/screens/profile/components/basic_information/family/mother_edit_modal.dart +++ b/lib/screens/profile/components/basic_information/family/mother_edit_modal.dart @@ -1,17 +1,14 @@ import 'package:date_time_picker/date_time_picker.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; -import 'package:flutter_progress_hud/flutter_progress_hud.dart'; -import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:unit2/bloc/profile/family/family_bloc.dart'; import 'package:unit2/model/profile/family_backround.dart'; - import '../../../../../model/utils/position.dart'; import '../../../../../theme-data.dart/btn-style.dart'; import '../../../../../theme-data.dart/colors.dart'; import '../../../../../theme-data.dart/form-style.dart'; +import '../../../../../utils/formatters.dart'; import '../../../../../utils/global.dart'; import '../../../../../utils/text_container.dart'; import '../../../../../utils/validators.dart'; @@ -52,6 +49,11 @@ class _MotherEditAlertState extends State { String? selectedCivilStatus; bool deceased = false; @override + void dispose() { + bdayController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { selectedExtension = widget.familyBackground.relatedPerson?.nameExtension; selectedGender = widget.familyBackground.relatedPerson?.gender; @@ -71,28 +73,31 @@ class _MotherEditAlertState extends State { children: [ ////name FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], initialValue: widget.familyBackground.relatedPerson!.lastName, validator: FormBuilderValidators.required( errorText: "This field is required"), name: "lastname", - decoration: normalTextFieldStyle("Last name", ""), + decoration: normalTextFieldStyle("Last name *", ""), ), const SizedBox( height: 8, ), //// firstname FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], initialValue: widget.familyBackground.relatedPerson!.firstName, name: "firstname", validator: FormBuilderValidators.required( errorText: "This field is required"), - decoration: normalTextFieldStyle("First name", ""), + decoration: normalTextFieldStyle("First name *", ""), ), const SizedBox( height: 8, ), ////middle name FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], initialValue: widget.familyBackground.relatedPerson?.middleName, name: "middlename", @@ -148,7 +153,7 @@ class _MotherEditAlertState extends State { flex: 1, //// sex child: DropdownButtonFormField( - decoration: normalTextFieldStyle("Sex", ""), + decoration: normalTextFieldStyle("Sex *", ""), value: selectedSex, validator: FormBuilderValidators.required( errorText: "This field is required"), @@ -235,11 +240,12 @@ class _MotherEditAlertState extends State { flex: 1, ////height child: FormBuilderTextField( + initialValue: widget.familyBackground.relatedPerson?.heightM == null? null:widget.familyBackground.relatedPerson?.heightM.toString(), name: "height", - validator: integerAndNumeric, + validator: FormBuilderValidators.numeric(errorText: "Enter a number"), keyboardType: TextInputType.number, - decoration: normalTextFieldStyle("Height", ""), + decoration: normalTextFieldStyle("Height (Meter)", ""), ), ), const SizedBox( @@ -251,9 +257,9 @@ class _MotherEditAlertState extends State { child: FormBuilderTextField( initialValue: widget.familyBackground.relatedPerson?.weightKg == null? null:widget.familyBackground.relatedPerson?.weightKg.toString(), name: "weight", - validator: integerAndNumeric, + validator: FormBuilderValidators.numeric(errorText: "Enter a number"), keyboardType: TextInputType.number, - decoration: normalTextFieldStyle("Weight", ""), + decoration: normalTextFieldStyle("Weight (Kg.)", ""), ), ), @@ -294,6 +300,7 @@ class _MotherEditAlertState extends State { ), ////maiden lastname FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], initialValue: widget.familyBackground.relatedPerson?.maidenName?.lastName, validator: FormBuilderValidators.required( errorText: "This field is required"), @@ -306,6 +313,7 @@ class _MotherEditAlertState extends State { ), ////maiden middle name FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], initialValue: widget.familyBackground.relatedPerson?.maidenName?.middleName, name: "maiden_middlename", decoration: normalTextFieldStyle("Middle name", ""), @@ -327,7 +335,7 @@ class _MotherEditAlertState extends State { _formKey.currentState!.value['lastname']; String? mname = _formKey.currentState?.value['middlename']; - String maidenMiddleName = _formKey.currentState!.value['maiden_middlename']; + String? maidenMiddleName = _formKey.currentState?.value['maiden_middlename']; String maidenLastName = _formKey.currentState!.value['maiden_lastname']; String bday = bdayController.text; String? gender = selectedGender =="NONE"?null:selectedGender; @@ -339,11 +347,11 @@ class _MotherEditAlertState extends State { Position? position; double? height = _formKey.currentState?.value['height']==null? null: - double.parse( + double.tryParse( _formKey.currentState?.value['height']); double? weight = _formKey.currentState?.value['weight']==null?null: - double.parse( + double.tryParse( _formKey.currentState?.value['weight']); Relationship relationship = Relationship( id: 1, diff --git a/lib/screens/profile/components/basic_information/family/related_add_modal.dart b/lib/screens/profile/components/basic_information/family/related_add_modal.dart index 0e4b22b..3738114 100644 --- a/lib/screens/profile/components/basic_information/family/related_add_modal.dart +++ b/lib/screens/profile/components/basic_information/family/related_add_modal.dart @@ -2,15 +2,13 @@ import 'package:date_time_picker/date_time_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; -import 'package:unit2/model/utils/agency.dart'; -import 'package:unit2/model/utils/category.dart'; - import '../../../../../bloc/profile/family/family_bloc.dart'; import '../../../../../model/profile/family_backround.dart'; import '../../../../../model/utils/position.dart'; import '../../../../../theme-data.dart/btn-style.dart'; import '../../../../../theme-data.dart/colors.dart'; import '../../../../../theme-data.dart/form-style.dart'; +import '../../../../../utils/formatters.dart'; import '../../../../../utils/global.dart'; import '../../../../../utils/text_container.dart'; import '../../../../../utils/validators.dart'; @@ -22,31 +20,29 @@ class RelatedAlert extends StatefulWidget { final List bloodType; final List civilStatus; final List relationships; - final String token; + final String token; final int profileId; final FamilyBloc familyBloc; - - const RelatedAlert( - {super.key, - required this.bloodType, - required this.token, - required this.profileId, - required this.familyBloc, - required this.civilStatus, - required this.relationships, - required this.gender, - required this.nameExtensions, - required this.sexes, - - }); + const RelatedAlert({ + super.key, + required this.bloodType, + required this.token, + required this.profileId, + required this.familyBloc, + required this.civilStatus, + required this.relationships, + required this.gender, + required this.nameExtensions, + required this.sexes, + }); @override State createState() => _RelatedAlertState(); } class _RelatedAlertState extends State { -final _formKey = GlobalKey(); + final _formKey = GlobalKey(); final bdayController = TextEditingController(); ////selected @@ -57,7 +53,11 @@ final _formKey = GlobalKey(); String? selectedCivilStatus; Relationship? selectedRelationship; bool deceased = false; - +@override + void dispose() { +bdayController.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { return AlertDialog( @@ -71,50 +71,51 @@ final _formKey = GlobalKey(); children: [ ////Relationship FormBuilderDropdown( - decoration: normalTextFieldStyle("Relationship", ""), - name: "extension", - validator: FormBuilderValidators.required( - errorText: "This field is required"), - items: widget.relationships - .map((element){ - return DropdownMenuItem( - value: element, - child: Text(element.type!), - ); - - },) - .toList(), - onChanged: (e){ - selectedRelationship = e; - }, - ), + decoration: normalTextFieldStyle("Relationship *", ""), + name: "extension", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + items: widget.relationships.map( + (element) { + return DropdownMenuItem( + value: element, + child: Text(element.type!), + ); + }, + ).toList(), + onChanged: (e) { + selectedRelationship = e; + }, + ), const SizedBox( height: 8, ), ////name FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], validator: FormBuilderValidators.required( errorText: "This field is required"), name: "lastname", - decoration: normalTextFieldStyle("Last name", ""), + decoration: normalTextFieldStyle("Last name *", ""), ), const SizedBox( height: 8, ), //// firstname FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], name: "firstname", validator: FormBuilderValidators.required( errorText: "This field is required"), - decoration: normalTextFieldStyle("First name", ""), + decoration: normalTextFieldStyle("First name *", ""), ), const SizedBox( height: 8, ), ////middle name FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], name: "middlename", - decoration: normalTextFieldStyle("Middle name", ""), ), @@ -122,7 +123,7 @@ final _formKey = GlobalKey(); height: 8, ), //// extension - FormBuilderDropdown( + FormBuilderDropdown( decoration: normalTextFieldStyle("Name extension", ""), name: "extension", items: widget.nameExtensions @@ -140,7 +141,7 @@ final _formKey = GlobalKey(); height: 8, ), - ////Bday + ////Bday DateTimePicker( controller: bdayController, use24HourFormat: false, @@ -148,7 +149,7 @@ final _formKey = GlobalKey(); errorText: "This field is required"), timeHintText: "Birthdate", decoration: - normalTextFieldStyle("Birthdate *", "*").copyWith( + normalTextFieldStyle("Birthdate *", "").copyWith( prefixIcon: const Icon( Icons.date_range, color: Colors.black87, @@ -168,7 +169,7 @@ final _formKey = GlobalKey(); //// sex child: FormBuilderDropdown( decoration: normalTextFieldStyle("Sex", ""), - name: "sex", + name: "sex *", validator: FormBuilderValidators.required( errorText: "This field is required"), items: widget.sexes @@ -209,7 +210,7 @@ final _formKey = GlobalKey(); ////Blood Type Flexible( flex: 1, - child: FormBuilderDropdown( + child: FormBuilderDropdown( decoration: normalTextFieldStyle("Blood type", ""), name: "bloodtype", items: widget.bloodType @@ -252,9 +253,9 @@ final _formKey = GlobalKey(); ////height child: FormBuilderTextField( name: "height", - validator: integerAndNumeric, + validator: FormBuilderValidators.numeric(errorText: "Enter a number"), keyboardType: TextInputType.number, - decoration: normalTextFieldStyle("Height", ""), + decoration: normalTextFieldStyle("Height (Meter)", ""), ), ), const SizedBox( @@ -265,9 +266,9 @@ final _formKey = GlobalKey(); //// weight child: FormBuilderTextField( name: "weight", - validator: integerAndNumeric, + validator:FormBuilderValidators.numeric(errorText: "Enter a number"), keyboardType: TextInputType.number, - decoration: normalTextFieldStyle("Weight", ""), + decoration: normalTextFieldStyle("Weight (Kg.)", ""), ), ), const SizedBox( @@ -301,8 +302,7 @@ final _formKey = GlobalKey(); const SizedBox( height: 8, ), - - + const SizedBox( height: 12, ), @@ -321,20 +321,30 @@ final _formKey = GlobalKey(); String? mname = _formKey.currentState?.value['middlename']; String bday = bdayController.text; - String? gender = selectedGender =="NONE"?null:selectedGender; - String? extension = selectedExtension=="NONE"?null:selectedExtension; - String? blood = selectedBloodType =="NONE"?null:selectedBloodType; - String? civilStatus = selectedCivilStatus =="NONE"?null:selectedCivilStatus; + String? gender = selectedGender == "NONE" + ? null + : selectedGender; + String? extension = selectedExtension == "NONE" + ? null + : selectedExtension; + String? blood = selectedBloodType == "NONE" + ? null + : selectedBloodType; + String? civilStatus = selectedCivilStatus == "NONE" + ? null + : selectedCivilStatus; String? sex = selectedSex; Company? company; Position? position; double? height = - _formKey.currentState?.value['height']==null? null: - double.parse( + _formKey.currentState?.value['height'] == null + ? null + : double.tryParse( _formKey.currentState?.value['height']); double? weight = - _formKey.currentState?.value['weight']==null?null: - double.parse( + _formKey.currentState?.value['weight'] == null + ? null + : double.tryParse( _formKey.currentState?.value['weight']); Relationship relationship = Relationship( id: 1, @@ -366,10 +376,22 @@ final _formKey = GlobalKey(); showTitleId: false, ); FamilyBackground familyBackground = - FamilyBackground(company: company,position: position,relatedPerson: person,relationship: relationship,companyAddress: companyAddress,emergencyContact: emergnecyContacts,incaseOfEmergency: incaseOfEmergency,companyContactNumber: null); - Navigator.of(context).pop(); - - widget.familyBloc.add(AddFamily(familyBackground: familyBackground, profileId: widget.profileId, token: widget.token,relationshipId: selectedRelationship!.id!)); + FamilyBackground( + company: company, + position: position, + relatedPerson: person, + relationship: relationship, + companyAddress: companyAddress, + emergencyContact: emergnecyContacts, + incaseOfEmergency: incaseOfEmergency, + companyContactNumber: null); + Navigator.of(context).pop(); + + widget.familyBloc.add(AddFamily( + familyBackground: familyBackground, + profileId: widget.profileId, + token: widget.token, + relationshipId: selectedRelationship!.id!)); } }, child: const Text(submit)), diff --git a/lib/screens/profile/components/basic_information/family/related_edit_modal.dart b/lib/screens/profile/components/basic_information/family/related_edit_modal.dart index febcae4..b3e808a 100644 --- a/lib/screens/profile/components/basic_information/family/related_edit_modal.dart +++ b/lib/screens/profile/components/basic_information/family/related_edit_modal.dart @@ -7,6 +7,7 @@ import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:unit2/bloc/profile/family/family_bloc.dart'; import 'package:unit2/model/profile/family_backround.dart'; +import 'package:unit2/utils/formatters.dart'; import '../../../../../model/utils/position.dart'; import '../../../../../theme-data.dart/btn-style.dart'; @@ -58,6 +59,11 @@ class _RelatedEditAlertState extends State { Relationship? selectedRelationship; bool deceased = false; @override + void dispose() { + bdayController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { selectedExtension = widget.familyBackground.relatedPerson?.nameExtension; selectedGender = widget.familyBackground.relatedPerson?.gender; @@ -79,7 +85,7 @@ class _RelatedEditAlertState extends State { children: [ ////Relationship DropdownButtonFormField( - decoration: normalTextFieldStyle("Relationship", ""), + decoration: normalTextFieldStyle("Relationship *", ""), value: selectedRelationship, validator: FormBuilderValidators.required( errorText: "This field is required"), @@ -98,30 +104,33 @@ class _RelatedEditAlertState extends State { const SizedBox(height: 8,), ////name FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], initialValue: widget.familyBackground.relatedPerson!.lastName, validator: FormBuilderValidators.required( errorText: "This field is required"), name: "lastname", - decoration: normalTextFieldStyle("Last name", ""), + decoration: normalTextFieldStyle("Last name *", ""), ), const SizedBox( height: 8, ), //// firstname FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], initialValue: widget.familyBackground.relatedPerson!.firstName, name: "firstname", validator: FormBuilderValidators.required( errorText: "This field is required"), - decoration: normalTextFieldStyle("First name", ""), + decoration: normalTextFieldStyle("First name *", ""), ), const SizedBox( height: 8, ), ////middle name FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], initialValue: widget.familyBackground.relatedPerson?.middleName, name: "middlename", @@ -158,7 +167,7 @@ class _RelatedEditAlertState extends State { errorText: "This field is required"), timeHintText: "Birthdate", decoration: - normalTextFieldStyle("Birthdate *", "*").copyWith( + normalTextFieldStyle("Birthdate *", "").copyWith( prefixIcon: const Icon( Icons.date_range, color: Colors.black87, @@ -177,7 +186,7 @@ class _RelatedEditAlertState extends State { flex: 1, //// sex child: DropdownButtonFormField( - decoration: normalTextFieldStyle("Sex", ""), + decoration: normalTextFieldStyle("Sex *", ""), value: selectedSex, validator: FormBuilderValidators.required( errorText: "This field is required"), @@ -271,9 +280,9 @@ class _RelatedEditAlertState extends State { : widget.familyBackground.relatedPerson?.heightM .toString(), name: "height", - validator: integerAndNumeric, + validator: FormBuilderValidators.numeric(errorText: "Enter a number"), keyboardType: TextInputType.number, - decoration: normalTextFieldStyle("Height", ""), + decoration: normalTextFieldStyle("Height (Meter)", ""), ), ), const SizedBox( @@ -290,9 +299,9 @@ class _RelatedEditAlertState extends State { : widget.familyBackground.relatedPerson?.weightKg .toString(), name: "weight", - validator: integerAndNumeric, + validator: FormBuilderValidators.numeric(errorText: "Enter a number"), keyboardType: TextInputType.number, - decoration: normalTextFieldStyle("Weight", ""), + decoration: normalTextFieldStyle("Weight (Kg.)", ""), ), ), const SizedBox( @@ -357,12 +366,12 @@ class _RelatedEditAlertState extends State { double? height = _formKey.currentState?.value['height'] == null ? null - : double.parse( + : double.tryParse( _formKey.currentState?.value['height']); double? weight = _formKey.currentState?.value['weight'] == null ? null - : double.parse( + : double.tryParse( _formKey.currentState?.value['weight']); Relationship relationship = Relationship( id: 1, diff --git a/lib/screens/profile/components/basic_information/family/spouse_add_modal.dart b/lib/screens/profile/components/basic_information/family/spouse_add_modal.dart index 0ea373b..5abe49e 100644 --- a/lib/screens/profile/components/basic_information/family/spouse_add_modal.dart +++ b/lib/screens/profile/components/basic_information/family/spouse_add_modal.dart @@ -14,6 +14,7 @@ import '../../../../../theme-data.dart/box_shadow.dart'; import '../../../../../theme-data.dart/btn-style.dart'; import '../../../../../theme-data.dart/colors.dart'; import '../../../../../theme-data.dart/form-style.dart'; +import '../../../../../utils/formatters.dart'; import '../../../../../utils/global.dart'; import '../../../../../utils/text_container.dart'; import '../../../../../utils/validators.dart'; @@ -51,7 +52,7 @@ class SpouseAlert extends StatefulWidget { } class _SpouseAlertState extends State { - final _formKey = GlobalKey(); + final _formKey = GlobalKey(); final bdayController = TextEditingController(); ////selected @@ -59,7 +60,8 @@ class _SpouseAlertState extends State { String? selectedGender; String? selectedSex; String? selectedBloodType; - String? selectedCivilStatus; Position? selectedPosition; + String? selectedCivilStatus; + Position? selectedPosition; Category? selectedAgencyCategory; Agency? selectedAgency; bool deceased = false; @@ -76,6 +78,18 @@ class _SpouseAlertState extends State { bool showAgency = false; bool? isPrivate = false; bool showIsPrivateRadio = false; + @override + void dispose() { + bdayController.dispose(); + agencyFocusNode.dispose(); + positionFocusNode.dispose(); + appointmentStatusNode.dispose(); + agencyCategoryFocusNode.dispose(); + addAgencyController.dispose(); + addPositionController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return AlertDialog( @@ -89,26 +103,29 @@ class _SpouseAlertState extends State { children: [ ////name FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], validator: FormBuilderValidators.required( errorText: "This field is required"), name: "lastname", - decoration: normalTextFieldStyle("Last name", ""), + decoration: normalTextFieldStyle("Last name *", ""), ), const SizedBox( height: 8, ), //// firstname FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], name: "firstname", validator: FormBuilderValidators.required( errorText: "This field is required"), - decoration: normalTextFieldStyle("First name", ""), + decoration: normalTextFieldStyle("First name *", ""), ), const SizedBox( height: 8, ), ////middle name FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], name: "middlename", decoration: normalTextFieldStyle("Middle name", ""), ), @@ -120,7 +137,6 @@ class _SpouseAlertState extends State { FormBuilderDropdown( decoration: normalTextFieldStyle("Name extension", ""), name: "extension", - items: widget.nameExtensions .map((element) => DropdownMenuItem( value: element, @@ -132,7 +148,7 @@ class _SpouseAlertState extends State { height: 8, ), - ////Bday + ////Bday DateTimePicker( controller: bdayController, use24HourFormat: false, @@ -140,7 +156,7 @@ class _SpouseAlertState extends State { errorText: "This field is required"), timeHintText: "Birthdate", decoration: - normalTextFieldStyle("Birthdate *", "*").copyWith( + normalTextFieldStyle("Birthdate *", "").copyWith( prefixIcon: const Icon( Icons.date_range, color: Colors.black87, @@ -158,8 +174,8 @@ class _SpouseAlertState extends State { Flexible( flex: 1, //// sex - child: FormBuilderDropdown( - decoration: normalTextFieldStyle("Sex", ""), + child: FormBuilderDropdown( + decoration: normalTextFieldStyle("Sex *", ""), name: "sex", validator: FormBuilderValidators.required( errorText: "This field is required"), @@ -178,7 +194,7 @@ class _SpouseAlertState extends State { ////gender Flexible( flex: 1, - child: FormBuilderDropdown( + child: FormBuilderDropdown( decoration: normalTextFieldStyle("Gender", ""), name: "gender", items: widget.gender @@ -201,7 +217,7 @@ class _SpouseAlertState extends State { ////Blood Type Flexible( flex: 1, - child: FormBuilderDropdown( + child: FormBuilderDropdown( decoration: normalTextFieldStyle("Blood type", ""), name: "bloodtype", items: widget.bloodType @@ -217,20 +233,20 @@ class _SpouseAlertState extends State { width: 8, ), //// Civil Status - Flexible( - flex: 1, - child: FormBuilderDropdown( - decoration: normalTextFieldStyle("Civil status", ""), - name: "civil_status", - items: widget.civilStatus - .map((element) => DropdownMenuItem( - value: element, child: Text(element))) - .toList(), - onChanged: (e) { - selectedCivilStatus = e; - }, - ), - ), + Flexible( + flex: 1, + child: FormBuilderDropdown( + decoration: normalTextFieldStyle("Civil status", ""), + name: "civil_status", + items: widget.civilStatus + .map((element) => DropdownMenuItem( + value: element, child: Text(element))) + .toList(), + onChanged: (e) { + selectedCivilStatus = e; + }, + ), + ), ]), ), const SizedBox( @@ -244,9 +260,11 @@ class _SpouseAlertState extends State { ////height child: FormBuilderTextField( name: "height", - validator: integerAndNumeric, + validator: FormBuilderValidators.numeric( + errorText: "Enter a number"), keyboardType: TextInputType.number, - decoration: normalTextFieldStyle("Height", ""), + decoration: + normalTextFieldStyle("Height (Meter)", ""), ), ), const SizedBox( @@ -257,9 +275,10 @@ class _SpouseAlertState extends State { //// weight child: FormBuilderTextField( name: "weight", - validator: integerAndNumeric, + validator: FormBuilderValidators.numeric( + errorText: "Enter a number"), keyboardType: TextInputType.number, - decoration: normalTextFieldStyle("Weight", ""), + decoration: normalTextFieldStyle("Weight (Kg.)", ""), ), ), const SizedBox( @@ -325,7 +344,11 @@ class _SpouseAlertState extends State { builder: (context, setState) { ////Position return SearchField( - itemHeight: 50, + inputFormatters: [ + UpperCaseTextFormatter() + ], + + itemHeight: 100, suggestionsDecoration: box1(), suggestions: widget.positions .map((Position position) => @@ -339,8 +362,12 @@ class _SpouseAlertState extends State { horizontal: 10), child: ListTile( - title: Text(position - .title!), + title: Text( + position.title!, + overflow: + TextOverflow + .visible, + ), )))) .toList(), focusNode: positionFocusNode, @@ -399,7 +426,10 @@ class _SpouseAlertState extends State { return Column( children: [ SearchField( - itemHeight: 70, + inputFormatters: [ + UpperCaseTextFormatter() + ], + itemHeight: 100, focusNode: agencyFocusNode, suggestions: widget.agencies .map((Agency agency) => @@ -411,7 +441,7 @@ class _SpouseAlertState extends State { agency.name!, overflow: TextOverflow - .ellipsis, + .visible, ), subtitle: Text(agency .privateEntity == @@ -428,8 +458,13 @@ class _SpouseAlertState extends State { "Agency *", "") .copyWith( suffixIcon: - const Icon(Icons - .arrow_drop_down)), + IconButton( + icon: const Icon( + Icons.arrow_drop_down), + onPressed: () { + agencyFocusNode.unfocus(); + }, + )), onSuggestionTap: (agency) { setState(() { selectedAgency = @@ -493,6 +528,9 @@ class _SpouseAlertState extends State { focusNode: agencyCategoryFocusNode, itemHeight: 70, + suggestionDirection: + SuggestionDirection + .up, suggestions: widget .category .map((Category @@ -544,9 +582,14 @@ class _SpouseAlertState extends State { "") .copyWith( suffixIcon: - const Icon( - Icons - .arrow_drop_down)), + IconButton( + icon: const Icon(Icons + .arrow_drop_down), + onPressed: () { + agencyCategoryFocusNode + .unfocus(); + }, + )), validator: (value) { if (value!.isEmpty) { return "This field is required"; @@ -650,67 +693,99 @@ class _SpouseAlertState extends State { mainBtnStyle(second, Colors.transparent, primary), onPressed: () { if (_formKey.currentState!.saveAndValidate()) { - - if (_formKey.currentState!.saveAndValidate()) { - String fname = - _formKey.currentState!.value['firstname']; - String lastName = - _formKey.currentState!.value['lastname']; - String? mname = - _formKey.currentState?.value['middlename']; - String bday = bdayController.text; - String? gender = selectedGender =="NONE"?null:selectedGender; - String? extension = selectedExtension=="NONE"?null:selectedExtension; - String? blood = selectedBloodType =="NONE"?null:selectedBloodType; - String? civilStatus = selectedCivilStatus =="NONE"?null:selectedCivilStatus; - String? sex = selectedSex; - String? companyAddress = _formKey.currentState?.value['company_address']; - String? companyContactNumber= _formKey.currentState?.value['company_tel']; - Company? company = selectedAgency == null? null:Company(id: selectedAgency?.id,name: selectedAgency?.name,category: selectedAgencyCategory,privateEntity: isPrivate); - Position? position = selectedPosition; - double? height = - _formKey.currentState?.value['height']==null? null: - double.parse( - _formKey.currentState?.value['height']); - double? weight = - _formKey.currentState?.value['weight']==null?null: - double.parse( - _formKey.currentState?.value['weight']); - Relationship relationship = Relationship( - id: 1, - type: "Paternal_Parent", - category: "Family"); - List? emergnecyContacts; - bool incaseOfEmergency = false; - RelatedPerson person = RelatedPerson( - titlePrefix: null, - firstName: fname, - maidenName: null, - middleName: mname, - lastName: lastName, - birthdate: DateTime.parse(bday), - id: null, - sex: sex, - gender: gender, - deceased: deceased, - heightM: height, - weightKg: weight, - esigPath: null, - bloodType: blood, - photoPath: null, - uuidQrcode: null, - nameExtension: extension, - civilStatus: civilStatus, - titleSuffix: null, - showTitleId: false, - ); - FamilyBackground familyBackground = - FamilyBackground(company: company,position: position,relatedPerson: person,relationship: relationship,companyAddress: companyAddress,emergencyContact: emergnecyContacts,incaseOfEmergency: incaseOfEmergency,companyContactNumber: companyContactNumber); - Navigator.of(context).pop(); - - widget.familyBloc.add(AddFamily(familyBackground: familyBackground, profileId: widget.profileId, token: widget.token,relationshipId: 3)); - } - + if (_formKey.currentState!.saveAndValidate()) { + String fname = + _formKey.currentState!.value['firstname']; + String lastName = + _formKey.currentState!.value['lastname']; + String? mname = + _formKey.currentState?.value['middlename']; + String bday = bdayController.text; + String? gender = selectedGender == "NONE" + ? null + : selectedGender; + String? extension = selectedExtension == "NONE" + ? null + : selectedExtension; + String? blood = selectedBloodType == "NONE" + ? null + : selectedBloodType; + String? civilStatus = + selectedCivilStatus == "NONE" + ? null + : selectedCivilStatus; + String? sex = selectedSex; + String? companyAddress = _formKey + .currentState?.value['company_address']; + String? companyContactNumber = + _formKey.currentState?.value['company_tel']; + Company? company = selectedAgency == null + ? null + : Company( + id: selectedAgency?.id, + name: selectedAgency?.name, + category: selectedAgencyCategory, + privateEntity: isPrivate); + Position? position = selectedPosition; + double? height = _formKey + .currentState?.value['height'] == + null + ? null + : double.tryParse( + _formKey.currentState?.value['height']); + double? weight = _formKey + .currentState?.value['weight'] == + null + ? null + : double.tryParse( + _formKey.currentState?.value['weight']); + Relationship relationship = Relationship( + id: 1, + type: "Paternal_Parent", + category: "Family"); + List? emergnecyContacts; + bool incaseOfEmergency = false; + RelatedPerson person = RelatedPerson( + titlePrefix: null, + firstName: fname, + maidenName: null, + middleName: mname, + lastName: lastName, + birthdate: DateTime.parse(bday), + id: null, + sex: sex, + gender: gender, + deceased: deceased, + heightM: height, + weightKg: weight, + esigPath: null, + bloodType: blood, + photoPath: null, + uuidQrcode: null, + nameExtension: extension, + civilStatus: civilStatus, + titleSuffix: null, + showTitleId: false, + ); + FamilyBackground familyBackground = + FamilyBackground( + company: company, + position: position, + relatedPerson: person, + relationship: relationship, + companyAddress: companyAddress, + emergencyContact: emergnecyContacts, + incaseOfEmergency: incaseOfEmergency, + companyContactNumber: + companyContactNumber); + Navigator.of(context).pop(); + + widget.familyBloc.add(AddFamily( + familyBackground: familyBackground, + profileId: widget.profileId, + token: widget.token, + relationshipId: 3)); + } } }, child: const Text(submit)), diff --git a/lib/screens/profile/components/basic_information/family/spouse_edit_modal.dart b/lib/screens/profile/components/basic_information/family/spouse_edit_modal.dart index 56f8575..30012e4 100644 --- a/lib/screens/profile/components/basic_information/family/spouse_edit_modal.dart +++ b/lib/screens/profile/components/basic_information/family/spouse_edit_modal.dart @@ -14,6 +14,7 @@ import '../../../../../theme-data.dart/box_shadow.dart'; import '../../../../../theme-data.dart/btn-style.dart'; import '../../../../../theme-data.dart/colors.dart'; import '../../../../../theme-data.dart/form-style.dart'; +import '../../../../../utils/formatters.dart'; import '../../../../../utils/global.dart'; import '../../../../../utils/text_container.dart'; import '../../../../../utils/validators.dart'; @@ -92,9 +93,19 @@ class _SpouseEditAlertState extends State { widget.familyBackground.relatedPerson!.birthdate!.toString(); deceased = widget.familyBackground.relatedPerson!.deceased!; hasOccupation = widget.familyBackground.position != null; - oldPositionController.text = widget.familyBackground.position == null?"":widget.familyBackground.position!.title!; - oldAgencyController.text = widget.familyBackground.company == null?"":widget.familyBackground.company!.name!; - selectedAgency = widget.familyBackground.company==null?null:Agency(id: widget.familyBackground.company!.id, name: widget.familyBackground.company!.name,category: widget.familyBackground.company!.category,privateEntity: widget.familyBackground.company!.privateEntity); + oldPositionController.text = widget.familyBackground.position == null + ? "" + : widget.familyBackground.position!.title!; + oldAgencyController.text = widget.familyBackground.company == null + ? "" + : widget.familyBackground.company!.name!; + selectedAgency = widget.familyBackground.company == null + ? null + : Agency( + id: widget.familyBackground.company!.id, + name: widget.familyBackground.company!.name, + category: widget.familyBackground.company!.category, + privateEntity: widget.familyBackground.company!.privateEntity); return AlertDialog( title: const Text("Family - Spouse"), contentPadding: const EdgeInsets.all(24), @@ -106,30 +117,33 @@ class _SpouseEditAlertState extends State { children: [ ////name FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], initialValue: widget.familyBackground.relatedPerson!.lastName, validator: FormBuilderValidators.required( errorText: "This field is required"), name: "lastname", - decoration: normalTextFieldStyle("Last name", ""), + decoration: normalTextFieldStyle("Last name *", ""), ), const SizedBox( height: 8, ), //// firstname FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], initialValue: widget.familyBackground.relatedPerson!.firstName, name: "firstname", validator: FormBuilderValidators.required( errorText: "This field is required"), - decoration: normalTextFieldStyle("First name", ""), + decoration: normalTextFieldStyle("First name *", ""), ), const SizedBox( height: 8, ), ////middle name FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], initialValue: widget.familyBackground.relatedPerson?.middleName, name: "middlename", @@ -165,7 +179,7 @@ class _SpouseEditAlertState extends State { errorText: "This field is required"), timeHintText: "Birthdate", decoration: - normalTextFieldStyle("Birthdate *", "*").copyWith( + normalTextFieldStyle("Birthdate *", "").copyWith( prefixIcon: const Icon( Icons.date_range, color: Colors.black87, @@ -184,7 +198,7 @@ class _SpouseEditAlertState extends State { flex: 1, //// sex child: DropdownButtonFormField( - decoration: normalTextFieldStyle("Sex", ""), + decoration: normalTextFieldStyle("Sex *", ""), value: selectedSex, validator: FormBuilderValidators.required( errorText: "This field is required"), @@ -278,9 +292,9 @@ class _SpouseEditAlertState extends State { : widget.familyBackground.relatedPerson?.heightM .toString(), name: "height", - validator: integerAndNumeric, + validator: FormBuilderValidators.numeric(errorText: "Enter a number"), keyboardType: TextInputType.number, - decoration: normalTextFieldStyle("Height", ""), + decoration: normalTextFieldStyle("Height (Meter)", ""), ), ), const SizedBox( @@ -297,9 +311,9 @@ class _SpouseEditAlertState extends State { : widget.familyBackground.relatedPerson?.weightKg .toString(), name: "weight", - validator: integerAndNumeric, + validator: FormBuilderValidators.numeric(errorText: "Enter a number"), keyboardType: TextInputType.number, - decoration: normalTextFieldStyle("Weight", ""), + decoration: normalTextFieldStyle("Weight (Kg.)", ""), ), ), const SizedBox( @@ -365,8 +379,13 @@ class _SpouseEditAlertState extends State { builder: (context, setState) { ////Position return SearchField( + inputFormatters: [ + UpperCaseTextFormatter() + ], controller: oldPositionController, - itemHeight: 50, + suggestionDirection: + SuggestionDirection.down, + itemHeight: 100, suggestionsDecoration: box1(), suggestions: widget.positions .map((Position position) => @@ -381,7 +400,8 @@ class _SpouseEditAlertState extends State { 10), child: ListTile( title: Text(position - .title!), + .title!,overflow: TextOverflow + .visible,), )))) .toList(), focusNode: positionFocusNode, @@ -403,27 +423,30 @@ class _SpouseEditAlertState extends State { }); }, ////EMPTY WIDGET - emptyWidget: EmptyWidget( - title: "Add Position", - controller: addPositionController, - onpressed: () { - setState(() { - Position newAgencyPosition = - Position( - id: null, - title: - addPositionController - .text - .toUpperCase()); - - widget.positions.insert( - 0, newAgencyPosition); - - addPositionController.text = - ""; - Navigator.pop(context); - }); - }), + emptyWidget: Container( + padding: const EdgeInsets.only(top: 25), + child: EmptyWidget( + title: "Add Position", + controller: addPositionController, + onpressed: () { + setState(() { + Position newAgencyPosition = + Position( + id: null, + title: + addPositionController + .text + .toUpperCase()); + + widget.positions.insert( + 0, newAgencyPosition); + + addPositionController.text = + ""; + Navigator.pop(context); + }); + }), + ), validator: (position) { if (position!.isEmpty) { return "This field is required"; @@ -441,8 +464,13 @@ class _SpouseEditAlertState extends State { children: [ ////Company SearchField( - controller: oldAgencyController, - itemHeight: 70, + inputFormatters: [ + UpperCaseTextFormatter() + ], + controller: oldAgencyController, + itemHeight: 100, + suggestionDirection: + SuggestionDirection.down, focusNode: agencyFocusNode, suggestions: widget.agencies .map((Agency agency) => @@ -454,7 +482,7 @@ class _SpouseEditAlertState extends State { agency.name!, overflow: TextOverflow - .ellipsis, + .visible, ), subtitle: Text(agency .privateEntity == @@ -471,8 +499,13 @@ class _SpouseEditAlertState extends State { "Agency *", "") .copyWith( suffixIcon: - const Icon(Icons - .arrow_drop_down)), + IconButton( + icon: const Icon( + Icons.arrow_drop_down), + onPressed: () { + agencyFocusNode.unfocus(); + }, + )), onSuggestionTap: (agency) { setState(() { selectedAgency = @@ -587,9 +620,14 @@ class _SpouseEditAlertState extends State { "") .copyWith( suffixIcon: - const Icon( - Icons - .arrow_drop_down)), + IconButton( + icon: const Icon(Icons + .arrow_drop_down), + onPressed: () { + agencyCategoryFocusNode + .unfocus(); + }, + )), validator: (value) { if (value!.isEmpty) { return "This field is required"; @@ -647,7 +685,9 @@ class _SpouseEditAlertState extends State { ), ////Company Address FormBuilderTextField( - initialValue: widget.familyBackground.companyAddress, + initialValue: widget + .familyBackground + .companyAddress, validator: FormBuilderValidators .required( errorText: @@ -662,8 +702,9 @@ class _SpouseEditAlertState extends State { ), ////Company Tel Number FormBuilderTextField( - initialValue: - widget.familyBackground.companyContactNumber, + initialValue: widget + .familyBackground + .companyContactNumber, validator: FormBuilderValidators .required( errorText: @@ -734,13 +775,13 @@ class _SpouseEditAlertState extends State { .currentState?.value['height'] == null ? null - : double.parse( + : double.tryParse( _formKey.currentState?.value['height']); double? weight = _formKey .currentState?.value['weight'] == null ? null - : double.parse( + : double.tryParse( _formKey.currentState?.value['weight']); Relationship relationship = Relationship( id: 1, @@ -772,15 +813,17 @@ class _SpouseEditAlertState extends State { ); FamilyBackground familyBackground = FamilyBackground( - company: hasOccupation?company:null, - position: hasOccupation? position:null, + company: hasOccupation ? company : null, + position: hasOccupation ? position : null, relatedPerson: person, relationship: relationship, - companyAddress: hasOccupation? companyAddress:null, + companyAddress: + hasOccupation ? companyAddress : null, emergencyContact: emergnecyContacts, incaseOfEmergency: incaseOfEmergency, - companyContactNumber:hasOccupation? - companyContactNumber:null); + companyContactNumber: hasOccupation + ? companyContactNumber + : null); Navigator.of(context).pop(); widget.familyBloc.add(Updatefamily( diff --git a/lib/screens/profile/components/basic_information/identification/add_modal.dart b/lib/screens/profile/components/basic_information/identification/add_modal.dart index fef97a8..ef9272e 100644 --- a/lib/screens/profile/components/basic_information/identification/add_modal.dart +++ b/lib/screens/profile/components/basic_information/identification/add_modal.dart @@ -1,7 +1,5 @@ import 'package:date_time_picker/date_time_picker.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; @@ -11,14 +9,13 @@ import 'package:searchfield/searchfield.dart'; import 'package:unit2/bloc/profile/primary_information/identification/identification_bloc.dart'; import 'package:unit2/model/utils/industry_class.dart'; import 'package:unit2/theme-data.dart/btn-style.dart'; +import 'package:unit2/utils/formatters.dart'; import 'package:unit2/utils/text_container.dart'; - import '../../../../../model/location/city.dart'; import '../../../../../model/location/country.dart'; import '../../../../../model/location/provinces.dart'; import '../../../../../model/location/region.dart'; import '../../../../../model/profile/basic_information/identification_information.dart'; -import '../../../../../model/profile/voluntary_works.dart'; import '../../../../../model/utils/agency.dart'; import '../../../../../model/utils/category.dart'; import '../../../../../theme-data.dart/box_shadow.dart'; @@ -63,7 +60,8 @@ class _AddIdentificationScreenState extends State { Country? selectedCountry; List? provinces; List? citymuns; - + DateTime? issuedDate; + DateTime? expirationDate; final formKey = GlobalKey(); List? requiredAgency = [ Agency( @@ -115,6 +113,15 @@ class _AddIdentificationScreenState extends State { Agency(id: 0, name: "OTHERS"), ]; List requiredIds = [173, 2, 3, 4, 165]; + @override + void dispose() { + dateIssuedController.dispose(); + addAgencyController.dispose(); + expirationController.dispose(); + agencyFocusNode.dispose(); + agencyCategoryFocusNode.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { @@ -123,9 +130,7 @@ class _AddIdentificationScreenState extends State { return false; }, builder: (context, state) { - if (state is IdentificationAddingState) { - for (var agency in state.addedAgencies) { if (requiredIds.contains(agency.id)) { Agency? newAgency = requiredAgency @@ -136,244 +141,217 @@ class _AddIdentificationScreenState extends State { } } return Padding( - padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 24), + padding: const EdgeInsets.symmetric(vertical: 32, horizontal: 24), child: FormBuilder( key: formKey, child: SizedBox( height: screenHeight * 90, - child: ListView( + child: Column( children: [ - Column( - children: [ - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - FormBuilderDropdown( - isExpanded: true, - decoration: normalTextFieldStyle( - "Select Agency", "Select Agency"), - name: "requiredAgency", - items: requiredAgency! - .map>( - (Agency agency) { - return DropdownMenuItem( - value: agency, - child: Container( - width: double.infinity, - decoration: - box1().copyWith(boxShadow: []), - child: Text(agency.name!)), - ); - }).toList(), - onChanged: (value) { - selectedAgency = value; - - if (value!.id == 0) { - setState(() { - otherAgency = true; - }); - } else { - isPrivate = value.privateEntity!; - setState(() { - otherAgency = false; - }); - } - }, - ), - ////Other agency - SizedBox( - child: otherAgency - ? StatefulBuilder( - builder: (context, setState) { - return Column( - children: [ - ////Company - SizedBox( - child: SearchField( - itemHeight: 70, - focusNode: agencyFocusNode, - suggestions: state.agencies - .map((Agency agency) => - SearchFieldListItem( - agency.name!, - item: agency, - child: ListTile( - title: Text( - agency - .name!, - overflow: - TextOverflow - .ellipsis, - ), - subtitle: Text(agency - .privateEntity == - true - ? "Private" - : agency.privateEntity == - false - ? "Government" - : ""), - ))) - .toList(), - searchInputDecoration: - normalTextFieldStyle( - "Agency *", "") - .copyWith( - suffixIcon: - const Icon(Icons - .arrow_drop_down)), - onSuggestionTap: (agency) { - setState(() { - selectedAgency = - agency.item; + Flexible( + child: ListView( + children: [ + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderDropdown( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + isExpanded: true, + decoration: normalTextFieldStyle( + "Select Agency", "Select Agency"), + name: "requiredAgency", + items: requiredAgency! + .map>( + (Agency agency) { + return DropdownMenuItem( + value: agency, + child: Container( + width: double.infinity, + decoration: + box1().copyWith(boxShadow: []), + child: Text( + agency.name!, + maxLines: 3, + )), + ); + }).toList(), + onChanged: (value) { + selectedAgency = value; - if (selectedAgency! - .privateEntity == - null) { - showIsPrivateRadio = - true; - } else { - showIsPrivateRadio = - false; - } - - agencyFocusNode - .unfocus(); - }); - }, - validator: (agency) { - if (agency!.isEmpty) { - return "This field is required"; - } - return null; - }, - emptyWidget: EmptyWidget( - controller: - addAgencyController, - onpressed: () { - setState(() { - Agency newAgency = Agency( - id: null, - name: addAgencyController - .text - .toUpperCase(), - category: null, - privateEntity: - null); - - state.agencies - .insert(0, - newAgency); - selectedAgency = - newAgency; - addAgencyController - .text = ""; - showAgency = true; + if (value!.id == 0) { + setState(() { + otherAgency = true; + }); + } else { + isPrivate = value.privateEntity!; + setState(() { + otherAgency = false; + }); + } + }, + ), + SizedBox( + height: otherAgency ? 12 : 0, + ), + ////Other agency + SizedBox( + child: otherAgency + ? StatefulBuilder( + builder: (context, setState) { + return Column( + children: [ + ////Company + SizedBox( + child: SearchField( + inputFormatters: [ + UpperCaseTextFormatter() + ], + itemHeight: 100, + focusNode: + agencyFocusNode, + suggestions: state + .agencies + .map((Agency + agency) => + SearchFieldListItem( + agency.name!, + item: agency, + child: + ListTile( + title: Text( + agency + .name!, + overflow: + TextOverflow + .visible, + ), + subtitle: Text(agency.privateEntity == + true + ? "Private" + : agency.privateEntity == + false + ? "Government" + : ""), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle( + "Agency *", "") + .copyWith( + suffixIcon: + GestureDetector( + child: const Icon( + Icons.arrow_drop_down, + ), + onTap: () => + agencyFocusNode + .unfocus(), + )), + onSuggestionTap: + (agency) { + setState(() { + selectedAgency = + agency.item; + if (selectedAgency! + .privateEntity == + null) { showIsPrivateRadio = true; - - Navigator.pop( - context); - }); - }, - title: "Add Agency")), - ), - - SizedBox( - height: showAgency ? 8 : 0, - ), - ////SHOW CATEGORY AGENCY - SizedBox( - child: showAgency - ? SearchField( - focusNode: - agencyCategoryFocusNode, - itemHeight: 70, - suggestions: state - .agencyCategory - .map((Category - category) => - SearchFieldListItem( - category - .name!, - item: - category, - child: - ListTile( - title: Text( - category - .name!), - subtitle: Text(category - .industryClass! - .name!), - ))) - .toList(), - emptyWidget: Container( - height: 100, - decoration: box1(), - child: const Center( - child: Text( - "No result found ...")), - ), - onSuggestionTap: - (agencyCategory) { - setState(() { - selectedCategoty = - agencyCategory - .item; - agencyCategoryFocusNode - .unfocus(); - selectedAgency = Agency( - id: null, - name: - selectedAgency! - .name, - category: - selectedCategoty, - privateEntity: - null); - }); - }, - searchInputDecoration: - normalTextFieldStyle( - "Category *", - "") - .copyWith( - suffixIcon: - const Icon( - Icons.arrow_drop_down)), - validator: (value) { - if (value!.isEmpty) { - return "This field is required"; + } else { + showIsPrivateRadio = + false; } - return null; - }, - ) - : const SizedBox(), - ), - const SizedBox( - height: 8, - ), - ////PRVIATE SECTOR - SizedBox( - width: screenWidth, - child: showIsPrivateRadio - ? FormBuilderSwitch( - initialValue: - isPrivate, - title: Text(isPrivate - ? "YES" - : "NO"), - decoration: - normalTextFieldStyle( - "Private Entity?", - ""), - ////onvhange private sector - onChanged: (value) { + + agencyFocusNode + .unfocus(); + }); + }, + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + emptyWidget: EmptyWidget( + controller: + addAgencyController, + onpressed: () { setState(() { - isPrivate = - value!; + Agency newAgency = Agency( + id: null, + name: addAgencyController + .text + .toUpperCase(), + category: + null, + privateEntity: + null); + + state.agencies + .insert(0, + newAgency); + selectedAgency = + newAgency; + addAgencyController + .text = ""; + showAgency = true; + + showIsPrivateRadio = + true; + + Navigator.pop( + context); + }); + }, + title: "Add Agency")), + ), + + SizedBox( + height: showAgency ? 12 : 0, + ), + ////SHOW CATEGORY AGENCY + SizedBox( + child: showAgency + ? SearchField( + focusNode: + agencyCategoryFocusNode, + itemHeight: 70, + suggestions: state + .agencyCategory + .map((Category + category) => + SearchFieldListItem( + category + .name!, + item: + category, + child: + ListTile( + title: Text( + category.name!), + subtitle: Text(category + .industryClass! + .name!), + ))) + .toList(), + emptyWidget: + Container( + height: 100, + decoration: box1(), + child: const Center( + child: Text( + "No result found ...")), + ), + onSuggestionTap: + (agencyCategory) { + setState(() { + selectedCategoty = + agencyCategory + .item; + agencyCategoryFocusNode + .unfocus(); selectedAgency = Agency( id: null, name: @@ -382,404 +360,544 @@ class _AddIdentificationScreenState extends State { category: selectedCategoty, privateEntity: - isPrivate); - agencyFocusNode - .unfocus(); - agencyCategoryFocusNode - .unfocus(); + null); }); }, - - name: 'isPrivate', - validator: - FormBuilderValidators - .required(), + searchInputDecoration: + normalTextFieldStyle( + "Category *", + "") + .copyWith( + suffixIcon: + GestureDetector( + child: const Icon( + Icons + .arrow_drop_down, + ), + onTap: () => + agencyCategoryFocusNode + .unfocus(), + )), + validator: (value) { + if (value! + .isEmpty) { + return "This field is required"; + } + return null; + }, ) - : const SizedBox()), - ], - ); - }) - : const SizedBox(), - ), - ], - ); - }), - SizedBox( - height: otherAgency ? 8 : 0, - ), + : const SizedBox(), + ), + const SizedBox( + height: 12, + ), + ////PRVIATE SECTOR + SizedBox( + width: screenWidth, + child: showIsPrivateRadio + ? FormBuilderSwitch( + initialValue: + isPrivate, + title: Text( + isPrivate + ? "YES" + : "NO"), + decoration: + normalTextFieldStyle( + "Private Entity?", + ""), + ////onvhange private sector + onChanged: (value) { + setState(() { + isPrivate = + value!; + selectedAgency = Agency( + id: null, + name: selectedAgency! + .name, + category: + selectedCategoty, + privateEntity: + isPrivate); + agencyFocusNode + .unfocus(); + agencyCategoryFocusNode + .unfocus(); + }); + }, - const SizedBox( - height: 8, - ), - //// Identification numner - FormBuilderTextField( - validator: FormBuilderValidators.required( - errorText: "This field is required"), - name: "identification_number", - decoration: normalTextFieldStyle( - "Identification Number *", ""), - ), - const SizedBox( - height: 8, - ), - Row( - children: [ - //// Date Issued - Flexible( - flex: 1, - child: DateTimePicker( - controller: dateIssuedController, - use24HourFormat: false, - icon: const Icon(Icons.date_range), - firstDate: DateTime(1970), - lastDate: DateTime(2100), - timeHintText: "Date Issued", + name: 'isPrivate', + validator: + FormBuilderValidators + .required(), + ) + : const SizedBox()), + ], + ); + }) + : const SizedBox(), + ), + ], + ); + }), + SizedBox( + height: otherAgency ? 8 : 0, + ), + + const SizedBox( + height: 8, + ), + //// Identification numner + FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "identification_number", + decoration: normalTextFieldStyle( + "Identification Number *", ""), + ), + const SizedBox( + height: 12, + ), + StatefulBuilder(builder: (context, setState) { + return Row( + children: [ + //// Date Issued + Flexible( + flex: 1, + child: DateTimePicker( + type: DateTimePickerType.date, + controller: dateIssuedController, + use24HourFormat: false, + icon: const Icon(Icons.date_range), + selectableDayPredicate: (date) { + if (expirationDate != null && + expirationDate! + .microsecondsSinceEpoch <= + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, + onChanged: (value) { + setState(() { + issuedDate = DateTime.parse(value); + }); + }, + initialDate: expirationDate == null + ? DateTime.now() + : expirationDate!.subtract( + const Duration(days: 1)), + firstDate: DateTime(1990), + lastDate: DateTime(2100), + timeHintText: "Date Issued", + decoration: normalTextFieldStyle( + "Date Issued *", + "Date Issued *") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + )), + const SizedBox( + width: 8, + ), + //// Expiration Date + Flexible( + flex: 1, + child: DateTimePicker( + type: DateTimePickerType.date, + controller: expirationController, + use24HourFormat: false, + icon: const Icon(Icons.date_range), + firstDate: DateTime(1990), + lastDate: DateTime(2100), + selectableDayPredicate: (date) { + if (issuedDate != null && + issuedDate! + .microsecondsSinceEpoch >= + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, + timeHintText: "Expiration date", + decoration: normalTextFieldStyle( + "Expiration Date *", + "Expiration Date *") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + initialDate: issuedDate == null + ? DateTime.now() + : issuedDate! + .add(const Duration(days: 1)), + onChanged: (value) { + setState(() { + expirationDate = + DateTime.parse(value); + }); + }, + )), + ], + ); + }), + const SizedBox( + height: 12, + ), + //// as pdf reference + StatefulBuilder(builder: (context, setState) { + return FormBuilderSwitch( + initialValue: asPdfReference, + activeColor: second, + onChanged: (value) { + setState(() { + asPdfReference = value!; + }); + }, + decoration: normalTextFieldStyle( + "As PDF Reference?", ''), + name: 'pdf_reference', + title: Text(asPdfReference ? "YES" : "NO"), + ); + }), + const SizedBox( + height: 12, + ), + //// OVERSEAS + //// OVERSEAS + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, decoration: normalTextFieldStyle( - "Date Issued *", "Date Issued *") - .copyWith( - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - )), - initialValue: null, - )), - const SizedBox( - width: 8, - ), - //// Expiration Date - Flexible( - flex: 1, - child: DateTimePicker( - controller: expirationController, - use24HourFormat: false, - icon: const Icon(Icons.date_range), - firstDate: DateTime(1970), - lastDate: DateTime(2100), - timeHintText: "Expiration date", - decoration: normalTextFieldStyle( - "Expiration Date *", - "Expiration Date *") - .copyWith( - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - )), - initialValue: null, - )), - ], - ), - const SizedBox( - height: 8, - ), - //// as pdf reference - StatefulBuilder(builder: (context, setState) { - return FormBuilderSwitch( - initialValue: asPdfReference, - activeColor: second, - onChanged: (value) { - setState(() { - asPdfReference = value!; - }); - }, - decoration: - normalTextFieldStyle("As PDF Reference?", ''), - name: 'pdf_reference', - title: Text(asPdfReference ? "YES" : "NO"), - ); - }), - const SizedBox( - height: 8, - ), - //// OVERSEAS - //// OVERSEAS - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - setState(() { - overseas = value!; - }); - }, - decoration: normalTextFieldStyle( - "Overseas Address?", ''), - name: 'overseas', - title: Text(overseas ? "YES" : "NO"), - ), - SizedBox( - height: overseas == true ? 8 : 0, - ), - SizedBox( - child: overseas == false - ? Column( - children: [ - const SizedBox( - height: 12, - ), - ////REGION DROPDOWN - DropdownButtonFormField( - isExpanded: true, - autovalidateMode: AutovalidateMode - .onUserInteraction, - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - onChanged: - (Region? region) async { - if (selectedRegion != region) { - setState(() { - provinceCall = true; - }); - selectedRegion = region; - try { - provinces = await LocationUtils - .instance - .getProvinces( - regionCode: - selectedRegion! + "Overseas Address?", ''), + name: 'overseas', + title: Text(overseas ? "YES" : "NO"), + ), + SizedBox( + height: overseas == true ? 8 : 0, + ), + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + DropdownButtonFormField( + isExpanded: true, + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + onChanged: + (Region? region) async { + if (selectedRegion != + region) { + setState(() { + provinceCall = true; + }); + selectedRegion = region; + try { + provinces = await LocationUtils + .instance + .getProvinces( + regionCode: + selectedRegion! + .code + .toString()); + selectedProvince = + provinces![0]; + setState(() { + provinceCall = false; + cityCall = true; + }); + try { + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! .code .toString()); - selectedProvince = - provinces![0]; - setState(() { - provinceCall = false; - cityCall = true; - }); - try { - citymuns = await LocationUtils - .instance - .getCities( - code: selectedProvince! - .code - .toString()); - selectedMunicipality = - citymuns![0]; - setState(() { - cityCall = false; - }); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + }); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read< + IdentificationBloc>() + .add(ShowErrorState( + message: e + .toString())); + } } catch (e) { - NavigationService - .navigatorKey - .currentContext - ?.read< + context + .read< IdentificationBloc>() .add(ShowErrorState( message: e .toString())); } - } catch (e) { - context - .read< - IdentificationBloc>() - .add(ShowErrorState( - message: - e.toString())); } - } - }, - value: selectedRegion, - decoration: normalTextFieldStyle( - "Region*", "Region"), - items: state.regions.map< - DropdownMenuItem>( - (Region region) { - return DropdownMenuItem( - value: region, - child: Text( - region.description!)); - }).toList(), - ), - const SizedBox( - height: 8, - ), - //// PROVINCE DROPDOWN - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: provinceCall, - child: DropdownButtonFormField< - Province?>( - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - value: selectedProvince, - onChanged: (Province? - province) async { - if (selectedProvince != - province) { - setState(() { - cityCall = true; - }); - selectedProvince = - province; - try { - citymuns = await LocationUtils - .instance - .getCities( - code: selectedProvince! - .code - .toString()); - selectedMunicipality = - citymuns![0]; - setState(() { - cityCall = false; - }); - } catch (e) { - context - .read< - IdentificationBloc>() - .add(ShowErrorState( - message: e - .toString())); - } - } - }, - items: provinces == null - ? [] - : provinces!.map< - DropdownMenuItem< - Province>>( - (Province - province) { - return DropdownMenuItem( - value: province, - child: - FittedBox( - child: Text( - province - .description!), - )); - }).toList(), - decoration: - normalTextFieldStyle( - "Province*", - "Province")), + }, + value: selectedRegion, + decoration: + normalTextFieldStyle( + "Region*", "Region"), + items: state.regions.map< + DropdownMenuItem< + Region>>( + (Region region) { + return DropdownMenuItem< + Region>( + value: region, + child: Text( + region.description!)); + }).toList(), ), - ), - ////CITY MUNICIPALITY - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: cityCall, - child: DropdownButtonFormField< - CityMunicipality>( - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - isExpanded: true, - onChanged: - (CityMunicipality? city) { - if (selectedMunicipality != - city) { - selectedMunicipality = - city; - } - }, - decoration: - normalTextFieldStyle( - "Municipality*", - "Municipality"), - value: selectedMunicipality, - items: citymuns == null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality c) { - return DropdownMenuItem( - value: c, - child: Text(c - .description!)); - }).toList(), + const SizedBox( + height: 12, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + + isExpanded: true, + value: selectedProvince, + onChanged: (Province? + province) async { + if (selectedProvince != + province) { + setState(() { + cityCall = true; + }); + selectedProvince = + province; + try { + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code + .toString()); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + }); + } catch (e) { + context + .read< + IdentificationBloc>() + .add(ShowErrorState( + message: e + .toString())); + } + } + }, + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province + province) { + return DropdownMenuItem( + value: + province, + child: + FittedBox( + child: Text( + province + .description!), + )); + }).toList(), + decoration: + normalTextFieldStyle( + "Province*", + "Province")), ), ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: + DropdownButtonFormField< + CityMunicipality>( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + isExpanded: true, + onChanged: + (CityMunicipality? + city) { + if (selectedMunicipality != + city) { + selectedMunicipality = + city; + } + }, + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality + c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), + ), + ), + ), + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( + height: 60, + child: DropdownButtonFormField< + Country>( + isExpanded: true, + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + items: state.countries.map< + DropdownMenuItem< + Country>>( + (Country country) { + return DropdownMenuItem< + Country>( + value: country, + child: FittedBox( + child: Text( + country.name!))); + }).toList(), + value: selectedCountry, + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) { + selectedCountry = value; + }, ), - ], - ) - //// COUNTRY DROPDOWN - : SizedBox( - height: 60, - child: - DropdownButtonFormField( - isExpanded: true, - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - items: state.countries - .map>( - (Country country) { - return DropdownMenuItem( - value: country, - child: FittedBox( - child: - Text(country.name!))); - }).toList(), - value: selectedCountry, - decoration: normalTextFieldStyle( - "Country*", "Country"), - onChanged: (Country? value) { - selectedCountry = value; - }, ), - ), - ), - ], - ); - }), - ], + ), + ], + ); + }), + ], + ), ), - SizedBox( - width: double.infinity, - height: 60, - child: ElevatedButton( - style: mainBtnStyle( - primary, Colors.transparent, second), - onPressed: () { - if (formKey.currentState! - .saveAndValidate()) { - Country country = selectedCountry ??= - Country( - id: 175, - name: 'Philippines', - code: 'PH'); - IssuedAt issuedAt = IssuedAt( - id:null, - barangay: null, - - addressCategory: null, - issuedAtClass: null, - cityMunicipality: overseas?null:selectedMunicipality - , - country: country); - if (selectedCategoty != null) { - selectedAgency = Agency( - id: selectedAgency?.id, - name: selectedAgency!.name, - category: selectedCategoty, - privateEntity: isPrivate); - } - Identification identification = Identification(id: null, agency: selectedAgency, issuedAt: issuedAt, asPdfReference: asPdfReference, identificationNumber: formKey.currentState!.value['identification_number'],dateIssued: dateIssuedController.text.isEmpty?null:DateTime.parse(dateIssuedController.text),expirationDate: expirationController.text.isEmpty?null:DateTime.tryParse(expirationController.text)); - final progress = ProgressHUD.of(context); + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState!.saveAndValidate()) { + IssuedAt issuedAt; + if (overseas) { + issuedAt = IssuedAt( + id: null, + barangay: null, + addressCategory: null, + issuedAtClass: null, + cityMunicipality: null, + country: selectedCountry); + } else { + issuedAt = IssuedAt( + id: null, + barangay: null, + addressCategory: null, + issuedAtClass: null, + cityMunicipality: overseas + ? null + : selectedMunicipality, + country: Country( + id: 175, + name: 'Philippines', + code: 'PH')); + } + if (selectedCategoty != null) { + selectedAgency = Agency( + id: selectedAgency?.id, + name: selectedAgency!.name, + category: selectedCategoty, + privateEntity: isPrivate); + } + Identification identification = Identification( + id: null, + agency: selectedAgency, + issuedAt: issuedAt, + asPdfReference: asPdfReference, + identificationNumber: formKey.currentState! + .value['identification_number'], + dateIssued: + dateIssuedController.text.isEmpty + ? null + : DateTime.parse( + dateIssuedController.text), + expirationDate: + expirationController.text.isEmpty + ? null + : DateTime.tryParse( + expirationController.text)); + final progress = ProgressHUD.of(context); progress!.showWithText("Loading..."); - context.read().add(AddIdentification(identification: identification, profileId: widget.profileId, token: widget.token)); - } - - }, - child: const Text(submit)), - ) - + context.read().add( + AddIdentification( + identification: identification, + profileId: widget.profileId, + token: widget.token)); + } + }, + child: const Text(submit)), + ), + const SizedBox( + height: 24, + ), ], ), )), diff --git a/lib/screens/profile/components/basic_information/identification/edit_modal.dart b/lib/screens/profile/components/basic_information/identification/edit_modal.dart index 31fee18..eb6a8c9 100644 --- a/lib/screens/profile/components/basic_information/identification/edit_modal.dart +++ b/lib/screens/profile/components/basic_information/identification/edit_modal.dart @@ -1,34 +1,25 @@ import 'package:date_time_picker/date_time_picker.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; -import 'package:searchfield/searchfield.dart'; import 'package:unit2/bloc/profile/primary_information/identification/identification_bloc.dart'; -import 'package:unit2/model/utils/industry_class.dart'; import 'package:unit2/theme-data.dart/btn-style.dart'; import 'package:unit2/utils/text_container.dart'; - import '../../../../../model/location/city.dart'; import '../../../../../model/location/country.dart'; import '../../../../../model/location/provinces.dart'; import '../../../../../model/location/region.dart'; import '../../../../../model/profile/basic_information/identification_information.dart'; -import '../../../../../model/profile/voluntary_works.dart'; import '../../../../../model/utils/agency.dart'; import '../../../../../model/utils/category.dart'; -import '../../../../../theme-data.dart/box_shadow.dart'; import '../../../../../theme-data.dart/colors.dart'; import '../../../../../theme-data.dart/form-style.dart'; import '../../../../../utils/global.dart'; import '../../../../../utils/global_context.dart'; import '../../../../../utils/location_utilities.dart'; -import '../../../shared/add_for_empty_search.dart'; - class EditIdentificationScreen extends StatefulWidget { final int profileId; final String token; @@ -62,7 +53,15 @@ class _EditIdentificationScreenState extends State { List? citymuns; final formKey = GlobalKey(); - +@override + void dispose() { + dateIssuedController.dispose(); + addAgencyController.dispose(); + expirationController.dispose(); + agencyFocusNode.dispose(); + agencyCategoryFocusNode.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { return BlocBuilder( @@ -88,338 +87,380 @@ class _EditIdentificationScreenState extends State { key: formKey, child: SizedBox( height: screenHeight * 90, - child: ListView( + child: Column( children: [ - Column( - children: [ - FormBuilderTextField( - initialValue: state.identification.agency!.name, - enabled: false, - name: "", - decoration: normalTextFieldStyle("", "").copyWith( - filled: true, fillColor: Colors.black12), - ), - SizedBox( - height: otherAgency ? 8 : 0, - ), - - const SizedBox( - height: 12, - ), - //// Identification numner - FormBuilderTextField( - initialValue: - state.identification.identificationNumber, - validator: FormBuilderValidators.required( - errorText: "This field is required"), - name: "identification_number", - decoration: normalTextFieldStyle( - "Identification Number *", ""), - ), - const SizedBox( - height: 12, - ), - Row( - children: [ - //// Date Issued - Flexible( - flex: 1, - child: DateTimePicker( - controller: dateIssuedController, - use24HourFormat: false, - icon: const Icon(Icons.date_range), - firstDate: DateTime(1970), - lastDate: DateTime(2100), - timeHintText: "Date Issued", + Flexible( + child: ListView( + children: [ + FormBuilderTextField( + initialValue: state.identification.agency!.name, + enabled: false, + name: "", + decoration: normalTextFieldStyle("", "").copyWith( + filled: true, fillColor: Colors.black12), + ), + SizedBox( + height: otherAgency ? 8 : 0, + ), + + const SizedBox( + height: 12, + ), + //// Identification numner + FormBuilderTextField( + initialValue: + state.identification.identificationNumber, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "identification_number", + decoration: normalTextFieldStyle( + "Identification Number *", ""), + ), + const SizedBox( + height: 12, + ), + StatefulBuilder( + builder: (context,setState) { + return Row( + children: [ + //// Date Issued + Flexible( + flex: 1, + child: DateTimePicker( + controller: dateIssuedController, + use24HourFormat: false, + icon: const Icon(Icons.date_range), + firstDate: DateTime(1990), + lastDate: DateTime(2100), + timeHintText: "Date Issued", + decoration: normalTextFieldStyle( + "Date Issued ", "Date Issued *") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + onChanged: (value){ + setState((){ + issuedDate = value; + }); + }, + selectableDayPredicate: (date) { + if (expDate != null && + DateTime.parse(expDate!) + .microsecondsSinceEpoch <= + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, + initialDate: expDate != null + ? DateTime.now() + : DateTime.parse(expDate!).subtract( + const Duration(days: 1)), + )), + + const SizedBox( + width: 12, + ), + //// Expiration Date + Flexible( + flex: 1, + child: DateTimePicker( + controller: expirationController, + use24HourFormat: false, + icon: const Icon(Icons.date_range), + firstDate: DateTime(1990), + lastDate: DateTime(2100), + timeHintText: "Expiration date", + onChanged: (value){ + setState((){ + expDate = value; + }); + }, + decoration: normalTextFieldStyle( + "Expiration Date", + "Expiration Date") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + selectableDayPredicate: (date) { + if (issuedDate != null && + DateTime.parse(issuedDate!) + .microsecondsSinceEpoch >= + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, + initialDate: issuedDate != null + ? DateTime.now() + : DateTime.parse(issuedDate!).add( + const Duration(days: 1)), + )), + ], + ); + } + ), + const SizedBox( + height: 12, + ), + + //// OVERSEAS + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, decoration: normalTextFieldStyle( - "Date Issued *", "Date Issued *") - .copyWith( - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - )), - initialValue: null, - )), - const SizedBox( - width: 12, - ), - //// Expiration Date - Flexible( - flex: 1, - child: DateTimePicker( - controller: expirationController, - use24HourFormat: false, - icon: const Icon(Icons.date_range), - firstDate: DateTime(1970), - lastDate: DateTime(2100), - timeHintText: "Expiration date", - decoration: normalTextFieldStyle( - "Expiration Date *", - "Expiration Date *") - .copyWith( - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - )), - initialValue: null, - )), - ], - ), - const SizedBox( - height: 12, - ), - - //// OVERSEAS - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - setState(() { - overseas = value!; - }); - }, - decoration: normalTextFieldStyle( - "Overseas Address?", ''), - name: 'overseas', - title: Text(overseas ? "YES" : "NO"), - ), - SizedBox( - height: overseas == true ? 8 : 0, - ), - SizedBox( - child: overseas == false - ? Column( - children: [ - const SizedBox( - height: 12, - ), - ////REGION DROPDOWN - DropdownButtonFormField( - isExpanded: true, - autovalidateMode: AutovalidateMode - .onUserInteraction, - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - onChanged: - (Region? region) async { - if (selectedRegion != region) { - setState(() { - provinceCall = true; - }); - selectedRegion = region; - try { - provinces = await LocationUtils - .instance - .getProvinces( - regionCode: - selectedRegion! - .code - .toString()); - selectedProvince = - provinces![0]; + "Overseas Address?", ''), + name: 'overseas', + title: Text(overseas ? "YES" : "NO"), + ), + SizedBox( + height: overseas == true ? 12 : 0, + ), + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + DropdownButtonFormField( + isExpanded: true, + autovalidateMode: AutovalidateMode + .onUserInteraction, + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + onChanged: + (Region? region) async { + if (selectedRegion != region) { setState(() { - provinceCall = false; - cityCall = true; + provinceCall = true; }); + selectedRegion = region; try { - citymuns = await LocationUtils + provinces = await LocationUtils .instance - .getCities( - code: selectedProvince! - .code - .toString()); - selectedMunicipality = - citymuns![0]; - setState(() { - cityCall = false; - }); - } catch (e) { - NavigationService - .navigatorKey - .currentContext - ?.read< - IdentificationBloc>() - .add(ShowErrorState( - message: e - .toString())); - } - } catch (e) { - context - .read< - IdentificationBloc>() - .add(ShowErrorState( - message: - e.toString())); - } - } - }, - value: selectedRegion, - decoration: normalTextFieldStyle( - "Region*", "Region"), - items: state.regions.map< - DropdownMenuItem>( - (Region region) { - return DropdownMenuItem( - value: region, - child: Text( - region.description!)); - }).toList(), - ), - const SizedBox( - height: 8, - ), - //// PROVINCE DROPDOWN - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: provinceCall, - child: DropdownButtonFormField< - Province?>( - value: selectedProvince, - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - onChanged: (Province? - province) async { - if (selectedProvince != - province) { - setState(() { - cityCall = true; - }); - selectedProvince = - province; - try { - citymuns = await LocationUtils - .instance - .getCities( - code: selectedProvince! + .getProvinces( + regionCode: + selectedRegion! .code .toString()); - selectedMunicipality = - citymuns![0]; - setState(() { - cityCall = false; - }); - } catch (e) { - context - .read< - IdentificationBloc>() - .add(ShowErrorState( - message: e - .toString())); - } + selectedProvince = + provinces![0]; + setState(() { + provinceCall = false; + cityCall = true; + }); + try { + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code + .toString()); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + }); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read< + IdentificationBloc>() + .add(ShowErrorState( + message: e + .toString())); } - }, - items: provinces == null - ? [] - : provinces!.map< - DropdownMenuItem< - Province>>( - (Province - province) { - return DropdownMenuItem( - value: province, - child: - FittedBox( - child: Text( - province - .description!), - )); - }).toList(), - decoration: - normalTextFieldStyle( - "Province*", - "Province")), - ), - ), - ////CITY MUNICIPALITY - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: cityCall, - child: DropdownButtonFormField< - CityMunicipality>( - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - isExpanded: true, - onChanged: - (CityMunicipality? city) { - if (selectedMunicipality != - city) { - selectedMunicipality = - city; + } catch (e) { + context + .read< + IdentificationBloc>() + .add(ShowErrorState( + message: + e.toString())); } - }, - decoration: - normalTextFieldStyle( - "Municipality*", - "Municipality"), - value: selectedMunicipality, - items: citymuns == null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality c) { - return DropdownMenuItem( - value: c, - child: Text(c - .description!)); - }).toList(), + } + }, + value: selectedRegion, + decoration: normalTextFieldStyle( + "Region*", "Region"), + items: state.regions.map< + DropdownMenuItem>( + (Region region) { + return DropdownMenuItem( + value: region, + child: Text( + region.description!)); + }).toList(), + ), + const SizedBox( + height: 12, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( + value: selectedProvince, + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + onChanged: (Province? + province) async { + if (selectedProvince != + province) { + setState(() { + cityCall = true; + }); + selectedProvince = + province; + try { + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code + .toString()); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + }); + } catch (e) { + context + .read< + IdentificationBloc>() + .add(ShowErrorState( + message: e + .toString())); + } + } + }, + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province + province) { + return DropdownMenuItem( + value: province, + child: + FittedBox( + child: Text( + province + .description!), + )); + }).toList(), + decoration: + normalTextFieldStyle( + "Province *", + "Province")), ), ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: DropdownButtonFormField< + CityMunicipality>( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + isExpanded: true, + onChanged: + (CityMunicipality? city) { + if (selectedMunicipality != + city) { + selectedMunicipality = + city; + } + }, + decoration: + normalTextFieldStyle( + "Municipality *", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), + ), + ), + ), + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( + height: 60, + child: + DropdownButtonFormField( + isExpanded: true, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + items: state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox( + child: + Text(country.name!))); + }).toList(), + value: selectedCountry?.id==175?null:selectedCountry, + decoration: normalTextFieldStyle( + "Country *", "Country"), + onChanged: (Country? value) { + selectedCountry = value; + }, ), - ], - ) - //// COUNTRY DROPDOWN - : SizedBox( - height: 60, - child: - DropdownButtonFormField( - isExpanded: true, - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - items: state.countries - .map>( - (Country country) { - return DropdownMenuItem( - value: country, - child: FittedBox( - child: - Text(country.name!))); - }).toList(), - value: selectedCountry, - decoration: normalTextFieldStyle( - "Country*", "Country"), - onChanged: (Country? value) { - selectedCountry = value; - }, ), - ), - ), - ], - ); - }), - const SizedBox( - height: 12, - ), - SizedBox( + ), + ], + ); + }), + + + ], + ), + ), + SizedBox( width: double.infinity, height: 60, child: ElevatedButton( @@ -435,7 +476,7 @@ class _EditIdentificationScreenState extends State { addressCategory: state.identification.issuedAt?.addressCategory, issuedAtClass: null, cityMunicipality: selectedMunicipality, - country: selectedCountry); + country: Country(id: 175,code: "PH",name: "PHILIPPINES")); }else{ issuedAt = IssuedAt( id: state.identification.issuedAt!.id, @@ -479,9 +520,8 @@ class _EditIdentificationScreenState extends State { } }, child: const Text(submit)), - ) - ], - ), + ), + const SizedBox(height: 24,), ], ), )), diff --git a/lib/screens/profile/components/basic_information/identification_information_screen.dart b/lib/screens/profile/components/basic_information/identification_information_screen.dart index 3cd6cab..cd9836e 100644 --- a/lib/screens/profile/components/basic_information/identification_information_screen.dart +++ b/lib/screens/profile/components/basic_information/identification_information_screen.dart @@ -19,6 +19,7 @@ import 'package:unit2/widgets/error_state.dart'; import '../../../../bloc/user/user_bloc.dart'; import '../../../../utils/alerts.dart'; +import '../../../../widgets/Leadings/close_leading.dart'; class IdentificationsScreen extends StatelessWidget { const IdentificationsScreen({super.key}); @@ -29,16 +30,20 @@ class IdentificationsScreen extends StatelessWidget { int? profileId; return Scaffold( appBar: AppBar( - title: const Text(identificationScreenTitle), + title: context.watch().state is IdentificationAddingState ? const Text("Add Identification"):context.watch().state is IdentificationEditingState?const Text("Edit Identification"):const Text("Identifications"), + centerTitle: true, backgroundColor: primary, - actions: [ + actions: (context.watch().state is IdentificationLoadedState)?[ AddLeading(onPressed: () { - context - .read() - .add(ShowAddIdentificationForm()); + context.read().add(ShowAddIdentificationForm()); }) - ], + ]:(context.watch().state is IdentificationAddingState || context.watch().state is IdentificationEditingState)?[ + CloseLeading(onPressed:() { + context.read().add(LoadIdentifications()); + }) + ]:[] + ), body: ProgressHUD( padding: const EdgeInsets.all(24), @@ -90,7 +95,7 @@ class IdentificationsScreen extends StatelessWidget { }); } } - //// Updated State + //// Updated State if (state is IdentificationEditedState) { if (state.response['success']) { successAlert(context, "Update Successfull!", @@ -116,7 +121,7 @@ class IdentificationsScreen extends StatelessWidget { if (state is IdentificationDeletedState) { if (state.success) { successAlert(context, "Deletion Successfull!", - "Deleted Successfully", () { + "Identification Deleted Successfully", () { Navigator.of(context).pop(); context .read() @@ -137,6 +142,7 @@ class IdentificationsScreen extends StatelessWidget { builder: (context, state) { if (state is IdentificationLoadedState) { if (state.identificationInformation.isNotEmpty) { + String issuedAt; return ListView.builder( padding: const EdgeInsets.symmetric( vertical: 8, horizontal: 10), @@ -155,8 +161,12 @@ class IdentificationsScreen extends StatelessWidget { .identificationInformation[index] .agency! .privateEntity!; - String issuedAt = + if(state.identificationInformation[index].issuedAt?.country?.id != 175){ + issuedAt= state.identificationInformation[index].issuedAt!.country!.name!.toUpperCase(); + }else{ + issuedAt = "${state.identificationInformation[index].issuedAt!.cityMunicipality?.description!} ${state.identificationInformation[index].issuedAt!.cityMunicipality?.province!.description}"; + } return Column( mainAxisAlignment: MainAxisAlignment.start, @@ -185,22 +195,29 @@ class IdentificationsScreen extends StatelessWidget { .copyWith( fontWeight: FontWeight - .w400)), - const Divider(), + .w500,color: primary)), + const SizedBox( height: 5, ), - Row(children: [ - Expanded( - child: Text( + + + Text( "$idNumberText : $idNumber", style: Theme.of( context) .textTheme .titleSmall, ), - ), - Badge( + + + + const SizedBox( + height: 5, + ), + Text(issuedAt,style: Theme.of(context).textTheme.titleSmall,), + const SizedBox(height: 8,), + Badge( backgroundColor: success2, label: Text( @@ -217,11 +234,6 @@ class IdentificationsScreen extends StatelessWidget { color: Colors .white), )) - ]), - const SizedBox( - height: 5, - ), - Text(issuedAt), ]), ), AppPopupMenu( @@ -257,12 +269,11 @@ class IdentificationsScreen extends StatelessWidget { ProgressHUD.of(context); progress!.showWithText( "Loading..."); - if (state .identificationInformation[ index] .issuedAt - ?.country!.id == 175) { + ?.cityMunicipality!=null) { isOverseas = false; } else { isOverseas = true; @@ -283,17 +294,14 @@ class IdentificationsScreen extends StatelessWidget { }, menuItems: [ popMenuItem( - text: "Edit", + text: "Update", value: 1, icon: Icons.edit), popMenuItem( - text: "Delete", + text: "Remove", value: 2, icon: Icons.delete), - popMenuItem( - text: "Attachment", - value: 3, - icon: FontAwesome.attach) + ], icon: const Icon( Icons.more_vert, @@ -324,7 +332,7 @@ class IdentificationsScreen extends StatelessWidget { } if (state is IdenficationErrorState) { return SomethingWentWrong( - message: state.message, onpressed: () {}); + message: state.message, onpressed: () {context.read().add(LoadIdentifications());}); } if (state is IdentificationEditingState) { return EditIdentificationScreen( diff --git a/lib/screens/profile/components/basic_information/primary_information_screen.dart b/lib/screens/profile/components/basic_information/primary_information_screen.dart index b506f04..875ca64 100644 --- a/lib/screens/profile/components/basic_information/primary_information_screen.dart +++ b/lib/screens/profile/components/basic_information/primary_information_screen.dart @@ -14,6 +14,7 @@ import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/theme-data.dart/form-style.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; +import 'package:unit2/widgets/Leadings/close_leading.dart'; import 'package:unit2/widgets/error_state.dart'; import '../../../../utils/alerts.dart'; @@ -21,11 +22,7 @@ import '../../../../utils/alerts.dart'; class PrimaryInfo extends StatefulWidget { final int profileId; final String token; - const PrimaryInfo({ - super.key, - required this.profileId, - required this.token - }); + const PrimaryInfo({super.key, required this.profileId, required this.token}); @override State createState() => _PrimaryInfoState(); @@ -40,17 +37,32 @@ class _PrimaryInfoState extends State { DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); return Scaffold( appBar: AppBar( - title: const Text(primaryInformationScreenTitle), - centerTitle: true, - backgroundColor: primary, - actions: [ - IconButton( - onPressed: () { - context.read().add(ShowPrimaryInfoEditForm(token: widget.token)); - }, - icon: const Icon(Icons.edit)) - ], - ), + title: context.watch().state + is BasicInformationEditingState + ? const Text("Edit Profile") + : const Text("Primary Information"), + centerTitle: true, + backgroundColor: primary, + actions: context.watch().state + is BasicInformationProfileLoaded + ? [ + IconButton( + onPressed: () { + context.read().add( + ShowPrimaryInfoEditForm(token: widget.token)); + }, + icon: const Icon(Icons.edit)) + ] + : context.watch().state + is BasicInformationEditingState + ? [ + CloseLeading(onPressed: () { + context + .read() + .add(LoadBasicPrimaryInfo()); + }) + ] + : []), body: ProgressHUD( padding: const EdgeInsets.all(24), backgroundColor: Colors.black87, @@ -62,30 +74,27 @@ class _PrimaryInfoState extends State { progress!.showWithText("Please wait..."); } if (state is BasicInformationProfileLoaded || - state is BasicInformationEditingState || state is BasicPrimaryInformationErrorState || state is BasicProfileInfoEditedState) { + state is BasicInformationEditingState || + state is BasicPrimaryInformationErrorState || + state is BasicProfileInfoEditedState) { final progress = ProgressHUD.of(context); progress!.dismiss(); } - if (state is BasicProfileInfoEditedState) { - if (state.response['success']) { - successAlert(context, "Updated Successfull!", - state.response['message'], () { - Navigator.of(context).pop(); - context - .read() - .add(LoadBasicPrimaryInfo()); - }); - } else { - errorAlert(context, "Update Failed", - "Something went wrong. Please try again.", - () { - Navigator.of(context).pop(); - context - .read() - .add(LoadBasicPrimaryInfo()); - }); - } - } + if (state is BasicProfileInfoEditedState) { + if (state.response['success']) { + successAlert(context, "Updated Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context.read().add(LoadBasicPrimaryInfo()); + }); + } else { + errorAlert(context, "Update Failed", + "Something went wrong. Please try again.", () { + Navigator.of(context).pop(); + context.read().add(LoadBasicPrimaryInfo()); + }); + } + } }, builder: (context, state) { if (state is BasicInformationProfileLoaded) { @@ -336,11 +345,17 @@ class _PrimaryInfoState extends State { ], ), ); - }if(state is BasicPrimaryInformationErrorState){ - return SomethingWentWrong(message: state.message, onpressed: (){}); - - }if(state is BasicInformationEditingState){ - return EditBasicProfileInfoScreen(profileId: widget.profileId, token: widget.token); + } + if (state is BasicPrimaryInformationErrorState) { + return SomethingWentWrong( + message: state.message, + onpressed: () { + context.read().add(LoadBasicPrimaryInfo()); + }); + } + if (state is BasicInformationEditingState) { + return EditBasicProfileInfoScreen( + profileId: widget.profileId, token: widget.token); } return Container(); }, diff --git a/lib/screens/profile/components/education/add_modal.dart b/lib/screens/profile/components/education/add_modal.dart index 556f36b..1e6b8dc 100644 --- a/lib/screens/profile/components/education/add_modal.dart +++ b/lib/screens/profile/components/education/add_modal.dart @@ -1,7 +1,4 @@ -import 'package:date_time_picker/date_time_picker.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; @@ -16,6 +13,7 @@ import 'package:multi_dropdown/multiselect_dropdown.dart'; import '../../../../theme-data.dart/btn-style.dart'; import '../../../../theme-data.dart/colors.dart'; +import '../../../../utils/formatters.dart'; import '../../../../utils/text_container.dart'; import '../../shared/add_for_empty_search.dart'; @@ -65,7 +63,18 @@ class _AddEducationScreenState extends State { bool graduated = true; int? unitsEarned; //// +@override + void dispose() { + addProgramController.dispose(); + addSchoolController.dispose(); + fromController.dispose(); + untilController.dispose(); + programFocusNode.dispose(); + schoolFocusNode.dispose(); + honorFocusNode.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { return BlocBuilder( @@ -75,424 +84,444 @@ class _AddEducationScreenState extends State { return ValueItem(label: honor.name!, value: honor.name); }).toList(); return Container( - padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 18), + padding: const EdgeInsets.symmetric(vertical: 32, horizontal: 24), child: FormBuilder( key: formKey, - child: SingleChildScrollView( - child: SizedBox( - height: blockSizeVertical * 85, - child: Column(children: [ - const SizedBox( - height: 12, - ), - //// LEVEL - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - FormBuilderDropdown( - validator: FormBuilderValidators.required( - errorText: "This field is required"), - decoration: - normalTextFieldStyle("level*", "level"), - name: "education_level", - onChanged: (EducationLevel? level) { - setState(() { - selectedLevel = level; - }); - }, - items: educationLevel - .map>( - (EducationLevel level) { - return level.type == "label" - ? DropdownMenuItem( - enabled: false, - value: level, - child: Text(level.value.toUpperCase(), - style: const TextStyle( - color: Colors.black38))) - : DropdownMenuItem( - value: level, - enabled: true, - child: Text( - " ${level.value.toUpperCase()}")); - }).toList()), - const SizedBox( - height: 12, - ), - ////school - StatefulBuilder(builder: (context, setState) { - return SearchField( - itemHeight: 50, - suggestionsDecoration: box1(), - suggestions: state.schools - .map((School school) => - SearchFieldListItem(school.name!, - item: school, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10), - child: ListTile( - title: Text(school.name!)), - ))) - .toList(), - validator: (agency) { - if (agency!.isEmpty) { - return "This field is required"; - } - return null; - }, - focusNode: schoolFocusNode, - searchInputDecoration: - normalTextFieldStyle("School *", "").copyWith( - suffixIcon: - const Icon(Icons.arrow_drop_down)), - onSuggestionTap: (school) { - setState(() { - selectedSchool = school.item; - schoolFocusNode.unfocus(); - }); - }, - emptyWidget: EmptyWidget( - title: "Add School", - controller: addSchoolController, - onpressed: () { + child: SizedBox( + height: blockSizeVertical * 85, + child: Column( + children: [ + Flexible( + child: ListView(children: [ + //// LEVEL + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderDropdown( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: + normalTextFieldStyle("level*", "level"), + name: "education_level", + onChanged: (EducationLevel? level) { setState(() { - School newSchool = School( - id: null, - name: addSchoolController.text.toUpperCase()); - state.schools.insert(0, newSchool); - addSchoolController.text = ""; - - Navigator.pop(context); + selectedLevel = level; }); - }), - ); - }), - const SizedBox( - height: 12, - ), - ////Programs - Container( - child: selectedLevel != null && - selectedLevel!.group != 1 - ? SearchField( - itemHeight: 50, - suggestionsDecoration: box1(), - suggestions: state.programs - .map((Course program) => - SearchFieldListItem( - program.program!, - item: program, - child: Padding( - padding: const EdgeInsets - .symmetric( - horizontal: 10), - child: ListTile( - title: Text( - program.program!)), - ))) - .toList(), - validator: (agency) { - if (agency!.isEmpty) { - return "This field is required"; - } - return null; - }, - focusNode: programFocusNode, - searchInputDecoration: - normalTextFieldStyle( - "Course/Programs *", "") - .copyWith( - suffixIcon: const Icon( - Icons.arrow_drop_down)), - onSuggestionTap: (position) { + }, + items: educationLevel + .map>( + (EducationLevel level) { + return level.type == "label" + ? DropdownMenuItem( + enabled: false, + value: level, + child: Text(level.value.toUpperCase(), + style: const TextStyle( + color: Colors.black38))) + : DropdownMenuItem( + value: level, + enabled: true, + child: Text( + " ${level.value.toUpperCase()}")); + }).toList()), + const SizedBox( + height: 12, + ), + ////school + StatefulBuilder(builder: (context, setState) { + return SearchField( + inputFormatters: [UpperCaseTextFormatter()], + itemHeight: 70, + suggestionsDecoration: box1(), + suggestions: state.schools + .map((School school) => + SearchFieldListItem(school.name!, + item: school, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10), + child: ListTile( + title: Text(school.name!,overflow: TextOverflow.visible,)), + ))) + .toList(), + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + focusNode: schoolFocusNode, + searchInputDecoration: + normalTextFieldStyle("School *", "").copyWith( + suffixIcon: IconButton( + icon: const Icon(Icons.arrow_drop_down), + onPressed: () { + schoolFocusNode.unfocus(); + }, + )), + onSuggestionTap: (school) { + setState(() { + selectedSchool = school.item; + schoolFocusNode.unfocus(); + }); + }, + emptyWidget: EmptyWidget( + title: "Add School", + controller: addSchoolController, + onpressed: () { setState(() { - selectedProgram = position.item; - programFocusNode.unfocus(); + School newSchool = School( + id: null, + name: addSchoolController.text + .toUpperCase()); + state.schools.insert(0, newSchool); + addSchoolController.text = ""; + + Navigator.pop(context); }); - }, - emptyWidget: EmptyWidget( - title: "Add Program", - controller: addProgramController, - onpressed: () { + }), + ); + }), + const SizedBox( + height: 12, + ), + ////Programs + Container( + child: selectedLevel != null && + selectedLevel!.group != 1 + ? SearchField( + inputFormatters: [ + UpperCaseTextFormatter() + ], + itemHeight: 100, + suggestionsDecoration: box1(), + suggestions: state.programs + .map((Course program) => + SearchFieldListItem( + program.program!, + item: program, + child: Padding( + padding: const EdgeInsets + .symmetric( + horizontal: 10), + child: ListTile( + title: Text( + program.program!,overflow: TextOverflow.visible,)), + ))) + .toList(), + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + focusNode: programFocusNode, + searchInputDecoration: + normalTextFieldStyle( + "Course/Programs *", "") + .copyWith( + suffixIcon: GestureDetector( + onTap: () => programFocusNode.unfocus(), + child: const Icon( + Icons.arrow_drop_down), + )), + onSuggestionTap: (position) { setState(() { - Course newProgram = Course( - id: null, - program: addProgramController - .text.toUpperCase()); - state.programs.insert(0,newProgram); - addProgramController.text = ""; - - Navigator.pop(context); + selectedProgram = position.item; + programFocusNode.unfocus(); }); - }), - ) - : Container()) - ], - ); - }), - const SizedBox( - height: 12, - ), - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - //// GRADUATED SWITCH - FormBuilderSwitch( - initialValue: graduated, - activeColor: second, - onChanged: (value) { - setState(() { - graduated = value!; - if (graduated) { - unitsEarned = null; - } else { - yearGraduated.text = ""; - } - }); - }, - decoration: normalTextFieldStyle( - "Graduated?", 'Graduated?'), - name: 'graudated', - title: Text(graduated ? "YES" : "NO"), - ), - const SizedBox( - height: 12, - ), - ////FROM - SizedBox( - width: screenWidth, - child: Row( - children: [ - Flexible( - flex: 1, - child: FormBuilderTextField( - validator: FormBuilderValidators.required( - errorText: "This fied is required"), - decoration: - normalTextFieldStyle("from *", "from"), - name: "", - controller: fromController, - onTap: () { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - content: SizedBox( - width: 300, - height: 300, - child: YearPicker( - firstDate: DateTime( - DateTime.now().year - 100, - 1), - lastDate: DateTime( - DateTime.now().year + 100, - 1), - initialDate: DateTime.now(), - selectedDate: DateTime.now(), - onChanged: (DateTime dateTime) { - fromController.text = - dateTime.year.toString(); + }, + emptyWidget: EmptyWidget( + title: "Add Program", + controller: addProgramController, + onpressed: () { + setState(() { + Course newProgram = Course( + id: null, + program: addProgramController + .text + .toUpperCase()); + state.programs + .insert(0, newProgram); + addProgramController.text = ""; + Navigator.pop(context); - }, - ), - ), + }); + }), + ) + : Container()) + ], + ); + }), + const SizedBox( + height: 12, + ), + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + //// GRADUATED SWITCH + FormBuilderSwitch( + initialValue: graduated, + activeColor: second, + onChanged: (value) { + setState(() { + graduated = value!; + if (graduated) { + unitsEarned = null; + } else { + yearGraduated.text = ""; + } + }); + }, + decoration: normalTextFieldStyle( + "Graduated?", 'Graduated?'), + name: 'graudated', + title: Text(graduated ? "YES" : "NO"), + ), + const SizedBox( + height: 12, + ), + ////FROM + SizedBox( + width: screenWidth, + child: Row( + children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This fied is required"), + decoration: + normalTextFieldStyle("from *", "from"), + name: "", + controller: fromController, + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + content: SizedBox( + width: 300, + height: 300, + child: YearPicker( + firstDate: DateTime( + DateTime.now().year - 100, + 1), + lastDate: DateTime( + DateTime.now().year + 100, + 1), + initialDate: DateTime.now(), + selectedDate: DateTime.now(), + onChanged: (DateTime dateTime) { + fromController.text = + dateTime.year.toString(); + Navigator.pop(context); + }, + ), + ), + ); + }, ); }, - ); - }, - ), - ), - const SizedBox( - width: 8, - ), - ////UNTIL - Flexible( - flex: 1, - child: FormBuilderTextField( - validator: FormBuilderValidators.required( - errorText: "This fied is required"), - decoration: normalTextFieldStyle( - "until *", "until"), - name: "", - controller: untilController, - onTap: () { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - content: SizedBox( - width: 300, - height: 300, - child: YearPicker( - firstDate: DateTime( - DateTime.now().year - 100, - 1), - lastDate: DateTime( - DateTime.now().year + 100, - 1), - initialDate: DateTime.now(), - selectedDate: DateTime.now(), - onChanged: (DateTime dateTime) { - untilController.text = - dateTime.year.toString(); - Navigator.pop(context); - }, - ), - ), + ), + ), + const SizedBox( + width: 8, + ), + ////UNTIL + Flexible( + flex: 1, + child: FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This fied is required"), + decoration: normalTextFieldStyle( + "until *", "until"), + name: "", + controller: untilController, + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + content: SizedBox( + width: 300, + height: 300, + child: YearPicker( + firstDate: DateTime( + DateTime.now().year - 100, + 1), + lastDate: DateTime( + DateTime.now().year + 100, + 1), + initialDate: DateTime.now(), + selectedDate: DateTime.now(), + onChanged: (DateTime dateTime) { + untilController.text = + dateTime.year.toString(); + Navigator.pop(context); + }, + ), + ), + ); + }, ); }, - ); - }, - ), + ), + ), + const SizedBox( + width: 8, + ), + ], ), - const SizedBox( - width: 8, - ), - ], - ), - ), - const SizedBox( - height: 12, - ), - SizedBox( - child: graduated - ////GRADUATED YEAR - ? FormBuilderTextField( - validator: FormBuilderValidators.required( - errorText: "This fied is required"), - onTap: () { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - content: SizedBox( - width: 300, - height: 300, - child: YearPicker( - firstDate: DateTime( - DateTime.now().year - 100, - 1), - lastDate: DateTime( - DateTime.now().year + 100, - 1), - initialDate: DateTime.now(), - selectedDate: DateTime.now(), - onChanged: (DateTime dateTime) { - yearGraduated.text = - dateTime.year.toString(); - Navigator.pop(context); - }, - ), - ), + ), + const SizedBox( + height: 12, + ), + SizedBox( + child: graduated + ////GRADUATED YEAR + ? FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This fied is required"), + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + content: SizedBox( + width: 300, + height: 300, + child: YearPicker( + firstDate: DateTime( + DateTime.now().year - 100, + 1), + lastDate: DateTime( + DateTime.now().year + 100, + 1), + initialDate: DateTime.now(), + selectedDate: DateTime.now(), + onChanged: (DateTime dateTime) { + yearGraduated.text = + dateTime.year.toString(); + Navigator.pop(context); + }, + ), + ), + ); + }, ); }, - ); - }, - name: "year_graduated", - controller: yearGraduated, - decoration: normalTextFieldStyle( - "Year Graduated *", "Year Graduated *"), - ) - //// HIGHEST UNITS EARNED - : FormBuilderTextField( - validator: FormBuilderValidators.required( - errorText: "This fied is required"), - name: "units_earned", - decoration: normalTextFieldStyle( - "Highest Level/Units Earned *", - "Highest Level/Units Earned *")), - ), - ], - ); - }), - - const SizedBox( - height: 12, - ), - //// HONORS - MultiSelectDropDown( - onOptionSelected: (List selectedOptions) { - selectedValueItem = selectedOptions; - }, - borderColor: Colors.grey, - borderWidth: 1, - borderRadius: 5, - hint: "Honors", - padding: const EdgeInsets.all(8), - options: valueItemHonorList, - selectionType: SelectionType.multi, - chipConfig: const ChipConfig(wrapType: WrapType.wrap), - dropdownHeight: 300, - optionTextStyle: const TextStyle(fontSize: 16), - selectedOptionIcon: const Icon(Icons.check_circle), - ), - ////sumit button - const Expanded(child: SizedBox()), - SizedBox( - width: double.infinity, - height: 60, - child: ElevatedButton( - style: - mainBtnStyle(primary, Colors.transparent, second), - onPressed: () { - if (formKey.currentState!.saveAndValidate()) { - //// program - if (selectedLevel!.value == "Elementary" || - selectedLevel!.value == - "Secondary (non-K12)" || - selectedLevel!.value == "Junior High" || - selectedLevel!.value == "Senior High") { - selectedProgram = null; - } - if (!graduated) { - unitsEarned = int.parse(formKey - .currentState!.value['units_earned']); - yearGraduated.text = ''; - }else{ - - } - ////education - Education newEducation = Education( - id: null, - level: selectedLevel!.value, - course: selectedProgram, - school: selectedSchool); - ////honors - if (selectedValueItem != null) { - for (var honor in selectedValueItem!) { - Honor newHonor = state.honors.firstWhere((element) => element.name == honor.value); - selectedHonors.add(newHonor); - } - } - - EducationalBackground educationalBackground = - EducationalBackground( - id: null, - honors: null, - education: newEducation, - periodTo: untilController.text, - periodFrom: fromController.text, - yearGraduated: - graduated ? yearGraduated.text : null, - unitsEarned: !graduated ? unitsEarned : null, - attachments: null, - ); - final progress = ProgressHUD.of(context); - progress!.showWithText("Loading..."); - context.read().add(AddEducation( - educationalBackground: educationalBackground, - profileId: widget.profileId, - token: widget.token, - honors: selectedHonors)); - } + name: "year_graduated", + controller: yearGraduated, + decoration: normalTextFieldStyle( + "Year Graduated *", "Year Graduated *"), + ) + //// HIGHEST UNITS EARNED + : FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This fied is required"), + name: "units_earned", + decoration: normalTextFieldStyle( + "Highest Level/Units Earned *", + "Highest Level/Units Earned *")), + ), + ], + ); + }), + + const SizedBox( + height: 12, + ), + //// HONORS + MultiSelectDropDown( + onOptionSelected: (List selectedOptions) { + selectedValueItem = selectedOptions; }, - child: const Text(submit)), + borderColor: Colors.grey, + borderWidth: 1, + borderRadius: 5, + hint: "Honors", + padding: const EdgeInsets.all(8), + options: valueItemHonorList, + selectionType: SelectionType.multi, + chipConfig: const ChipConfig(wrapType: WrapType.wrap), + dropdownHeight: 300, + optionTextStyle: const TextStyle(fontSize: 16), + selectedOptionIcon: const Icon(Icons.check_circle), + ), + const SizedBox( + height: 25, + ), + ////sumit button + + + const SizedBox( + height: 20, + ), + ]), ), - const SizedBox( - height: 20, - ), - ]), + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: + mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState!.saveAndValidate()) { + //// program + if (selectedLevel!.value == "Elementary" || + selectedLevel!.value == + "Secondary (non-K12)" || + selectedLevel!.value == "Junior High" || + selectedLevel!.value == "Senior High") { + selectedProgram = null; + } + if (!graduated) { + unitsEarned = int.parse(formKey + .currentState!.value['units_earned']); + yearGraduated.text = ''; + } else {} + ////education + Education newEducation = Education( + id: null, + level: selectedLevel!.value, + course: selectedProgram, + school: selectedSchool); + ////honors + if (selectedValueItem != null) { + for (var honor in selectedValueItem!) { + Honor newHonor = state.honors.firstWhere( + (element) => element.name == honor.value); + selectedHonors.add(newHonor); + } + } + + EducationalBackground educationalBackground = + EducationalBackground( + id: null, + honors: null, + education: newEducation, + periodTo: untilController.text, + periodFrom: fromController.text, + yearGraduated: + graduated ? yearGraduated.text : null, + unitsEarned: !graduated ? unitsEarned : null, + attachments: null, + ); + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add(AddEducation( + educationalBackground: educationalBackground, + profileId: widget.profileId, + token: widget.token, + honors: selectedHonors)); + } + }, + child: const Text(submit)), + + ), + + ], ), ), ), diff --git a/lib/screens/profile/components/education/edit_modal.dart b/lib/screens/profile/components/education/edit_modal.dart index 2c969c3..0cf7ced 100644 --- a/lib/screens/profile/components/education/edit_modal.dart +++ b/lib/screens/profile/components/education/edit_modal.dart @@ -1,7 +1,4 @@ -import 'package:date_time_picker/date_time_picker.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; @@ -12,9 +9,9 @@ import 'package:unit2/model/profile/educational_background.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/form-style.dart'; import 'package:unit2/utils/global.dart'; - import '../../../../theme-data.dart/btn-style.dart'; import '../../../../theme-data.dart/colors.dart'; +import '../../../../utils/formatters.dart'; import '../../../../utils/text_container.dart'; import '../../shared/add_for_empty_search.dart'; import 'package:multi_dropdown/multiselect_dropdown.dart'; @@ -65,6 +62,21 @@ class _EditEducationScreenState extends State { final honorFocusNode = FocusNode(); ////booleans bool graduated = true; + + @override + void dispose() { + addProgramController.dispose(); + addSchoolController.dispose(); + fromController.dispose(); + untilController.dispose(); + currentSchoolController.dispose(); + currentProgramController.dispose(); + unitsController.dispose(); + programFocusNode.dispose(); + schoolFocusNode .dispose(); + honorFocusNode .dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { return BlocBuilder( @@ -98,459 +110,471 @@ class _EditEducationScreenState extends State { return ValueItem(label: honor.name!, value: honor.name); }).toList(); return Container( - padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 18), + padding: const EdgeInsets.symmetric(vertical: 32, horizontal: 24), child: FormBuilder( key: formKey, - child: SingleChildScrollView( - child: SizedBox( - height: blockSizeVertical * 85, - child: Column(children: [ - const SizedBox( - height: 12, - ), - //// LEVEL - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - FormBuilderDropdown( - initialValue: selectedLevel, - validator: FormBuilderValidators.required( - errorText: "This field is required"), - decoration: - normalTextFieldStyle("level*", "level"), - name: "education_level", - onChanged: (EducationLevel? level) { - setState(() { - selectedLevel = level; - }); - }, - items: educationLevel - .map>( - (EducationLevel level) { - return level.type == "label" - ? DropdownMenuItem( - enabled: false, - value: level, - child: Text(level.value.toUpperCase(), - style: const TextStyle( - color: Colors.black38))) - : DropdownMenuItem( - value: level, - enabled: true, - child: Text( - " ${level.value.toUpperCase()}")); - }).toList()), - const SizedBox( - height: 12, - ), - ////school - StatefulBuilder(builder: (context, setState) { - return SearchField( - suggestionAction: SuggestionAction.next, - onSubmit: (p0) { - schoolFocusNode.unfocus(); - }, - controller: currentSchoolController, - itemHeight: 50, - suggestionsDecoration: box1(), - suggestions: state.schools - .map((School school) => - SearchFieldListItem(school.name!, - item: school, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10), - child: ListTile( - title: Text(school.name!)), - ))) - .toList(), - validator: (agency) { - if (agency!.isEmpty) { - return "This field is required"; - } - return null; - }, - focusNode: schoolFocusNode, - searchInputDecoration: - normalTextFieldStyle("School *", "").copyWith( - suffixIcon: - GestureDetector(child: const Icon(Icons.arrow_drop_down),onTap: (){ - schoolFocusNode.unfocus(); - },)), - onSuggestionTap: (school) { - setState(() { - selectedSchool = school.item; - schoolFocusNode.unfocus(); - }); - }, - emptyWidget: EmptyWidget( - title: "Add School", - controller: addSchoolController, - onpressed: () { + child: SizedBox( + height: blockSizeVertical * 85, + child: Column( + children: [ + Flexible( + child: ListView(children: [ + + //// LEVEL + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderDropdown( + initialValue: selectedLevel, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: + normalTextFieldStyle("level*", "level"), + name: "education_level", + onChanged: (EducationLevel? level) { setState(() { - School newSchool = School( - id: null, - name: addSchoolController.text - .toUpperCase()); - state.schools.insert(0, newSchool); - addSchoolController.text = ""; - - Navigator.pop(context); + selectedLevel = level; }); - }), - ); - }), - const SizedBox( - height: 12, - ), - ////Programs - Container( - child: selectedLevel != null && - selectedLevel!.group != 1 - ? SearchField( - suggestionAction: - SuggestionAction.unfocus, - controller: currentProgramController, - itemHeight: 50, - suggestionsDecoration: box1(), - suggestions: state.programs - .map((Course program) => - SearchFieldListItem( - program.program!, - item: program, - child: Padding( - padding: const EdgeInsets - .symmetric( - horizontal: 10), - child: ListTile( - title: Text( - program.program!)), - ))) - .toList(), - validator: (agency) { - if (agency!.isEmpty) { - return "This field is required"; - } - return null; - }, - focusNode: programFocusNode, - searchInputDecoration: - normalTextFieldStyle( - "Course/Programs *", "") - .copyWith( - suffixIcon: const Icon( - Icons.arrow_drop_down)), - onSuggestionTap: (position) { - setState(() { - selectedProgram = position.item; - programFocusNode.unfocus(); - }); - }, - emptyWidget: EmptyWidget( - title: "Add Program", - controller: addProgramController, - onpressed: () { - setState(() { - Course newProgram = Course( - id: null, - program: addProgramController - .text - .toUpperCase()); - state.programs - .insert(0, newProgram); - addProgramController.text = ""; - - Navigator.pop(context); - }); - }), - ) - : Container()) + }, + items: educationLevel + .map>( + (EducationLevel level) { + return level.type == "label" + ? DropdownMenuItem( + enabled: false, + value: level, + child: Text(level.value.toUpperCase(), + style: const TextStyle( + color: Colors.black38))) + : DropdownMenuItem( + value: level, + enabled: true, + child: Text( + " ${level.value.toUpperCase()}")); + }).toList()), + const SizedBox( + height: 12, + ), + ////school + StatefulBuilder(builder: (context, setState) { + return SearchField( + inputFormatters: [ + UpperCaseTextFormatter() ], - ); - }), - const SizedBox( - height: 12, - ), - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - //// GRADUATED SWITCH - FormBuilderSwitch( - initialValue: graduated, - activeColor: second, - onChanged: (value) { - setState(() { - graduated = value!; - if (graduated) { - unitsController.text = ""; - } else { - yearGraduated.text = ""; - } - }); - }, - decoration: normalTextFieldStyle( - "Graduated?", 'Graduated?'), - name: 'graudated', - title: Text(graduated ? "YES" : "NO"), - ), - const SizedBox( - height: 12, - ), - ////FROM - SizedBox( - width: screenWidth, - child: Row( - children: [ - Flexible( - flex: 1, - child: FormBuilderTextField( - validator: FormBuilderValidators.required( - errorText: "This fied is required"), - decoration: - normalTextFieldStyle("from *", "from"), - name: "", - controller: fromController, + + suggestionAction: SuggestionAction.next, + onSubmit: (p0) { + schoolFocusNode.unfocus(); + }, + controller: currentSchoolController, + itemHeight: 70, + suggestionsDecoration: box1(), + suggestions: state.schools + .map((School school) => + SearchFieldListItem(school.name!, + item: school, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10), + child: ListTile( + title: Text(school.name!,overflow: TextOverflow.visible,)), + ))) + .toList(), + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + focusNode: schoolFocusNode, + searchInputDecoration: + normalTextFieldStyle("School *", "").copyWith( + suffixIcon: GestureDetector( + child: const Icon(Icons.arrow_drop_down), onTap: () { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - content: SizedBox( - width: 300, - height: 300, - child: YearPicker( - firstDate: DateTime( - DateTime.now().year - 100, - 1), - lastDate: DateTime( - DateTime.now().year + 100, - 1), - initialDate: DateTime.now(), - selectedDate: DateTime.now(), - onChanged: (DateTime dateTime) { - fromController.text = - dateTime.year.toString(); - Navigator.pop(context); - }, - ), - ), - ); - }, - ); + schoolFocusNode.unfocus(); }, - ), - ), - const SizedBox( - width: 8, - ), - ////UNTIL - Flexible( - flex: 1, - child: FormBuilderTextField( - validator: FormBuilderValidators.required( - errorText: "This fied is required"), - decoration: normalTextFieldStyle( - "until *", "until"), - name: "", - controller: untilController, - onTap: () { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - content: SizedBox( - width: 300, - height: 300, - child: YearPicker( - firstDate: DateTime( - DateTime.now().year - 100, - 1), - lastDate: DateTime( - DateTime.now().year + 100, - 1), - initialDate: DateTime.now(), - selectedDate: DateTime.now(), - onChanged: (DateTime dateTime) { - untilController.text = - dateTime.year.toString(); + )), + onSuggestionTap: (school) { + setState(() { + selectedSchool = school.item; + schoolFocusNode.unfocus(); + }); + }, + emptyWidget: EmptyWidget( + title: "Add School", + controller: addSchoolController, + onpressed: () { + setState(() { + School newSchool = School( + id: null, + name: addSchoolController.text + .toUpperCase()); + state.schools.insert(0, newSchool); + addSchoolController.text = ""; + + Navigator.pop(context); + }); + }), + ); + }), + const SizedBox( + height: 12, + ), + + ////Programs + + Container( + child: selectedLevel != null && + selectedLevel!.group != 1 + ? SearchField( + inputFormatters: [UpperCaseTextFormatter()], + suggestionAction: + SuggestionAction.unfocus, + controller: currentProgramController, + itemHeight: 70, + suggestionsDecoration: box1(), + suggestions: state.programs + .map((Course program) => + SearchFieldListItem( + program.program!, + item: program, + child: Padding( + padding: const EdgeInsets + .symmetric( + horizontal: 10), + child: ListTile( + title: Text( + program.program!,overflow: TextOverflow.visible,)), + ))) + .toList(), + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + focusNode: programFocusNode, + searchInputDecoration: + normalTextFieldStyle( + "Course/Programs *", "") + .copyWith( + suffixIcon: IconButton( + icon:const Icon( + Icons.arrow_drop_down), onPressed: () { programFocusNode.unfocus(); },),), + onSuggestionTap: (position) { + setState(() { + selectedProgram = position.item; + programFocusNode.unfocus(); + }); + }, + emptyWidget: EmptyWidget( + title: "Add Program", + controller: addProgramController, + onpressed: () { + setState(() { + Course newProgram = Course( + id: null, + program: addProgramController + .text + .toUpperCase()); + state.programs + .insert(0, newProgram); + addProgramController.text = ""; + Navigator.pop(context); - }, - ), - ), - ); - }, - ); - }, - ), - ), - const SizedBox( - width: 8, - ), - Flexible( - flex: 1, - child: graduated - ////GRADUATED YEAR - ? FormBuilderTextField( - validator: - FormBuilderValidators.required( - errorText: - "This fied is required"), - onTap: () { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - content: SizedBox( - width: 300, - height: 300, - child: YearPicker( - firstDate: DateTime( - DateTime.now().year - - 100, - 1), - lastDate: DateTime( - DateTime.now().year + - 100, - 1), - initialDate: - DateTime.now(), - selectedDate: - DateTime.now(), - onChanged: - (DateTime dateTime) { - yearGraduated.text = - dateTime.year - .toString(); - Navigator.pop(context); - }, - ), + }); + }), + ) + : Container()), + SizedBox(height: selectedLevel != null && + selectedLevel!.group != 1?12:0,), + ], + ); + }), + + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + //// GRADUATED SWITCH + FormBuilderSwitch( + initialValue: graduated, + activeColor: second, + onChanged: (value) { + setState(() { + graduated = value!; + if (graduated) { + unitsController.text = ""; + } else { + yearGraduated.text = ""; + } + }); + }, + decoration: normalTextFieldStyle( + "Graduated?", 'Graduated?'), + name: 'graudated', + title: Text(graduated ? "YES" : "NO"), + ), + const SizedBox( + height: 12, + ), + ////FROM + SizedBox( + width: screenWidth, + child: Row( + children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This fied is required"), + decoration: + normalTextFieldStyle("from *", "from"), + name: "", + controller: fromController, + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + content: SizedBox( + width: 300, + height: 300, + child: YearPicker( + firstDate: DateTime( + DateTime.now().year - 100, + 1), + lastDate: DateTime( + DateTime.now().year + 100, + 1), + initialDate: DateTime.now(), + selectedDate: DateTime.now(), + onChanged: (DateTime dateTime) { + fromController.text = + dateTime.year.toString(); + Navigator.pop(context); + }, ), + ), + ); + }, + ); + }, + ), + ), + const SizedBox( + width: 8, + ), + ////UNTIL + Flexible( + flex: 1, + child: FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This fied is required"), + decoration: normalTextFieldStyle( + "until *", "until"), + name: "", + controller: untilController, + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + content: SizedBox( + width: 300, + height: 300, + child: YearPicker( + firstDate: DateTime( + DateTime.now().year - 100, + 1), + lastDate: DateTime( + DateTime.now().year + 100, + 1), + initialDate: DateTime.now(), + selectedDate: DateTime.now(), + onChanged: (DateTime dateTime) { + untilController.text = + dateTime.year.toString(); + Navigator.pop(context); + }, + ), + ), + ); + }, + ); + }, + ), + ), + + + ], + ), + + ), const SizedBox(height: 20,), + SizedBox( + + child: graduated + ////GRADUATED YEAR + ? FormBuilderTextField( + validator: + FormBuilderValidators.required( + errorText: + "This fied is required"), + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + content: SizedBox( + width: 300, + height: 300, + child: YearPicker( + firstDate: DateTime( + DateTime.now().year - + 100, + 1), + lastDate: DateTime( + DateTime.now().year + + 100, + 1), + initialDate: + DateTime.now(), + selectedDate: + DateTime.now(), + onChanged: + (DateTime dateTime) { + yearGraduated.text = + dateTime.year + .toString(); + Navigator.pop(context); + }, + ), + ), + ); + }, ); }, - ); - }, - name: "year_graduated", - controller: yearGraduated, - decoration: normalTextFieldStyle( - "Year Graduated *", - "Year Graduated *"), - ) - //// HIGHEST UNITS EARNED - : FormBuilderTextField( - validator: - FormBuilderValidators.required( - errorText: - "This fied is required"), - controller: unitsController, - name: "units_earned", - decoration: normalTextFieldStyle( - "Highest Level/Units Earned *", - "Highest Level/Units Earned *")), - ) - ], - ), - ) - ], - ); - }), - const SizedBox( - height: 12, + name: "year_graduated", + controller: yearGraduated, + decoration: normalTextFieldStyle( + "Year Graduated *", + "Year Graduated *"), + ) + //// HIGHEST UNITS EARNED + : FormBuilderTextField( + validator: + FormBuilderValidators.required( + errorText: + "This fied is required"), + controller: unitsController, + name: "units_earned", + decoration: normalTextFieldStyle( + "Highest Level/Units Earned *", + "Highest Level/Units Earned *")), + ) + + ], + ); + }), + const SizedBox( + height: 12, + ), + //// HONORS + + StatefulBuilder(builder: (context, setState) { + return MultiSelectDropDown( + onOptionSelected: (List selectedOptions) { + selectedValueItem = selectedOptions; + }, + borderColor: Colors.grey, + borderWidth: 1, + borderRadius: 5, + hint: "Honors", + padding: const EdgeInsets.all(8), + options: valueItemHonorList, + selectionType: SelectionType.multi, + chipConfig: const ChipConfig(wrapType: WrapType.wrap), + dropdownHeight: 300, + optionTextStyle: const TextStyle(fontSize: 16), + selectedOptionIcon: const Icon(Icons.check_circle), + selectedOptions: + (state.educationalBackground.honors!.isNotEmpty && + state.educationalBackground.honors != null) + ? selectedValueItem = state + .educationalBackground.honors! + .map((Honor honor) => ValueItem( + label: honor.name!, value: honor.name)) + .toList() + : [], + ); + }), + + ]), ), - //// HONORS - - StatefulBuilder(builder: (context, setState) { - return MultiSelectDropDown( - onOptionSelected: (List selectedOptions) { - selectedValueItem = selectedOptions; - }, - borderColor: Colors.grey, - borderWidth: 1, - borderRadius: 5, - hint: "Honors", - padding: const EdgeInsets.all(8), - options: valueItemHonorList, - selectionType: SelectionType.multi, - chipConfig: const ChipConfig(wrapType: WrapType.wrap), - dropdownHeight: 300, - optionTextStyle: const TextStyle(fontSize: 16), - selectedOptionIcon: const Icon(Icons.check_circle), - selectedOptions: - (state.educationalBackground.honors!.isNotEmpty && - state.educationalBackground.honors != null) - ? selectedValueItem = state - .educationalBackground.honors! - .map((Honor honor) => ValueItem( - label: honor.name!, value: honor.name)) - .toList() - : [], - ); - }), - ////sumit button - const Expanded(child: SizedBox()), - SizedBox( - width: double.infinity, - height: 60, - child: ElevatedButton( - style: - mainBtnStyle(primary, Colors.transparent, second), - onPressed: () { - if (formKey.currentState!.saveAndValidate()) { - //// program - if (selectedLevel!.value == "Elementary" || - selectedLevel!.value == - "Secondary (non-K12)" || - selectedLevel!.value == "Junior High" || - selectedLevel!.value == "Senior High") { - selectedProgram = null; - } else { - selectedProgram ??= state - .educationalBackground.education!.course; - } - selectedSchool ??= - state.educationalBackground.education!.school; - ////education - Education newEducation = Education( - id: null, - level: selectedLevel!.value, - course: selectedProgram, - school: selectedSchool); - ////honors - if (selectedValueItem.isNotEmpty) { - for (var honor in selectedValueItem) { - Honor newHonor = state.honors.firstWhere( - (element) => element.name == honor.value); - selectedHonors.add(newHonor); + ////sumit button + + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: + mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState!.saveAndValidate()) { + //// program + if (selectedLevel!.value == "Elementary" || + selectedLevel!.value == + "Secondary (non-K12)" || + selectedLevel!.value == "Junior High" || + selectedLevel!.value == "Senior High") { + selectedProgram = null; + } else { + selectedProgram ??= state + .educationalBackground.education!.course; + } + selectedSchool ??= + state.educationalBackground.education!.school; + ////education + Education newEducation = Education( + id: null, + level: selectedLevel!.value, + course: selectedProgram, + school: selectedSchool); + ////honors + if (selectedValueItem.isNotEmpty) { + for (var honor in selectedValueItem) { + Honor newHonor = state.honors.firstWhere( + (element) => element.name == honor.value); + selectedHonors.add(newHonor); + } + } + EducationalBackground educationalBackground = + EducationalBackground( + id: state.educationalBackground.id, + honors: null, + education: newEducation, + periodTo: untilController.text, + periodFrom: fromController.text, + yearGraduated: + graduated ? yearGraduated.text : null, + unitsEarned: !graduated + ? int.parse(unitsController.text) + : null, + attachments: null, + ); + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add(UpdateEducation( + educationalBackground: educationalBackground, + profileId: widget.profileId, + token: widget.token, + honors: selectedHonors)); } - } - EducationalBackground educationalBackground = - EducationalBackground( - id: state.educationalBackground.id, - honors: null, - education: newEducation, - periodTo: untilController.text, - periodFrom: fromController.text, - yearGraduated: - graduated ? yearGraduated.text : null, - unitsEarned: !graduated - ? int.parse(unitsController.text) - : null, - attachments: null, - ); - final progress = ProgressHUD.of(context); - progress!.showWithText("Loading..."); - context.read().add(UpdateEducation( - educationalBackground: educationalBackground, - profileId: widget.profileId, - token: widget.token, - honors: selectedHonors)); - } - }, - child: const Text(submit)), - ), - const SizedBox( - height: 20, - ), - ]), + }, + child: const Text(submit)), + ), + + ], ), ), ), diff --git a/lib/screens/profile/components/education_screen.dart b/lib/screens/profile/components/education_screen.dart index 6b94216..3da29ed 100644 --- a/lib/screens/profile/components/education_screen.dart +++ b/lib/screens/profile/components/education_screen.dart @@ -6,7 +6,6 @@ import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:fluttericon/font_awesome_icons.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; - import 'package:unit2/model/profile/educational_background.dart'; import 'package:unit2/screens/profile/components/education/add_modal.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; @@ -15,7 +14,6 @@ import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; - import '../../../bloc/profile/education/education_bloc.dart'; import '../../../utils/alerts.dart'; import '../../../widgets/Leadings/close_leading.dart'; @@ -30,7 +28,7 @@ class EducationScreen extends StatelessWidget { String? token; return Scaffold( appBar: AppBar( - title: const Text(educationScreenTitle), + title:context.watch().state is AddEducationState? const FittedBox(child: Text("Add Educational Background")): context.watch().state is EditEducationState?const FittedBox(child: Text("Edit Educational Background")):const Text("Education Background"), centerTitle: true, backgroundColor: primary, actions: context.watch().state is EducationalBackgroundLoadedState?[ @@ -195,14 +193,11 @@ class EducationScreen extends StatelessWidget { style: Theme.of( context) .textTheme - .titleMedium! - .copyWith( - fontWeight: - FontWeight - .w500), + .titleSmall! + )), Text( - "$periodFrom - ", + "$periodFrom - $periodTo", style: Theme.of( context) .textTheme @@ -217,7 +212,7 @@ class EducationScreen extends StatelessWidget { school, style: Theme.of(context) .textTheme - .titleSmall, + .titleMedium!.copyWith(color: primary,fontWeight: FontWeight.w500), ), Container( padding: @@ -233,8 +228,9 @@ class EducationScreen extends StatelessWidget { CrossAxisAlignment .start, children: [ + const SizedBox(height: 8,), const Text( - " : ", + " honors: ", style: TextStyle( fontWeight: FontWeight.w600), @@ -242,7 +238,7 @@ class EducationScreen extends StatelessWidget { Column( children: honors .map((Honor honor) => - Text(" - ${honor.name!}")) + Text("-${honor.name!.trim()}",style: Theme.of(context).textTheme.labelMedium,)) .toList(), ), ], @@ -309,17 +305,18 @@ class EducationScreen extends StatelessWidget { }, menuItems: [ popMenuItem( - text: "Edit", + text: "Update", value: 1, icon: Icons.edit), popMenuItem( - text: "Delete", + text: "Remove", value: 2, icon: Icons.delete), - popMenuItem( - text: "Attachment", - value: 3, - icon: FontAwesome.attach) + popMenuItem( + text: "Attach", + value: 2, + icon: Icons.attach_file), + ], icon: const Icon( Icons.more_vert, @@ -337,14 +334,16 @@ class EducationScreen extends StatelessWidget { ); }); } else { - const EmptyData( + return const EmptyData( message: "You don't have any Educational Background added. Please click + to add."); } } if (state is EducationalBackgroundErrorState) { return SomethingWentWrong( - message: state.message, onpressed: () {}); + message: state.message, onpressed: () { + context.read().add(GetEducationalBackground(profileId: profileId, token: token!)); + }); } if (state is AddEducationState) { return AddEducationScreen( diff --git a/lib/screens/profile/components/eligibility/add_modal.dart b/lib/screens/profile/components/eligibility/add_modal.dart index 55d6612..945ec9a 100644 --- a/lib/screens/profile/components/eligibility/add_modal.dart +++ b/lib/screens/profile/components/eligibility/add_modal.dart @@ -6,10 +6,7 @@ import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:intl/intl.dart'; import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; -import 'package:unit2/bloc/profile/profile_bloc.dart'; -import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/eligibility.dart'; - import '../../../../bloc/profile/eligibility/eligibility_bloc.dart'; import '../../../../model/location/city.dart'; import '../../../../model/location/country.dart'; @@ -52,6 +49,15 @@ class _AddEligibilityScreenState extends State { String? profileId; String? rating; String? license; + DateTime? examDate; + DateTime? expireDate; + @override + void dispose() { + examDateController.dispose(); + validityDateController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return BlocBuilder( @@ -61,108 +67,121 @@ class _AddEligibilityScreenState extends State { builder: (context, state) { ////ADD ELIGIBILITY STATE if (state is AddEligibilityState) { - return SingleChildScrollView( - child: SizedBox( - height: screenHeight * .90, - child: Center( - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 25, horizontal: 25), - child: FormBuilder( - key: formKey, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - ////ELIGIBILITIES DROPDOWN - FormBuilderDropdown( - onChanged: (Eligibility? eligibility) { - selectedEligibility = eligibility; - }, - autovalidateMode: - AutovalidateMode.onUserInteraction, - validator: (value) => - value == null ? 'required' : null, - items: state.eligibilities - .map>( - (Eligibility eligibility) { - return DropdownMenuItem( - value: eligibility, - child: Text(eligibility.title)); - }).toList(), - name: "eligibility", - decoration: normalTextFieldStyle( - "Eligibility", "Eligibility")), - const SizedBox( - height: 8, - ), + return Padding( + padding: const EdgeInsets.symmetric(vertical: 32, horizontal: 28), + child: FormBuilder( + key: formKey, + child: SizedBox( + height: screenHeight * 90, + child: Column( + children: [ + Flexible( + child: ListView(children: [ + ////ELIGIBILITIES DROPDOWN + FormBuilderDropdown( + onChanged: (Eligibility? eligibility) { + selectedEligibility = eligibility; + }, + autovalidateMode: + AutovalidateMode.onUserInteraction, + validator: (value) => + value == null ? 'required' : null, + items: state.eligibilities + .map>( + (Eligibility eligibility) { + return DropdownMenuItem( + value: eligibility, + child: Text(eligibility.title)); + }).toList(), + name: "eligibility", + decoration: normalTextFieldStyle( + "Eligibility", "Eligibility")), + const SizedBox( + height: 8, + ), - SizedBox( - width: screenWidth, - child: Row( - children: [ - ////LICENSE NUMBER - Flexible( - flex: 1, - child: FormBuilderTextField( - onChanged: (value) { - license = value; - }, - name: 'license_number', - decoration: normalTextFieldStyle( - "license number", "license number"), - ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + ////LICENSE NUMBER + Flexible( + flex: 1, + child: FormBuilderTextField( + onChanged: (value) { + license = value; + }, + name: 'license_number', + decoration: normalTextFieldStyle( + "license number", "license number"), ), - const SizedBox( - width: 8, + ), + const SizedBox( + width: 8, + ), + ////RATING + Flexible( + flex: 1, + child: FormBuilderTextField( + validator: FormBuilderValidators.numeric( + errorText: "Enter a number"), + keyboardType: + const TextInputType.numberWithOptions(), + onChanged: (value) { + rating = value; + }, + name: 'rating', + decoration: normalTextFieldStyle( + 'rating %', 'rating'), ), - ////RATING - Flexible( - flex: 1, - child: FormBuilderTextField( - keyboardType: const TextInputType - .numberWithOptions(), - onChanged: (value) { - rating = value; - }, - name: 'rating', - decoration: normalTextFieldStyle( - 'rating %', 'rating'), - ), - ), - ], - ), + ), + ], ), - const SizedBox( - height: 8, - ), - SizedBox( - width: screenWidth, - child: Row( + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: StatefulBuilder(builder: (context, setState) { + return Row( children: [ ////EXAM DATE Flexible( flex: 1, child: DateTimePicker( - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), use24HourFormat: false, icon: const Icon(Icons.date_range), controller: examDateController, - firstDate: DateTime(1970), + firstDate: DateTime(1990), lastDate: DateTime(2100), timeHintText: "Date of Examination/Conferment", - decoration: normalTextFieldStyle( - "Exam date", "") - .copyWith( - prefixIcon: const Icon( + decoration: + normalTextFieldStyle("Exam date", "") + .copyWith( + prefixIcon: const Icon( Icons.date_range, color: Colors.black87, )), - initialValue: null, + initialDate: expireDate == null + ? DateTime.now() + : expireDate!.subtract( + const Duration(days: 1)), + selectableDayPredicate: (date) { + if (expireDate != null && + expireDate! + .microsecondsSinceEpoch <= + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, + onChanged: (value) { + setState(() { + examDate = DateTime.parse(value); + }); + }, )), const SizedBox( width: 8, @@ -171,8 +190,6 @@ class _AddEligibilityScreenState extends State { Flexible( flex: 1, child: DateTimePicker( - validator: FormBuilderValidators.required( - errorText: "This field is required"), controller: validityDateController, firstDate: DateTime(1970), lastDate: DateTime(2100), @@ -183,281 +200,271 @@ class _AddEligibilityScreenState extends State { Icons.date_range, color: Colors.black87, )), - initialValue: null, + selectableDayPredicate: (date) { + if (examDate != null && + examDate!.microsecondsSinceEpoch >= + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, + onChanged: (value) { + setState(() { + expireDate = DateTime.parse(value); + }); + }, + initialDate: examDate == null + ? DateTime.now() + : examDate! + .add(const Duration(days: 1)), ), ), ], + ); + }), + ), + const SizedBox( + height: 8, + ), + Text( + "Placement of Examination/Conferment", + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith(fontSize: blockSizeVertical * 2), + ), + const SizedBox( + height: 8, + ), + ////OVERSEAS ADDRESS SWITCH + Column( + children: [ + FormBuilderSwitch( + validator: FormBuilderValidators.required( + errorText: 'This field is required'), + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value; + }); + }, + decoration: normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Overseas Address?"), ), - ), - const SizedBox( - height: 8, - ), - Text( - "Placement of Examination/Conferment", - style: Theme.of(context) - .textTheme - .displaySmall! - .copyWith(fontSize: blockSizeVertical * 2), - ), - const SizedBox( - height: 8, - ), - ////OVERSEAS ADDRESS SWITCH - Column( - children: [ - FormBuilderSwitch( - validator: FormBuilderValidators.required( - errorText: 'This field is required'), - initialValue: overseas, - activeColor: second, - onChanged: (value) { - setState(() { - overseas = value; - }); - }, - decoration: normalTextFieldStyle("", ''), - name: 'overseas', - title: const Text("Overseas Address?"), - ), - const SizedBox( - height: 8, - ), - ////COUNTRY DROPDOWN - SizedBox( - child: overseas == true - ? FormBuilderDropdown( - initialValue: null, - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - items: state.countries - .map>( - (Country country) { - return DropdownMenuItem( - value: country, - child: FittedBox( - child: - Text(country.name!))); - }).toList(), - name: 'country', - decoration: normalTextFieldStyle( - "Country*", "Country"), - onChanged: (Country? value) { - selectedCountry = value; - }, - ) - : Column( - children: [ - ////REGION DROPDOWN - FormBuilderDropdown( - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - //// region onchange - onChanged: - (Region? region) async { - if (selectedRegion != - region) { - setState(() { - provinceCall = true; - }); - selectedRegion = region; - getProvinces(); - } - }, - initialValue: selectedRegion, - decoration: - normalTextFieldStyle( - "Region*", "Region"), - name: 'region', - items: state.regions.map< - DropdownMenuItem< - Region>>( - (Region region) { - return DropdownMenuItem< - Region>( - value: region, - child: Text( - region.description!)); - }).toList(), - ), - const SizedBox( - height: 8, - ), - ////PROVINCE DROPDOWN - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: provinceCall, - child: DropdownButtonFormField< - Province?>( - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - value: selectedProvince, - onChanged: - (Province? province) { - if (selectedProvince != - province) { - setState(() { - cityCall = true; - }); - selectedProvince = - province; - getCities(); - } - }, - items: provinces == null - ? [] - : provinces!.map< - DropdownMenuItem< - Province>>( - (Province - province) { - return DropdownMenuItem( - value: - province, - child: - FittedBox( - child: Text( - province - .description!), - )); - }).toList(), - decoration: - normalTextFieldStyle( - "Province*", - "Province")), - ), - ), - - //// CityMunicipalities dropdown - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: cityCall, - child: - DropdownButtonFormField< - CityMunicipality>( + const SizedBox( + height: 8, + ), + ////COUNTRY DROPDOWN + SizedBox( + child: overseas == true + ? FormBuilderDropdown( + initialValue: null, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + items: state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox( + child: Text(country.name!))); + }).toList(), + name: 'country', + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) { + selectedCountry = value; + }, + ) + : Column( + children: [ + ////REGION DROPDOWN + FormBuilderDropdown( + autovalidateMode: AutovalidateMode + .onUserInteraction, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + //// region onchange + onChanged: (Region? region) async { + if (selectedRegion != region) { + setState(() { + provinceCall = true; + }); + selectedRegion = region; + getProvinces(); + } + }, + initialValue: selectedRegion, + decoration: normalTextFieldStyle( + "Region*", "Region"), + name: 'region', + items: state.regions + .map>( + (Region region) { + return DropdownMenuItem( + value: region, + child: Text( + region.description!)); + }).toList(), + ), + const SizedBox( + height: 8, + ), + ////PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, validator: (value) => value == null ? 'required' : null, isExpanded: true, + value: selectedProvince, onChanged: - (CityMunicipality? - city) { - selectedMunicipality = - city; + (Province? province) { + if (selectedProvince != + province) { + setState(() { + cityCall = true; + }); + selectedProvince = + province; + getCities(); + } }, + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province province) { + return DropdownMenuItem( + value: province, + child: FittedBox( + child: Text(province + .description!), + )); + }).toList(), decoration: normalTextFieldStyle( - "Municipality*", - "Municipality"), - value: selectedMunicipality, - items: citymuns == null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality - c) { - return DropdownMenuItem( - value: c, - child: Text(c - .description!)); - }).toList(), - ), + "Province*", + "Province")), + ), + ), + + //// CityMunicipalities dropdown + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: DropdownButtonFormField< + CityMunicipality>( + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + onChanged: + (CityMunicipality? city) { + selectedMunicipality = city; + }, + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), ), ), - ], - )), - ], - ), - - const Expanded( - child: SizedBox(), - ), - - SizedBox( - width: screenWidth, - height: 60, - child: ElevatedButton( - style: mainBtnStyle( - primary, Colors.transparent, second), - onPressed: () { - ////rating - double? rate = rating == null + ), + ], + )), + ], + ), + ]), + ), + SizedBox( + width: screenWidth, + height: 60, + child: ElevatedButton( + style: + mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState!.saveAndValidate()) { + ////rating + double? rate = rating == null + ? null + : double.tryParse(rating!); + ////lisence + String? licenseNumber = license; + CityMunicipality? cityMunicipality = + selectedMunicipality; + DateTime? examDate = + examDateController.text.isEmpty ? null - : double.parse(rating!); - ////lisence - String? licenseNumber = license; - CityMunicipality? cityMunicipality = - selectedMunicipality; - DateTime? examDate = - examDateController.text.isEmpty - ? null - : DateTime.parse( - examDateController.text); - DateTime? validityDate = - validityDateController.text.isEmpty - ? null - : DateTime.parse( - validityDateController.text); + : DateTime.parse(examDateController.text); + DateTime? validityDate = validityDateController + .text.isEmpty + ? null + : DateTime.parse(validityDateController.text); - ExamAddress examAddress = ExamAddress( - barangay: null, - id: null, - addressCategory: null, - examAddressClass: null, - country: selectedCountry ?? - Country( - id: 175, - name: 'Philippines', - code: 'PH'), - cityMunicipality: cityMunicipality); - EligibityCert eligibityCert = EligibityCert( - id: null, - rating: rate, - examDate: examDate, - attachments: null, - eligibility: selectedEligibility, - examAddress: examAddress, - validityDate: validityDate, - licenseNumber: licenseNumber, - overseas: overseas); - if (formKey.currentState! - .saveAndValidate()) { - final progress = ProgressHUD.of(context); - progress!.showWithText("Loading..."); - context.read().add( - AddEligibility( - eligibityCert: eligibityCert, - profileId: - widget.profileId.toString(), - token: widget.token)); - } - // context.read().add(AddEligibility(eligibityCert: eligibityCert, profileId: profileId, token: token)) - }, - child: const Text(submit)), - ), - const SizedBox( - height: 20, - ), - ]), - ), + ExamAddress examAddress = ExamAddress( + barangay: null, + id: null, + addressCategory: null, + examAddressClass: null, + country: selectedCountry ?? + Country( + id: 175, + name: 'Philippines', + code: 'PH'), + cityMunicipality: cityMunicipality); + EligibityCert eligibityCert = EligibityCert( + id: null, + rating: rate, + examDate: examDate, + attachments: null, + eligibility: selectedEligibility, + examAddress: examAddress, + validityDate: validityDate, + licenseNumber: licenseNumber, + overseas: overseas); + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add( + AddEligibility( + eligibityCert: eligibityCert, + profileId: widget.profileId.toString(), + token: widget.token)); + } + }, + child: const Text(submit)), + ), + ], ), ), ), diff --git a/lib/screens/profile/components/eligibility/edit_modal.dart b/lib/screens/profile/components/eligibility/edit_modal.dart index d77dec6..ca4dadc 100644 --- a/lib/screens/profile/components/eligibility/edit_modal.dart +++ b/lib/screens/profile/components/eligibility/edit_modal.dart @@ -24,7 +24,11 @@ class EditEligibilityScreen extends StatefulWidget { final EligibityCert eligibityCert; final int profileId; final String token; - const EditEligibilityScreen({super.key, required this.eligibityCert, required this.profileId, required this.token}); + const EditEligibilityScreen( + {super.key, + required this.eligibityCert, + required this.profileId, + required this.token}); @override State createState() => _EditEligibilityScreenState(); @@ -32,7 +36,6 @@ class EditEligibilityScreen extends StatefulWidget { class _EditEligibilityScreenState extends State { final formKey = GlobalKey(); - final provinceKey = GlobalKey(); bool? overseas; List? provinces; List? citymuns; @@ -51,490 +54,491 @@ class _EditEligibilityScreenState extends State { String? license; final examDateController = TextEditingController(); final validityDateController = TextEditingController(); + + @override + void dispose() { + examDateController.dispose(); + validityDateController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { + return BlocBuilder( + buildWhen: (previous, current) { + return false; + }, + builder: (context, state) { + //EDIT ELIGIBILITY STATE + if (state is EditEligibilityState) { + examDateController.text = state.eligibityCert.examDate == null + ? '' + : state.eligibityCert.examDate.toString(); + validityDateController.text = state.eligibityCert.validityDate == null + ? '' + : state.eligibityCert.validityDate.toString(); + DateTime? examDate = DateTime.tryParse(examDateController.text) ; + DateTime? expireDate = DateTime.tryParse(validityDateController.text); + provinces = state.provinces; + citymuns = state.cities; + regions = state.regions; + overseas = state.isOverseas; + selectedRegion = state.currentRegion; + selectedProvince = state.currentProvince; + selectedMunicipality = state.currentCity; + selectedEligibility = state.currentEligibility; + rating = state.eligibityCert.rating?.toString(); + license = state.eligibityCert.licenseNumber; + selectedCountry = state.selectedCountry; + return Center( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 28), + child: FormBuilder( + key: formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox( + height: 24, + ), + ////ELIGIBILITIES DROPDOWN + DropdownButtonFormField( + validator: (value) => + value == null ? 'required' : null, + isExpanded: true, + onChanged: (Eligibility? eligibility) { + selectedEligibility = eligibility; + }, + value: selectedEligibility, + items: state.eligibilities + .map>( + (Eligibility eligibility) { + return DropdownMenuItem( + value: eligibility, + child: Text(eligibility.title)); + }).toList(), + decoration: normalTextFieldStyle("Eligibility", "")), + const SizedBox( + height: 12, + ), - return BlocBuilder( - buildWhen: (previous, current) { - - return false; - }, - builder: (context, state) { - //EDIT ELIGIBILITY STATE - if (state is EditEligibilityState) { - examDateController.text = - state.eligibityCert.examDate == null - ? '' - : state.eligibityCert.examDate.toString(); - validityDateController.text = - state.eligibityCert.validityDate == null - ? '' - : state.eligibityCert.validityDate.toString(); - - provinces = state.provinces; - citymuns = state.cities; - regions = state.regions; - overseas = state.isOverseas; - selectedRegion = state.currentRegion; - selectedProvince = state.currentProvince; - selectedMunicipality = state.currentCity; - selectedEligibility = state.currentEligibility; - rating = state.eligibityCert.rating?.toString(); - license = state.eligibityCert.licenseNumber; - return Center( - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 25, horizontal: 24), - child: FormBuilder( - key: formKey, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const SizedBox(height: 24,), - ////ELIGIBILITIES DROPDOWN - DropdownButtonFormField( - validator: (value) => - value == null ? 'required' : null, - isExpanded: true, - onChanged: (Eligibility? eligibility) { - selectedEligibility = eligibility; + SizedBox( + width: screenWidth, + child: Row( + children: [ + ////LICENSE NUMBER + Flexible( + flex: 1, + child: FormBuilderTextField( + onChanged: (value) { + license = value; + }, + name: 'license_number', + initialValue: license, + decoration: normalTextFieldStyle( + "license number", "license number"), + ), + ), + const SizedBox( + width: 12, + ), + // //RATING + Flexible( + flex: 1, + child: FormBuilderTextField( + validator: FormBuilderValidators.numeric( + errorText: "Enter a number"), + keyboardType: + const TextInputType.numberWithOptions(), + onChanged: (value) { + rating = value; + }, + name: 'rating', + initialValue: + rating == null ? 'N/A' : rating.toString(), + decoration: + normalTextFieldStyle('rating', 'rating'), + ), + ), + ], + ), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + child: StatefulBuilder(builder: (context, setState) { + return Row( + children: [ + // //EXAM DATE + Flexible( + flex: 1, + child: DateTimePicker( + use24HourFormat: false, + controller: examDateController, + firstDate: DateTime(1990), + lastDate: DateTime(2100), + decoration: + normalTextFieldStyle("Exam date", "") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + initialDate: expireDate == null + ? DateTime.now() + : expireDate! + .subtract(const Duration(days: 1)), + selectableDayPredicate: (date) { + if (expireDate != null && + expireDate!.microsecondsSinceEpoch <= + date.microsecondsSinceEpoch) { + return false; + } + return true; }, - value: selectedEligibility, - items: state.eligibilities - .map>( - (Eligibility eligibility) { - return DropdownMenuItem( - value: eligibility, - child: Text(eligibility.title)); - }).toList(), - decoration: normalTextFieldStyle( - "Eligibility", "")), - const SizedBox( - height: 12, - ), + onChanged: (value) { + setState(() { + examDate = DateTime.parse(value); + }); + }, + )), - SizedBox( - width: screenWidth, - child: Row( - children: [ - ////LICENSE NUMBER - Flexible( - flex: 1, - child: FormBuilderTextField( - onChanged: (value) { - license = value; - }, - name: 'license_number', - initialValue: license, - decoration: normalTextFieldStyle( - "license number", - "license number"), - ), - ), - const SizedBox( - width: 12, - ), - // //RATING - Flexible( - flex: 1, - child: FormBuilderTextField( - keyboardType: const TextInputType - .numberWithOptions(), - onChanged: (value) { - rating = value; - }, - name: 'rating', - initialValue: rating == null - ? 'N/A' - : rating.toString(), - decoration: normalTextFieldStyle( - 'rating', 'rating'), - ), - ), - ], - ), + const SizedBox( + width: 12, + ), + ////VALIDITY DATE + Flexible( + flex: 1, + child: DateTimePicker( + use24HourFormat: false, + controller: validityDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + decoration: + normalTextFieldStyle("validity date", "") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + selectableDayPredicate: (date) { + if (examDate != null && + examDate!.microsecondsSinceEpoch >= + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, + onChanged: (value) { + setState(() { + expireDate = DateTime.parse(value); + }); + }, + initialDate: examDate == null + ? DateTime.now() + : examDate!.add(const Duration(days: 1)), ), - const SizedBox( - height: 12, - ), - SizedBox( - width: screenWidth, - child: Row( - children: [ - // //EXAM DATE - Flexible( - flex: 1, - child: DateTimePicker( - validator: FormBuilderValidators.required(errorText: "This field is required"), - use24HourFormat: false, - controller: examDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), - decoration: normalTextFieldStyle( - "Exam date", "") - .copyWith( - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - )), - )), - const SizedBox( - width: 12, - ), - ////VALIDITY DATE - Flexible( - flex: 1, - child: DateTimePicker( - validator: FormBuilderValidators.required(errorText: "This field is required"), - - use24HourFormat: false, - controller: validityDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), - decoration: normalTextFieldStyle( - "validity date", "") - .copyWith( - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - )), - ), - ), - ], - ), - ), - const SizedBox( - height: 20, - ), - Text( - "Placement of Examination/Confinement", - style: Theme.of(context) - .textTheme - .displaySmall! - .copyWith( - fontSize: blockSizeVertical * 2), - ), - const SizedBox( - height: 12, - ), - //OVERSEAS ADDRESS SWITCH - StatefulBuilder( - builder: (context, StateSetter setState) { - return Column( - children: [ - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - setState(() { - overseas = value; - }); + ), + ], + ); + }), + ), + const SizedBox( + height: 20, + ), + Text( + "Placement of Examination/Confinement", + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith(fontSize: blockSizeVertical * 2), + ), + const SizedBox( + height: 12, + ), + //OVERSEAS ADDRESS SWITCH + StatefulBuilder(builder: (context, StateSetter setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value; + }); + }, + decoration: normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Overseas Address?"), + ), + const SizedBox( + height: 12, + ), + //COUNTRY DROPDOWN + SizedBox( + child: overseas == true + ? FormBuilderDropdown( + validator: (value) => + value == null ? 'required' : null, + initialValue: selectedCountry!.id == 175 + ? null + : selectedCountry, + items: state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox( + child: Text(country.name!))); + }).toList(), + name: 'country', + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) { + selectedCountry = value; }, - decoration: - normalTextFieldStyle("", ''), - name: 'overseas', - title: const Text("Overseas Address?"), - ), - const SizedBox( - height: 12, - ), - //COUNTRY DROPDOWN - SizedBox( - child: overseas == true - ? FormBuilderDropdown( + ) + : Column( + children: [ + ////REGION DROPDOWN + DropdownButtonFormField( + validator: (value) => value == null + ? 'required' + : null, + isExpanded: true, + onChanged: (Region? region) async { + setState(() { + provinceCall = true; + }); + selectedRegion = region; + provinces = await LocationUtils + .instance + .getProvinces( + regionCode: + selectedRegion!.code + .toString()); + selectedProvince = provinces![0]; + setState(() { + provinceCall = false; + cityCall = true; + }); + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code!); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + }); + }, + value: selectedRegion, + decoration: normalTextFieldStyle( + "Region*", "Region"), + items: regions == null + ? [] + : regions!.map< + DropdownMenuItem< + Region>>( + (Region region) { + return DropdownMenuItem< + Region>( + value: region, + child: Text(region + .description!)); + }).toList(), + ), + const SizedBox( + height: 12, + ), + ////PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( validator: (value) => value == null ? 'required' : null, - initialValue: - state.selectedCountry, - items: state.countries.map< - DropdownMenuItem< - Country>>( - (Country country) { - return DropdownMenuItem< - Country>( - value: country, - child: FittedBox( - child: Text(country - .name!))); - }).toList(), - name: 'country', + isExpanded: true, + value: selectedProvince, + onChanged: (Province? + province) async { + setState(() { + cityCall = true; + }); + selectedProvince = province; + citymuns = await LocationUtils + .instance + .getCities( + code: + selectedProvince! + .code + .toString()); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + }); + }, + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province province) { + return DropdownMenuItem( + value: province, + child: FittedBox( + child: Text(province + .description!), + )); + }).toList(), decoration: normalTextFieldStyle( - "Country*", - "Country"), - onChanged: (Country? value) { - selectedCountry = value; - }, - ) - : Column( - children: [ - //REGION DROPDOWN - DropdownButtonFormField< - Region?>( - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - onChanged: (Region? - region) async { - setState(() { - provinceCall = true; - }); - selectedRegion = region; - provinces = await LocationUtils - .instance - .getProvinces( - regionCode: - selectedRegion! - .code - .toString()); - selectedProvince = - provinces![0]; - setState(() { - provinceCall = false; - cityCall = true; - }); - citymuns = await LocationUtils - .instance - .getCities( - code: - selectedProvince! - .code!); - selectedMunicipality = - citymuns![0]; - setState(() { - cityCall = false; - }); - }, - value: selectedRegion, - decoration: - normalTextFieldStyle( - "Region*", - "Region"), - items: regions == null - ? [] - : regions!.map< - DropdownMenuItem< - Region>>((Region - region) { - return DropdownMenuItem< - Region>( - value: region, - child: Text(region - .description!)); - }).toList(), - ), - const SizedBox( - height: 12, - ), - ////PROVINCE DROPDOWN - SizedBox( - height: 70, - child: ModalProgressHUD( - color: - Colors.transparent, - inAsyncCall: - provinceCall, - child: DropdownButtonFormField< - Province?>( - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - value: - selectedProvince, - onChanged: (Province? - province) async { - setState(() { - cityCall = true; - }); - selectedProvince = - province; - citymuns = await LocationUtils - .instance - .getCities( - code: selectedProvince! - .code - .toString()); - selectedMunicipality = - citymuns![0]; - setState(() { - cityCall = - false; - }); - }, - items: provinces == - null - ? [] - : provinces!.map< - DropdownMenuItem< - Province>>((Province - province) { - return DropdownMenuItem( - value: - province, - child: - FittedBox( - child: - Text(province.description!), - )); - }).toList(), - decoration: - normalTextFieldStyle( - "Province*", - "Province")), - ), - ), + "Province*", + "Province")), + ), + ), - //// City municipality - SizedBox( - height: 60, - child: ModalProgressHUD( - color: - Colors.transparent, - inAsyncCall: cityCall, - child: - DropdownButtonFormField< - CityMunicipality>( - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - onChanged: - (CityMunicipality? - city) { - selectedMunicipality = - city; - }, - decoration: - normalTextFieldStyle( - "Municipality*", - "Municipality"), - value: - selectedMunicipality, - items: citymuns == - null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality - c) { - return DropdownMenuItem( - value: c, - child: Text( - c.description!)); - }).toList(), - ), - ), - ), - const SizedBox( - height: 20, - ), - ], - )), - ], - ); - }), + //// City municipality + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: cityCall, + child: DropdownButtonFormField< + CityMunicipality>( + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + onChanged: + (CityMunicipality? city) { + selectedMunicipality = city; + }, + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), + ), + ), + ), + const SizedBox( + height: 20, + ), + ], + )), + ], + ); + }), - const Expanded( - child: SizedBox(), - ), - - SizedBox( - width: screenWidth, - height: 60, - child: ElevatedButton( - style: mainBtnStyle( - primary, Colors.transparent, second), - onPressed: () { - ////rating - double? rate = rating == null - ? null - : double.parse(rating!); - ////license - String? newLicense = license; - ////city municipality - CityMunicipality? cityMunicipality = - selectedMunicipality; - ////exam date - DateTime? examDate = - examDateController.text.isEmpty - ? null - : DateTime.parse( - examDateController.text); - // // validity date - DateTime? validityDate = - validityDateController.text.isEmpty - ? null - : DateTime.parse( - validityDateController - .text); - //// exam address - ExamAddress examAddress = ExamAddress( - barangay: state.eligibityCert - .examAddress?.barangay, - id: state - .eligibityCert.examAddress?.id, - addressCategory: state.eligibityCert - .examAddress?.addressCategory, - examAddressClass: state - .eligibityCert - .examAddress - ?.examAddressClass, - country: selectedCountry ??= - Country( - id: 175, - name: 'Philippines', - code: 'PH'), - cityMunicipality: cityMunicipality); - EligibityCert eligibityCert = - EligibityCert( - id: state.eligibityCert.id, - rating: rate, - examDate: examDate, - attachments: null, - eligibility: - selectedEligibility, - examAddress: examAddress, - validityDate: validityDate, - licenseNumber: newLicense, - overseas: overseas); - if (formKey.currentState! - .saveAndValidate()) { - final progress = - ProgressHUD.of( - context); - progress!.showWithText( - "Loading..."); - context.read().add( - UpdateEligibility( - eligibityCert: eligibityCert, - oldEligibility: state - .eligibityCert - .eligibility! - .id, - profileId:widget.profileId.toString(), - token: widget.token)); - } - }, - child: const Text(submit)), - ), - const SizedBox( - height: 20, - ), - ]), - ), + const Expanded( + child: SizedBox(), ), - ); - - + + SizedBox( + width: screenWidth, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + ExamAddress examAddress; + ////rating + double? rate = + rating == null ? null : double.parse(rating!); + ////license + String? newLicense = license; + ////city municipality + CityMunicipality? cityMunicipality = + selectedMunicipality; + ////exam date + DateTime? examDate = + examDateController.text.isEmpty + ? null + : DateTime.parse(examDateController.text); + // // validity date + DateTime? validityDate = validityDateController + .text.isEmpty + ? null + : DateTime.parse(validityDateController.text); + //// exam address + if (overseas!) { + examAddress = ExamAddress( + barangay: null, + id: state.eligibityCert.examAddress?.id, + addressCategory: state.eligibityCert + .examAddress?.addressCategory, + examAddressClass: state.eligibityCert + .examAddress?.examAddressClass, + country: selectedCountry, + cityMunicipality: null); + } else { + examAddress = ExamAddress( + barangay: state + .eligibityCert.examAddress?.barangay, + id: state.eligibityCert.examAddress?.id, + addressCategory: state.eligibityCert + .examAddress?.addressCategory, + examAddressClass: state.eligibityCert + .examAddress?.examAddressClass, + country: Country( + id: 175, + name: 'Philippines', + code: 'PH'), + cityMunicipality: cityMunicipality); + } + + EligibityCert eligibityCert = EligibityCert( + id: state.eligibityCert.id, + rating: rate, + examDate: examDate, + attachments: null, + eligibility: selectedEligibility, + examAddress: examAddress, + validityDate: validityDate, + licenseNumber: newLicense, + overseas: overseas); + if (formKey.currentState!.saveAndValidate()) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add( + UpdateEligibility( + eligibityCert: eligibityCert, + oldEligibility: + state.eligibityCert.eligibility!.id, + profileId: widget.profileId.toString(), + token: widget.token)); + } + }, + child: const Text(submit)), + ), + ]), + ), + ), + ); } return Container(); }, diff --git a/lib/screens/profile/components/eligibility_screen.dart b/lib/screens/profile/components/eligibility_screen.dart index 34c1af2..e3ddd67 100644 --- a/lib/screens/profile/components/eligibility_screen.dart +++ b/lib/screens/profile/components/eligibility_screen.dart @@ -196,9 +196,9 @@ class EligibiltyScreen extends StatelessWidget { .copyWith( fontWeight: FontWeight - .w500), + .w500,color: primary), ), - const Divider(), + const SizedBox( height: 5, ), @@ -284,21 +284,21 @@ class EligibiltyScreen extends StatelessWidget { eligibityCert)); } }, - menuItems: [ - popMenuItem( - text: "Edit", - value: 1, - icon: Icons.edit), - popMenuItem( - text: "Delete", - value: 2, - icon: Icons.delete), - popMenuItem( - text: "Attachment", - value: 3, - icon: FontAwesome - .attach) - ], + menuItems: [ + popMenuItem( + text: "Update", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Remove", + value: 2, + icon: Icons.delete), + popMenuItem( + text: "Attach", + value: 2, + icon: Icons.attach_file), + + ], icon: const Icon( Icons.more_vert, color: Colors.grey, @@ -331,7 +331,7 @@ class EligibiltyScreen extends StatelessWidget { } if (state is EligibilityErrorState) { return SomethingWentWrong(message: state.message, onpressed: (){ - context.read().add(LoadEligibility(token: token,profileId: profileId)); + context.read().add(GetEligibilities(token: token!,profileId: profileId!)); }); } return Container( diff --git a/lib/screens/profile/components/family_background_screen.dart b/lib/screens/profile/components/family_background_screen.dart index 95de086..c0c6368 100644 --- a/lib/screens/profile/components/family_background_screen.dart +++ b/lib/screens/profile/components/family_background_screen.dart @@ -20,6 +20,7 @@ import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/global_context.dart'; import 'package:unit2/utils/text_container.dart'; +import 'package:unit2/widgets/error_state.dart'; import '../../../bloc/profile/family/family_bloc.dart'; import '../../../model/utils/agency.dart'; import '../../../model/utils/category.dart'; @@ -261,7 +262,7 @@ class _FamilyBackgroundScreenState extends State { fatherText.toUpperCase(), style: Theme.of(context) .textTheme - .titleLarge! + .titleSmall! .copyWith(color: primary), ), ), @@ -327,7 +328,7 @@ class _FamilyBackgroundScreenState extends State { vertical: -4), title: Text( - "${father?.relatedPerson?.firstName} ${father?.relatedPerson?.middleName} ${father?.relatedPerson!.lastName} ${father?.relatedPerson?.nameExtension ?? ''}", + "${father?.relatedPerson?.firstName} ${father?.relatedPerson?.middleName??''} ${father?.relatedPerson!.lastName} ${father?.relatedPerson?.nameExtension ?? ''}", style: Theme.of( context) .textTheme @@ -494,7 +495,7 @@ class _FamilyBackgroundScreenState extends State { icon: Icons.edit), popMenuItem( - text: "Reset", + text: "Remove", value: 2, icon: Icons .delete), @@ -547,7 +548,7 @@ class _FamilyBackgroundScreenState extends State { motherText.toUpperCase(), style: Theme.of(context) .textTheme - .titleLarge! + .titleSmall! .copyWith(color: primary), ), ), @@ -611,7 +612,7 @@ class _FamilyBackgroundScreenState extends State { vertical: -4), title: Text( - "${mother?.relatedPerson?.firstName} ${mother?.relatedPerson?.middleName} ${mother?.relatedPerson?.lastName} ${mother?.relatedPerson?.nameExtension ?? ''}", + "${mother?.relatedPerson?.firstName} ${mother?.relatedPerson?.middleName??''} ${mother?.relatedPerson?.lastName} ${mother?.relatedPerson?.nameExtension ?? ''}", style: Theme.of( context) .textTheme @@ -792,7 +793,7 @@ class _FamilyBackgroundScreenState extends State { value: 1, icon: Icons.edit), popMenuItem( - text: "Reset", + text: "Remove", value: 2, icon: Icons.delete), @@ -844,7 +845,7 @@ class _FamilyBackgroundScreenState extends State { spouseText.toUpperCase(), style: Theme.of(context) .textTheme - .titleLarge! + .titleSmall! .copyWith(color: primary), ), ), @@ -937,7 +938,7 @@ class _FamilyBackgroundScreenState extends State { vertical: -4), title: Text( - "${spouse?.relatedPerson?.firstName} ${spouse?.relatedPerson?.middleName} ${spouse?.relatedPerson!.lastName} ${spouse?.relatedPerson?.nameExtension ?? ''}", + "${spouse?.relatedPerson?.firstName} ${spouse?.relatedPerson?.middleName??''} ${spouse?.relatedPerson!.lastName} ${spouse?.relatedPerson?.nameExtension ?? ''}", style: Theme.of( context) .textTheme @@ -1126,7 +1127,7 @@ class _FamilyBackgroundScreenState extends State { .read< FamilyBloc>() .add(DeleteFamily( - id: mother! + id: spouse! .relatedPerson! .id!, profileId: @@ -1209,7 +1210,7 @@ class _FamilyBackgroundScreenState extends State { value: 1, icon: Icons.edit), popMenuItem( - text: "Reset", + text: "Remove", value: 2, icon: Icons.delete), @@ -1267,7 +1268,7 @@ class _FamilyBackgroundScreenState extends State { childrenText.toUpperCase(), style: Theme.of(context) .textTheme - .titleLarge! + .titleSmall! .copyWith(color: primary), ), const Expanded(child: SizedBox()), @@ -1355,7 +1356,7 @@ class _FamilyBackgroundScreenState extends State { horizontal: -4, vertical: -4), title: Text( - "${child.relatedPerson?.firstName} ${child.relatedPerson?.middleName} ${child.relatedPerson?.lastName} ${child.relatedPerson?.nameExtension ?? ''}", + "${child.relatedPerson?.firstName} ${child.relatedPerson?.middleName??''} ${child.relatedPerson?.lastName} ${child.relatedPerson?.nameExtension ?? ''}", style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500)), subtitle: const Text("fullname"), @@ -1501,7 +1502,7 @@ class _FamilyBackgroundScreenState extends State { .edit), popMenuItem( text: - "Reset", + "Remove", value: 2, icon: Icons @@ -1568,7 +1569,7 @@ class _FamilyBackgroundScreenState extends State { otherRelatedText.toUpperCase(), style: Theme.of(context) .textTheme - .titleLarge! + .titleSmall! .copyWith(color: primary), ), ), @@ -1815,7 +1816,7 @@ class _FamilyBackgroundScreenState extends State { .edit), popMenuItem( text: - "Reset", + "Remove", value: 2, icon: Icons @@ -1858,7 +1859,9 @@ class _FamilyBackgroundScreenState extends State { ), ), ]); - } + }if(state is FamilyErrorState){ + return SomethingWentWrong(message: state.message, onpressed: (){context.read().add(GetFamilies(profileId: profileId!, token: token!));}); + } return Container(); }, ); diff --git a/lib/screens/profile/components/learning_and_development_screen.dart b/lib/screens/profile/components/learning_and_development_screen.dart index ae6add0..1ca5d34 100644 --- a/lib/screens/profile/components/learning_and_development_screen.dart +++ b/lib/screens/profile/components/learning_and_development_screen.dart @@ -20,6 +20,7 @@ import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; import '../../../bloc/profile/learningDevelopment/learning_development_bloc.dart'; import '../../../utils/alerts.dart'; +import '../../../widgets/Leadings/close_leading.dart'; import 'learning_development/add_modal.dart'; class LearningAndDevelopmentScreen extends StatelessWidget { @@ -34,16 +35,18 @@ class LearningAndDevelopmentScreen extends StatelessWidget { DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); return Scaffold( appBar: AppBar( - title: const Text(learningAndDevelopmentScreenTitle), + title: context.watch().state is LearningDevelopmentAddingState? const FittedBox(child: Text("Add $learningAndDevelopmentScreenTitle")):context.watch().state is LearningDevelopmentUpdatingState?const FittedBox(child: Text("Edit $learningAndDevelopmentScreenTitle")):const FittedBox(child: Text(learningAndDevelopmentScreenTitle)), centerTitle: true, backgroundColor: primary, - actions: [ + actions: (context.watch().state is LearningDevelopmentLoadedState)?[ AddLeading(onPressed: () { - context - .read() - .add(ShowAddLearningDevelopmentForm()); + context.read().add(ShowAddLearningDevelopmentForm()); }) - ], + ]:(context.watch().state is LearningDevelopmentAddingState || context.watch().state is LearningDevelopmentUpdatingState)?[ + CloseLeading(onPressed:() { + context.read().add(LoadLearniningDevelopment()); + }) + ]:[] ), body: ProgressHUD( padding: const EdgeInsets.all(24), @@ -120,7 +123,7 @@ class LearningAndDevelopmentScreen extends StatelessWidget { if (state is DeleteLearningDevelopmentState) { if (state.success) { successAlert(context, "Deletion Successfull!", - "Deleted Successfully", () { + "Learning Development Has Been Deleted Successfully", () { Navigator.of(context).pop(); context .read() @@ -197,11 +200,11 @@ class LearningAndDevelopmentScreen extends StatelessWidget { .copyWith( fontWeight: FontWeight - .w500), + .w600,color: primary), ), - const Divider(), + const SizedBox( - height: 5, + height: 8, ), Text( provider, @@ -293,19 +296,20 @@ class LearningAndDevelopmentScreen extends StatelessWidget { isOverseas)); } }, - menuItems: [ + menuItems: [ popMenuItem( - text: "Edit", + text: "Update", value: 1, icon: Icons.edit), popMenuItem( - text: "Delete", + text: "Remove", value: 2, icon: Icons.delete), - popMenuItem( - text: "Attachment", - value: 3, - icon: FontAwesome.attach) + popMenuItem( + text: "Attach", + value: 2, + icon: Icons.attach_file), + ], icon: const Icon( Icons.more_vert, @@ -330,7 +334,7 @@ class LearningAndDevelopmentScreen extends StatelessWidget { } if (state is LearningDevelopmentErrorState) { return (SomethingWentWrong( - message: state.message, onpressed: () {})); + message: state.message, onpressed: () {context.read().add(GetLearningDevelopments(profileId: profileId, token: token));})); } if (state is LearningDevelopmentAddingState) { return AddLearningAndDevelopmentScreen( diff --git a/lib/screens/profile/components/learning_development/add_modal.dart b/lib/screens/profile/components/learning_development/add_modal.dart index 52fe09b..14a945d 100644 --- a/lib/screens/profile/components/learning_development/add_modal.dart +++ b/lib/screens/profile/components/learning_development/add_modal.dart @@ -17,7 +17,6 @@ import 'package:unit2/sevices/profile/learningDevelopment_service.dart'; import 'package:unit2/theme-data.dart/btn-style.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/utils/validators.dart'; - import '../../../../model/location/barangay.dart'; import '../../../../model/location/city.dart'; import '../../../../model/location/country.dart'; @@ -28,6 +27,7 @@ import '../../../../model/utils/category.dart'; import '../../../../theme-data.dart/box_shadow.dart'; import '../../../../theme-data.dart/colors.dart'; import '../../../../theme-data.dart/form-style.dart'; +import '../../../../utils/formatters.dart'; import '../../../../utils/global.dart'; import '../../../../utils/location_utilities.dart'; import '../../shared/add_for_empty_search.dart'; @@ -70,7 +70,6 @@ class _AddLearningAndDevelopmentScreenState final addTopicController = TextEditingController(); LearningDevelopmentType? selectedTopic; - ////address Barangay? selectedBarangay; Country? selectedCountry; @@ -106,1273 +105,1415 @@ class _AddLearningAndDevelopmentScreenState bool conductedByCategoryIsPrivate = false; final selectedTrainingController = TextEditingController(); + DateTime? from; + DateTime? to; + @override + void dispose() { + fromDateController.dispose(); + toDateController.dispose(); + addTrainingController.dispose(); + addTopicController.dispose(); + topicFocusNode.dispose(); + + addSponsorAgencyController.dispose(); + sponsorByFocusNode.dispose(); + sponsorAgencyCategoryFocusNode.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { return BlocBuilder( builder: (context, state) { if (state is LearningDevelopmentAddingState) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 24), - child: FormBuilder( - key: formKey, - child: ListView( - children: [ - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - ////Training SearchField - SizedBox( - child: show - ? SearchableDropdownFormField.paginated( - noRecordTex: SizedBox( - width: double.infinity, - height: 300, - child: EmptyWidget( - controller: addTrainingController, - onpressed: () { - setState(() { - show = false; - showOtherInputs = true; - - selectedTrainingController.text = - addTrainingController.text.toUpperCase(); - selectedTraining = - LearningDevelopmentType( - id: null, - title: - selectedTrainingController - .text); - addTrainingController.text =""; - Navigator.of(context).pop(); - Navigator.of(context).pop(); - }); - }, - title: "Add Training"), - ), - isDialogExpanded: false, - hintText: const Text('Search Training'), - margin: const EdgeInsets.all(15), - backgroundDecoration: (child) { - return SizedBox( - width: double.infinity, - child: Card( - child: Padding( - padding: const EdgeInsets.all(16), - child: child), - ), - ); - }, - validator: FormBuilderValidators.required( - errorText: "This field is required"), - paginatedRequest: - (int page, String? searchKey) async { - List paginatedList = - await LearningDevelopmentServices - .instance - .getConductedTrainings( - page: page, - key: searchKey ??= ""); - return paginatedList.map((e) { - return SearchableDropdownMenuItem( - value: e, - onTap: () {}, - label: e.title!.title!, - child: TrainingDisplayDetails( - ////not what you are looking for - notWhatYourLookingFor: () { + return FormBuilder( + key: formKey, + child: SizedBox( + height: screenHeight * 90, + child: Padding( + padding: const EdgeInsets.all(24), + child: StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + Flexible( + child: ListView( + children: [ + ////Training SearchField + SizedBox( + child: show + ? SearchableDropdownFormField.paginated( + noRecordTex: SizedBox( + width: double.infinity, + height: 300, + child: EmptyWidget( + controller: addTrainingController, + onpressed: () { setState(() { - if (e.learningDevelopmentType - ?.id != - null) { - selectedTraining = - LearningDevelopmentType( - id: e.title!.id, - title: null); - } else { - selectedTraining = - LearningDevelopmentType( - id: null, - title: - e.title!.title); - } show = false; showOtherInputs = true; selectedTrainingController - .text = e.title!.title!; + .text = + addTrainingController.text + .toUpperCase(); + selectedTraining = + LearningDevelopmentType( + id: null, + title: + selectedTrainingController + .text); + addTrainingController.text = + ""; + Navigator.of(context).pop(); Navigator.of(context).pop(); }); }, - e: e, - )); - }).toList(); - }, - dropDownMaxHeight: 500, - requestItemCount: 5, - //// on chage - onChanged: (ConductedTraining? value) { - setState(() { - selectedConductedTraining = value; - selectedTrainingController.text = - selectedConductedTraining! - .title!.title!; - show = false; - showTrainingDetails = true; - }); - }, - ) - : const SizedBox(), - ), - const SizedBox( - height: 10, - ), - SizedBox( - ////Training selected Textfield - child: !show - ? FormBuilderTextField( - maxLines: 5, - readOnly: true, - controller: selectedTrainingController, - name: "", - decoration: normalTextFieldStyle("", "") - .copyWith( - labelText: "Training", - suffixIcon: IconButton( - onPressed: () { + title: "Add Training"), + ), + isDialogExpanded: false, + hintText: const Text('Search Training'), + margin: const EdgeInsets.all(15), + backgroundDecoration: (child) { + return SizedBox( + width: double.infinity, + child: Card( + child: Padding( + padding: + const EdgeInsets.all(16), + child: child), + ), + ); + }, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + paginatedRequest: (int page, + String? searchKey) async { + List + paginatedList = + await LearningDevelopmentServices + .instance + .getConductedTrainings( + page: page, + key: searchKey ??= ""); + return paginatedList.map((e) { + return SearchableDropdownMenuItem( + value: e, + onTap: () {}, + label: e.title!.title!, + child: TrainingDisplayDetails( + ////not what you are looking for + notWhatYourLookingFor: () { setState(() { - selectedConductedTraining = - null; + if (e.learningDevelopmentType + ?.id != + null) { + selectedTraining = + LearningDevelopmentType( + id: e.title!.id, + title: null); + } else { + selectedTraining = + LearningDevelopmentType( + id: null, + title: e.title! + .title); + } + show = false; + showOtherInputs = true; + selectedTrainingController - .text = ""; - show = true; - showTrainingDetails = - false; - showOtherInputs = false; - selectedTraining = null; + .text = + e.title!.title!; + Navigator.of(context) + .pop(); }); }, - icon: - const Icon(Icons.close))), - ) - : const SizedBox()), - - ////ShowTraining Details - SizedBox( - child: showTrainingDetails - ? TrainingDetails( - trainingTitle: selectedConductedTraining! - .learningDevelopmentType!.title!, - totalHours: - selectedConductedTraining!.totalHours!, - trainingTopic: selectedConductedTraining! - .topic!.title!, - toDate: selectedConductedTraining!.toDate - .toString(), - fromDate: selectedConductedTraining! - .fromDate! - .toString(), - conductedBy: selectedConductedTraining! - .conductedBy!.name!) - : const SizedBox(), - ), - ///// end show training details - //// show other inputs - SizedBox( - child: showOtherInputs - ? SizedBox( - child: Column(children: [ - const SizedBox( - height: 12, - ), - ////learning development type - FormBuilderDropdown< - LearningDevelopmentType>( - decoration: normalTextFieldStyle( - "Learning Development Type *", - ""), - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - name: "types", - items: state.types - .map((e) => DropdownMenuItem< - LearningDevelopmentType>( - value: e, - child: Text(e.title!))) - .toList(), - onChanged: (value) { - selectedLearningDevelopmentType = - value; - }, - ), - - //// learning development topics - const SizedBox( - height: 12, - ), - StatefulBuilder( - builder: (context, setState) { - return SearchField( - focusNode: topicFocusNode, - itemHeight: 100, - suggestionsDecoration: box1(), - suggestions: state.topics - .map((LearningDevelopmentType - topic) => - SearchFieldListItem( - topic.title!, - item: topic, - child: Padding( - padding: - const EdgeInsets - .symmetric( - horizontal: - 10), - child: ListTile( - title: Text( - topic.title!, - softWrap: true, - ), - )))) - .toList(), - - searchInputDecoration: - normalTextFieldStyle( - "Topic *", "") - .copyWith( - suffixIcon: const Icon( - Icons - .arrow_drop_down)), - onSuggestionTap: (topic) { - if (topic.item?.id != null) { - selectedTopic = - LearningDevelopmentType( - id: topic.item!.id, - title: null); - } else { - selectedTopic = - LearningDevelopmentType( - id: null, - title: - topic.item!.title); - } - setState(() { - topicFocusNode.unfocus(); - }); - }, - ////EMPTY WIDGET - emptyWidget: EmptyWidget( - title: "Add Topic", - controller: addTopicController, - onpressed: () { - setState(() { - LearningDevelopmentType - newTopic = - LearningDevelopmentType( - id: null, - title: addTopicController - .text - .toUpperCase()); - state.topics - .insert(0, newTopic); - topicFocusNode.unfocus(); - addTopicController.text = - ""; - Navigator.pop(context); - }); - }), - validator: (position) { - if (position!.isEmpty) { - return "This field is required"; - } - return null; - }, - ); - }), - - const SizedBox( - height: 12, - ), - SizedBox( - width: screenWidth, - child: Row( - children: [ - //// FROM DATE - Flexible( - flex: 1, - child: DateTimePicker( - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - use24HourFormat: false, - icon: const Icon( - Icons.date_range), - controller: - fromDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), - timeHintText: - "Date of Examination/Conferment", - decoration: - normalTextFieldStyle( - "From *", - "From *") - .copyWith( - prefixIcon: - const Icon( - Icons.date_range, - color: Colors.black87, - )), - initialValue: null, - )), - const SizedBox( - width: 12, - ), - //// TO DATE - Flexible( - flex: 1, - child: DateTimePicker( - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - controller: toDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), - decoration: - normalTextFieldStyle( - "To *", "To *") - .copyWith( - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - ), - ), - initialValue: null, - ), - ), - ], - ), - ), - const SizedBox( - height: 12, - ), - //// total hours conducted - FormBuilderTextField( - validator: numericRequired, - name: "total_hours", - decoration: normalTextFieldStyle( - "Total Hours Conducted *", "0"), - keyboardType: TextInputType.number, - ), - const SizedBox( - height: 12, - ), - ////Address - StatefulBuilder( - builder: (context, setState) { - return Column( - children: [ - ////overseas textformfield - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - setState(() { - overseas = value!; - }); - }, - decoration: - normalTextFieldStyle( - "Overseas Address?", - ''), - name: 'overseas', - title: Text( - overseas ? "YES" : "NO"), - ), - SizedBox( - height: - overseas == true ? 8 : 0, - ), - SizedBox( - child: overseas == false - ? Column( - children: [ - const SizedBox( - height: 12, - ), - ////REGION DROPDOWN - FormBuilderDropdown< - Region?>( - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: - FormBuilderValidators - .required( - errorText: - "This field is required"), - onChanged: (Region? - region) async { - if (selectedRegion != - region) { - setState(() { - provinceCall = - true; - }); - selectedRegion = - region; - getProvinces(); - } - }, - initialValue: null, - decoration: - normalTextFieldStyle( - "Region*", - "Region"), - name: 'region', - items: state.regions.map< - DropdownMenuItem< - Region>>((Region - region) { - return DropdownMenuItem< - Region>( - value: region, - child: Text(region - .description!)); - }).toList(), - ), - const SizedBox( - height: 8, - ), - //// PROVINCE DROPDOWN - SizedBox( - height: 60, - child: - ModalProgressHUD( - color: Colors - .transparent, - inAsyncCall: - provinceCall, - child: DropdownButtonFormField< - Province?>( - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: (value) => - value == - null - ? 'required' - : null, - isExpanded: - true, - value: - selectedProvince, - onChanged: - (Province? - province) { - if (selectedProvince != - province) { - setState( - () { - cityCall = - true; - }); - selectedProvince = - province; - getCities(); - } - }, - items: provinces == - null - ? [] - : provinces!.map< - DropdownMenuItem< - Province>>((Province - province) { - return DropdownMenuItem( - value: province, - child: FittedBox( - child: Text(province.description!), - )); - }).toList(), - decoration: normalTextFieldStyle( - "Province*", - "Province")), - ), - ), - ////CITY MUNICIPALITY - SizedBox( - height: 60, - child: - ModalProgressHUD( - color: - Colors.white, - inAsyncCall: - cityCall, - child: DropdownButtonFormField< - CityMunicipality>( - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - isExpanded: - true, - onChanged: - (CityMunicipality? - city) { - if (selectedMunicipality != - city) { - setState( - () { - barangayCall = - true; - }); - selectedMunicipality = - city; - getBarangays(); - } - }, - decoration: normalTextFieldStyle( - "Municipality*", - "Municipality"), - value: - selectedMunicipality, - items: citymuns == - null - ? [] - : citymuns!.map< - DropdownMenuItem>( - (CityMunicipality - c) { - return DropdownMenuItem( - value: - c, - child: - Text(c.description!)); - }).toList(), - ), - ), - ), - //// BARANGAY - SizedBox( - height: 60, - child: - ModalProgressHUD( - color: - Colors.white, - inAsyncCall: - barangayCall, - child: - DropdownButtonFormField< - Barangay>( - isExpanded: - true, - onChanged: - (Barangay? - baragay) { - selectedBarangay = - baragay; - }, - decoration: normalTextFieldStyle( - "Barangay*", - "Barangay"), - value: - selectedBarangay, - items: barangays == - null - ? [] - : barangays!.map< - DropdownMenuItem< - Barangay>>((Barangay - barangay) { - return DropdownMenuItem( - value: - barangay, - child: - Text(barangay.description!)); - }).toList(), - ), - ), - ), - ], - ) - //// COUNTRY DROPDOWN - : SizedBox( - height: 60, - child: - FormBuilderDropdown< - Country>( - initialValue: null, - validator: - FormBuilderValidators - .required( - errorText: - "This field is required"), - items: state.countries.map< - DropdownMenuItem< - Country>>( - (Country - country) { - return DropdownMenuItem< - Country>( - value: country, - child: FittedBox( - child: Text( - country - .name!))); - }).toList(), - name: 'country', - decoration: - normalTextFieldStyle( - "Country*", - "Country"), - onChanged: - (Country? value) { - selectedCountry = - value; - }, - ), - ), - ), - ], - ); - }), - - ////Conducted By - StatefulBuilder( - builder: (context, setState) { - ////has sponsor switch - return Column( - children: [ - ////Add Conducted Agency============ - SizedBox( - child: SizedBox( - child: Column(children: [ - SearchField( - suggestionDirection: - SuggestionDirection - .up, - itemHeight: 70, - focusNode: - conductedByFocusNode, - suggestions: state - .conductedBy - .map((Agency - agency) => - SearchFieldListItem( - agency.name!, - item: agency, - child: - ListTile( - title: Text( - agency - .name!, - overflow: - TextOverflow - .ellipsis, - ), - subtitle: Text(agency.privateEntity == - true - ? "Private" - : agency.privateEntity == - false - ? "Government" - : ""), - ))) - .toList(), - searchInputDecoration: - normalTextFieldStyle( - " Conducted By *", - "") - .copyWith( - suffixIcon: - GestureDetector( - child: const Icon( - Icons.arrow_drop_down, - ), - onTap: () => - conductedByFocusNode - .unfocus(), - )), - ////SELETECTED - onSuggestionTap: - (agency) { - setState(() { - if (agency.item?.id != - null) { - selectedConductedByAgency = - Agency( - name: null, - id: agency - .item! - .id); - } else { - selectedConductedByAgency = - Agency( - id: null, - name: agency - .item! - .name); - } - - if (agency.item! - .privateEntity == - null) { - showConductedByAgencyCategory = - true; - showConductedByAgencyPrivateRadio = - true; - } else { - showConductedByAgencyCategory = - false; - showConductedByAgencyPrivateRadio = - false; - } - conductedByFocusNode - .unfocus(); - }); - }, - validator: (agency) { - if (agency!.isEmpty) { - return "This field is required"; - } - return null; - }, - ////conducter empty widget - emptyWidget: EmptyWidget( - controller: - addConductedByController, - onpressed: () { + e: e, + )); + }).toList(); + }, + dropDownMaxHeight: 500, + requestItemCount: 5, + //// on chage + onChanged: (ConductedTraining? value) { + setState(() { + selectedConductedTraining = value; + selectedTrainingController.text = + selectedConductedTraining! + .title!.title!; + show = false; + showTrainingDetails = true; + }); + }, + ) + : const SizedBox(), + ), + const SizedBox( + height: 12, + ), + SizedBox( + ////Training selected Textfield + child: !show + ? FormBuilderTextField( + maxLines: 5, + readOnly: true, + controller: + selectedTrainingController, + name: "", + decoration: + normalTextFieldStyle("", "") + .copyWith( + labelText: "Training", + suffixIcon: IconButton( + onPressed: () { setState(() { - Agency newAgency = Agency( - id: null, - name: addConductedByController - .text - .toUpperCase(), - category: - null, - privateEntity: - null); - state.conductedBy - .insert(0, - newAgency); - - addConductedByController + selectedConductedTraining = + null; + selectedTrainingController .text = ""; - Navigator.pop( - context); + show = true; + showTrainingDetails = + false; + showOtherInputs = + false; + selectedTraining = + null; }); }, - title: - "Add Conducted By Agency")), - SizedBox( - height: - showConductedByAgencyCategory - ? 12 - : 0, - ), - ////Sponsor Agency Category - SizedBox( - child: - showConductedByAgencyCategory - ? SearchField( - suggestionDirection: - SuggestionDirection - .up, - focusNode: - conductedByAgencyCategoryFocusNode, - itemHeight: 70, - suggestions: state - .agencyCategory - .map((Category category) => SearchFieldListItem( - category - .name!, - item: - category, - child: - ListTile( - title: - Text(category.name!), - subtitle: Text(category - .industryClass! - .name!), - ))) - .toList(), - emptyWidget: - Container( - height: 100, - decoration: - box1(), - child: const Center( - child: Text( - "No result found ...")), - ), - onSuggestionTap: - (agencyCategory) { - setState(() { - selectedConductedByAgencyCategory = - agencyCategory - .item; - selectedConductedByAgency = Agency( - id: selectedConductedByAgency - ?.id, - name: selectedConductedByAgency! - .name, - category: - selectedConductedByAgencyCategory, - privateEntity: - showConductedByAgencyPrivateRadio); - conductedByAgencyCategoryFocusNode - .unfocus(); - }); - }, - searchInputDecoration: normalTextFieldStyle( - "Category *", - "") - .copyWith( - suffixIcon: - const Icon(Icons.arrow_drop_down)), - validator: - (value) { - if (value! - .isEmpty) { - return "This field is required"; - } - return null; - }, - ) - : const SizedBox(), - ), + icon: const Icon( + Icons.close))), + ) + : const SizedBox()), - ////Sponsor Agency Private Radio - SizedBox( - height: - showConductedByAgencyPrivateRadio - ? 12 - : 0), - SizedBox( - child: showConductedByAgencyPrivateRadio - ? FormBuilderSwitch( - initialValue: - conductedByCategoryIsPrivate, - title: Text( - conductedByCategoryIsPrivate - ? "YES" - : "NO"), - decoration: normalTextFieldStyle( - "Private Entity?", - 'Private Entity?'), - - ////onvhange private sector - onChanged: - (value) { - setState(() { - conductedByCategoryIsPrivate = - value!; - selectedConductedByAgency = Agency( - category: - selectedConductedByAgency - ?.category, - id: selectedConductedByAgency - ?.id, - name: selectedConductedByAgency! - .name, - privateEntity: - conductedByCategoryIsPrivate); - conductedByAgencyCategoryFocusNode - .unfocus(); - }); - }, - name: - 'sponsorAgencyPrivate', - validator: - FormBuilderValidators - .required(), - ) - : const SizedBox()), - ]), - )), - ], - ); - }), - ]), - ) - : const SizedBox()), - const SizedBox( - height: 12, - ), - - ////Sponsor - StatefulBuilder(builder: (context, setState) { - ////has sponsor switch - return Column( - children: [ - FormBuilderSwitch( - initialValue: hasSponsor, - activeColor: second, - onChanged: (value) { - setState(() { - hasSponsor = value!; - }); - }, - decoration: - normalTextFieldStyle("Has Sponsor?", ''), - name: 'sponsor', - title: Text(hasSponsor ? "YES" : "NO"), - ), - ////Add Sponsor Agency============ - SizedBox( - child: hasSponsor + ////ShowTraining Details + SizedBox( + child: showTrainingDetails + ? TrainingDetails( + trainingTitle: + selectedConductedTraining! + .learningDevelopmentType! + .title!, + totalHours: selectedConductedTraining! + .totalHours!, + trainingTopic: + selectedConductedTraining! + .topic!.title!, + toDate: selectedConductedTraining! + .toDate + .toString(), + fromDate: selectedConductedTraining! + .fromDate! + .toString(), + conductedBy: selectedConductedTraining! + .conductedBy!.name!) + : const SizedBox(), + ), + ///// end show training details + //// show other inputs + SizedBox( + child: showOtherInputs ? SizedBox( child: Column(children: [ const SizedBox( height: 12, ), - SearchField( - suggestionDirection: - SuggestionDirection.up, - itemHeight: 70, - focusNode: sponsorByFocusNode, - suggestions: state - .sponsorAgencies - .map((Agency agency) => + ////learning development type + FormBuilderDropdown< + LearningDevelopmentType>( + decoration: normalTextFieldStyle( + "Learning Development Type *", + ""), + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + name: "types", + items: state.types + .map((e) => DropdownMenuItem< + LearningDevelopmentType>( + value: e, + child: Text(e.title!))) + .toList(), + onChanged: (value) { + selectedLearningDevelopmentType = + value; + }, + ), + + //// learning development topics + const SizedBox( + height: 12, + ), + StatefulBuilder( + builder: (context, setState) { + return SearchField( + inputFormatters: [ + UpperCaseTextFormatter() + ], + focusNode: topicFocusNode, + itemHeight: 100, + suggestionsDecoration: box1(), + suggestions: state.topics + .map((LearningDevelopmentType + topic) => SearchFieldListItem( - agency.name!, - item: agency, - child: ListTile( - title: Text( - agency.name!, - overflow: - TextOverflow - .ellipsis, - ), - subtitle: Text(agency - .privateEntity == - true - ? "Private" - : agency.privateEntity == - false - ? "Government" - : ""), - ))) + topic.title!, + item: topic, + child: Padding( + padding: const EdgeInsets + .symmetric( + horizontal: + 10), + child: ListTile( + title: Text( + topic + .title!, + softWrap: + true, + ), + )))) .toList(), + searchInputDecoration: normalTextFieldStyle( - " Sponsor Agency *", - "") + "Topic *", "") .copyWith( suffixIcon: - GestureDetector( - child: const Icon( - Icons.arrow_drop_down, - ), - onTap: () => - sponsorByFocusNode - .unfocus(), + IconButton( + icon: const Icon( + Icons.arrow_drop_down), + onPressed: () { + topicFocusNode.unfocus(); + }, )), - ////SELETECTED - onSuggestionTap: (agency) { + onSuggestionTap: (topic) { + if (topic.item?.id != null) { + selectedTopic = + LearningDevelopmentType( + id: topic.item!.id, + title: null); + } else { + selectedTopic = + LearningDevelopmentType( + id: null, + title: topic + .item!.title); + } setState(() { - if (agency.item?.id != - null) { - selectedSponsorAgency = - Agency( - name: null, - id: agency - .item!.id); - } else { - selectedSponsorAgency = - Agency( - id: null, - name: agency - .item!.name); - } - if (agency.item! - .privateEntity == - null) { - showSponsorCategoryAgency = - true; - showSponsorAgencyPrivateRadio = - true; - } else { - showSponsorCategoryAgency = - false; - showSponsorAgencyPrivateRadio = - false; - } - sponsorByFocusNode - .unfocus(); + topicFocusNode.unfocus(); }); }, - validator: (agency) { - if (agency!.isEmpty) { + ////EMPTY WIDGET + emptyWidget: EmptyWidget( + title: "Add Topic", + controller: + addTopicController, + onpressed: () { + setState(() { + LearningDevelopmentType + newTopic = + LearningDevelopmentType( + id: null, + title: addTopicController + .text + .toUpperCase()); + state.topics.insert( + 0, newTopic); + topicFocusNode + .unfocus(); + addTopicController + .text = ""; + Navigator.pop(context); + }); + }), + validator: (position) { + if (position!.isEmpty) { return "This field is required"; } return null; }, - ////sponsor empty widget - emptyWidget: EmptyWidget( - controller: - addSponsorAgencyController, - onpressed: () { - setState(() { - Agency newAgency = Agency( - id: null, - name: addSponsorAgencyController - .text - .toUpperCase(), - category: null, - privateEntity: - null); - state.sponsorAgencies - .insert( - 0, newAgency); + ); + }), - addSponsorAgencyController - .text = ""; - Navigator.pop(context); - }); - }, - title: - "Add Sponsor Agency")), - SizedBox( - height: showSponsorCategoryAgency - ? 12 - : 0, + const SizedBox( + height: 12, ), - ////Sponsor Agency Category SizedBox( - child: showSponsorCategoryAgency - ? SearchField( - suggestionDirection: - SuggestionDirection - .up, - focusNode: - sponsorAgencyCategoryFocusNode, - itemHeight: 70, - suggestions: state - .agencyCategory - .map((Category - category) => - SearchFieldListItem( - category - .name!, - item: - category, - child: - ListTile( - title: Text( - category - .name!), - subtitle: Text(category - .industryClass! - .name!), - ))) - .toList(), - emptyWidget: Container( - height: 100, - decoration: box1(), - child: const Center( - child: Text( - "No result found ...")), - ), - onSuggestionTap: - (agencyCategory) { - setState(() { - selectedSponsorAgencyCategory = - agencyCategory - .item; - selectedSponsorAgency = Agency( - id: selectedSponsorAgency - ?.id, - name: - selectedSponsorAgency! - .name, - category: - selectedSponsorAgencyCategory, - privateEntity: - sponsorAgencyIsPrivate); - sponsorAgencyCategoryFocusNode - .unfocus(); - }); - }, - searchInputDecoration: - normalTextFieldStyle( - "Category *", - "") - .copyWith( - suffixIcon: - const Icon( - Icons - .arrow_drop_down)), - validator: (value) { - if (value!.isEmpty) { - return "This field is required"; - } - return null; - }, - ) - : const SizedBox(), - ), - - ////Sponsor Agency Private Radio - SizedBox( - height: - showSponsorAgencyPrivateRadio - ? 12 - : 0), - SizedBox( - child: - showSponsorAgencyPrivateRadio - ? FormBuilderSwitch( - initialValue: - sponsorAgencyIsPrivate, - title: Text( - sponsorAgencyIsPrivate - ? "YES" - : "NO"), - decoration: normalTextFieldStyle( - "Private Entity?", - 'Private Entity?'), - - ////onvhange private sector - onChanged: (value) { - setState(() { - sponsorAgencyIsPrivate = - value!; - selectedSponsorAgency = Agency( - category: - selectedSponsorAgency - ?.category, - id: selectedSponsorAgency - ?.id, - name: selectedSponsorAgency! - .name, - privateEntity: - selectedSponsorAgency - ?.privateEntity); - sponsorAgencyCategoryFocusNode - .unfocus(); - }); - }, - - name: - 'sponsorAgencyPrivate', + width: screenWidth, + child: StatefulBuilder( + builder: (context, setState) { + return StatefulBuilder( + builder: (context,setState) { + return Row( + children: [ + //// FROM DATE + Flexible( + flex: 1, + child: DateTimePicker( + validator: + FormBuilderValidators + .required( + errorText: + "This field is required"), + use24HourFormat: + false, + icon: const Icon( + Icons.date_range), + controller: + fromDateController, + firstDate: + DateTime(1990), + lastDate: + DateTime(2100), + selectableDayPredicate: + (date) { + if (to != null && + to!.microsecondsSinceEpoch <= + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, + onChanged: (value) { + setState(() { + from = DateTime + .parse(value); + }); + }, + initialDate: to == + null + ? DateTime.now() + : to!.subtract( + const Duration( + days: 1)), + timeHintText: + "Date of Examination/Conferment", + decoration: + normalTextFieldStyle( + "From *", + "From *") + .copyWith( + prefixIcon: + const Icon( + Icons.date_range, + color: + Colors.black87, + )), + initialValue: null, + )), + const SizedBox( + width: 12, + ), + //// TO DATE + Flexible( + flex: 1, + child: DateTimePicker( validator: FormBuilderValidators - .required(), + .required( + errorText: + "This field is required"), + controller: + toDateController, + firstDate: + DateTime(1990), + selectableDayPredicate: + (date) { + if (from != null && + from!.microsecondsSinceEpoch >= + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, + onChanged: (value) { + setState(() { + to = DateTime.parse( + value); + }); + }, + initialDate: from == + null + ? DateTime.now() + : from!.add( + const Duration( + days: 1)), + lastDate: + DateTime(2100), + decoration: + normalTextFieldStyle( + "To *", + "To *") + .copyWith( + prefixIcon: + const Icon( + Icons.date_range, + color: + Colors.black87, + ), + ), + initialValue: null, + ), + ), + ], + ); + } + ); + }), + ), + const SizedBox( + height: 12, + ), + //// total hours conducted + FormBuilderTextField( + validator: numericRequired, + name: "total_hours", + decoration: normalTextFieldStyle( + "Total Hours Conducted *", + "0"), + keyboardType: + TextInputType.number, + ), + const SizedBox( + height: 12, + ), + ////Address + StatefulBuilder( + builder: (context, setState) { + return Column( + children: [ + ////overseas textformfield + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: + normalTextFieldStyle( + "Overseas Address?", + ''), + name: 'overseas', + title: Text(overseas + ? "YES" + : "NO"), + ), + SizedBox( + height: overseas == true + ? 8 + : 0, + ), + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + FormBuilderDropdown< + Region?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + onChanged: (Region? + region) async { + if (selectedRegion != + region) { + setState( + () { + provinceCall = + true; + }); + selectedRegion = + region; + getProvinces(); + } + }, + initialValue: + null, + decoration: + normalTextFieldStyle( + "Region*", + "Region"), + name: 'region', + items: state.regions.map< + DropdownMenuItem< + Region>>((Region + region) { + return DropdownMenuItem< + Region>( + value: + region, + child: Text( + region + .description!)); + }).toList(), + ), + const SizedBox( + height: 12, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: + ModalProgressHUD( + color: Colors + .transparent, + inAsyncCall: + provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => value == null + ? 'required' + : null, + isExpanded: + true, + value: + selectedProvince, + onChanged: + (Province? + province) { + if (selectedProvince != + province) { + setState( + () { + cityCall = + true; + }); + selectedProvince = + province; + getCities(); + } + }, + items: provinces == + null + ? [] + : provinces!.map>((Province + province) { + return DropdownMenuItem( + value: province, + child: FittedBox( + child: Text(province.description!), + )); + }).toList(), + decoration: normalTextFieldStyle( + "Province*", + "Province")), + ), + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: + ModalProgressHUD( + color: Colors + .white, + inAsyncCall: + cityCall, + child: DropdownButtonFormField< + CityMunicipality>( + validator: FormBuilderValidators.required( + errorText: + "This field is required"), + isExpanded: + true, + onChanged: + (CityMunicipality? + city) { + if (selectedMunicipality != + city) { + setState( + () { + barangayCall = + true; + }); + selectedMunicipality = + city; + getBarangays(); + } + }, + decoration: normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: + selectedMunicipality, + items: citymuns == + null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>((CityMunicipality + c) { + return DropdownMenuItem( + value: c, + child: Text(c.description!)); + }).toList(), + ), + ), + ), + //// BARANGAY + SizedBox( + height: 60, + child: + ModalProgressHUD( + color: Colors + .white, + inAsyncCall: + barangayCall, + child: DropdownButtonFormField< + Barangay>( + isExpanded: + true, + onChanged: + (Barangay? + baragay) { + selectedBarangay = + baragay; + }, + decoration: normalTextFieldStyle( + "Barangay*", + "Barangay"), + value: + selectedBarangay, + items: barangays == + null + ? [] + : barangays!.map< + DropdownMenuItem< + Barangay>>((Barangay + barangay) { + return DropdownMenuItem( + value: barangay, + child: Text(barangay.description!)); + }).toList(), + ), + ), + ), + ], ) - : const SizedBox()), + //// COUNTRY DROPDOWN + : SizedBox( + height: 60, + child: + FormBuilderDropdown< + Country>( + initialValue: + null, + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + items: state + .countries + .map< + DropdownMenuItem< + Country>>((Country + country) { + return DropdownMenuItem< + Country>( + value: + country, + child: FittedBox( + child: Text( + country.name!))); + }).toList(), + name: 'country', + decoration: + normalTextFieldStyle( + "Country*", + "Country"), + onChanged: + (Country? + value) { + selectedCountry = + value; + }, + ), + ), + ), + ], + ); + }), + + ////Conducted By + StatefulBuilder( + builder: (context, setState) { + ////has sponsor switch + return Column( + children: [ + ////Add Conducted Agency============ + SizedBox( + child: SizedBox( + child: Column(children: [ + SearchField( + inputFormatters: [ + UpperCaseTextFormatter() + ], + itemHeight: 100, + focusNode: + conductedByFocusNode, + suggestions: state + .conductedBy + .map((Agency + agency) => + SearchFieldListItem( + agency + .name!, + item: + agency, + child: + ListTile( + title: + Text( + agency + .name!, + overflow: + TextOverflow.visible, + ), + subtitle: Text(agency.privateEntity == + true + ? "Private" + : agency.privateEntity == false + ? "Government" + : ""), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle( + " Conducted By *", + "") + .copyWith( + suffixIcon: + GestureDetector( + child: const Icon( + Icons + .arrow_drop_down, + ), + onTap: () => + conductedByFocusNode + .unfocus(), + )), + ////SELETECTED + onSuggestionTap: + (agency) { + setState(() { + if (agency.item + ?.id != + null) { + selectedConductedByAgency = + Agency( + name: + null, + id: agency + .item! + .id); + } else { + selectedConductedByAgency = Agency( + id: null, + name: agency + .item! + .name); + } + + if (agency.item! + .privateEntity == + null) { + showConductedByAgencyCategory = + true; + showConductedByAgencyPrivateRadio = + true; + } else { + showConductedByAgencyCategory = + false; + showConductedByAgencyPrivateRadio = + false; + } + conductedByFocusNode + .unfocus(); + }); + }, + validator: (agency) { + if (agency! + .isEmpty) { + return "This field is required"; + } + return null; + }, + ////conducter empty widget + emptyWidget: + EmptyWidget( + controller: + addConductedByController, + onpressed: + () { + setState( + () { + Agency newAgency = Agency( + id: + null, + name: addConductedByController + .text + .toUpperCase(), + category: + null, + privateEntity: + null); + state + .conductedBy + .insert( + 0, + newAgency); + + addConductedByController + .text = ""; + Navigator.pop( + context); + }); + }, + title: + "Add Conducted By Agency")), + SizedBox( + height: + showConductedByAgencyCategory + ? 12 + : 0, + ), + ////Sponsor Agency Category + SizedBox( + child: + showConductedByAgencyCategory + ? SearchField( + suggestionDirection: + SuggestionDirection + .up, + focusNode: + conductedByAgencyCategoryFocusNode, + itemHeight: + 70, + suggestions: state + .agencyCategory + .map((Category category) => SearchFieldListItem( + category + .name!, + item: + category, + child: + ListTile( + title: Text(category.name!), + subtitle: Text(category.industryClass!.name!), + ))) + .toList(), + emptyWidget: + Container( + height: + 100, + decoration: + box1(), + child: const Center( + child: + Text("No result found ...")), + ), + onSuggestionTap: + (agencyCategory) { + setState( + () { + selectedConductedByAgencyCategory = + agencyCategory.item; + selectedConductedByAgency = Agency( + id: selectedConductedByAgency + ?.id, + name: selectedConductedByAgency! + .name, + category: + selectedConductedByAgencyCategory, + privateEntity: + showConductedByAgencyPrivateRadio); + conductedByAgencyCategoryFocusNode + .unfocus(); + }); + }, + searchInputDecoration: normalTextFieldStyle( + "Category *", + "") + .copyWith( + suffixIcon: + IconButton( + icon: const Icon( + Icons + .arrow_drop_down), + onPressed: + () { + conductedByAgencyCategoryFocusNode + .unfocus(); + }, + )), + validator: + (value) { + if (value! + .isEmpty) { + return "This field is required"; + } + return null; + }, + ) + : const SizedBox(), + ), + + ////Sponsor Agency Private Radio + SizedBox( + height: + showConductedByAgencyPrivateRadio + ? 12 + : 0), + SizedBox( + child: showConductedByAgencyPrivateRadio + ? FormBuilderSwitch( + initialValue: + conductedByCategoryIsPrivate, + title: Text( + conductedByCategoryIsPrivate + ? "YES" + : "NO"), + decoration: normalTextFieldStyle( + "Private Entity?", + 'Private Entity?'), + + ////onvhange private sector + onChanged: + (value) { + setState( + () { + conductedByCategoryIsPrivate = + value!; + selectedConductedByAgency = Agency( + category: selectedConductedByAgency + ?.category, + id: selectedConductedByAgency + ?.id, + name: selectedConductedByAgency! + .name, + privateEntity: + conductedByCategoryIsPrivate); + conductedByAgencyCategoryFocusNode + .unfocus(); + }); + }, + name: + 'sponsorAgencyPrivate', + validator: + FormBuilderValidators + .required(), + ) + : const SizedBox()), + ]), + )), + ], + ); + }), ]), ) - : const SizedBox(), - ), - ], - ); - }), - const SizedBox( - height: 12, + : const SizedBox()), + const SizedBox( + height: 12, + ), + + ////Sponsor + StatefulBuilder(builder: (context, setState) { + ////has sponsor switch + return Column( + children: [ + FormBuilderSwitch( + initialValue: hasSponsor, + activeColor: second, + onChanged: (value) { + setState(() { + hasSponsor = value!; + }); + }, + decoration: normalTextFieldStyle( + "Has Sponsor?", ''), + name: 'sponsor', + title: Text(hasSponsor ? "YES" : "NO"), + ), + ////Add Sponsor Agency============ + SizedBox( + child: hasSponsor + ? SizedBox( + child: Column(children: [ + const SizedBox( + height: 12, + ), + SearchField( + inputFormatters: [ + UpperCaseTextFormatter() + ], + itemHeight: 110, + focusNode: + sponsorByFocusNode, + suggestions: state + .sponsorAgencies + .map((Agency agency) => + SearchFieldListItem( + agency.name!, + item: agency, + child: ListTile( + title: Text( + agency + .name!, + overflow: + TextOverflow + .visible, + ), + subtitle: Text(agency + .privateEntity == + true + ? "Private" + : agency.privateEntity == + false + ? "Government" + : ""), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle( + " Sponsor Agency *", + "") + .copyWith( + suffixIcon: + GestureDetector( + child: const Icon( + Icons.arrow_drop_down, + ), + onTap: () => + sponsorByFocusNode + .unfocus(), + )), + ////SELETECTED + onSuggestionTap: (agency) { + setState(() { + if (agency.item?.id != + null) { + selectedSponsorAgency = + Agency( + name: null, + id: agency + .item! + .id); + } else { + selectedSponsorAgency = + Agency( + id: null, + name: agency + .item! + .name); + } + if (agency.item! + .privateEntity == + null) { + showSponsorCategoryAgency = + true; + showSponsorAgencyPrivateRadio = + true; + } else { + showSponsorCategoryAgency = + false; + showSponsorAgencyPrivateRadio = + false; + } + sponsorByFocusNode + .unfocus(); + }); + }, + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + ////sponsor empty widget + emptyWidget: EmptyWidget( + controller: + addSponsorAgencyController, + onpressed: () { + setState(() { + Agency newAgency = Agency( + id: null, + name: addSponsorAgencyController + .text + .toUpperCase(), + category: null, + privateEntity: + null); + state + .sponsorAgencies + .insert(0, + newAgency); + + addSponsorAgencyController + .text = ""; + Navigator.pop( + context); + }); + }, + title: + "Add Sponsor Agency")), + SizedBox( + height: + showSponsorCategoryAgency + ? 12 + : 0, + ), + ////Sponsor Agency Category + SizedBox( + child: + showSponsorCategoryAgency + ? SearchField( + suggestionDirection: + SuggestionDirection + .up, + focusNode: + sponsorAgencyCategoryFocusNode, + itemHeight: 70, + suggestions: state + .agencyCategory + .map((Category + category) => + SearchFieldListItem( + category + .name!, + item: + category, + child: + ListTile( + title: + Text(category.name!), + subtitle: + Text(category.industryClass!.name!), + ))) + .toList(), + emptyWidget: + Container( + height: 100, + decoration: + box1(), + child: const Center( + child: Text( + "No result found ...")), + ), + onSuggestionTap: + (agencyCategory) { + setState(() { + selectedSponsorAgencyCategory = + agencyCategory + .item; + selectedSponsorAgency = Agency( + id: selectedSponsorAgency + ?.id, + name: selectedSponsorAgency! + .name, + category: + selectedSponsorAgencyCategory, + privateEntity: + sponsorAgencyIsPrivate); + sponsorAgencyCategoryFocusNode + .unfocus(); + }); + }, + searchInputDecoration: + normalTextFieldStyle( + "Category *", + "") + .copyWith( + suffixIcon: + IconButton( + icon: const Icon( + Icons + .arrow_drop_down), + onPressed: () { + sponsorAgencyCategoryFocusNode + .unfocus(); + }, + )), + validator: + (value) { + if (value! + .isEmpty) { + return "This field is required"; + } + return null; + }, + ) + : const SizedBox(), + ), + + ////Sponsor Agency Private Radio + SizedBox( + height: + showSponsorAgencyPrivateRadio + ? 12 + : 0), + SizedBox( + child: + showSponsorAgencyPrivateRadio + ? FormBuilderSwitch( + initialValue: + sponsorAgencyIsPrivate, + title: Text( + sponsorAgencyIsPrivate + ? "YES" + : "NO"), + decoration: normalTextFieldStyle( + "Private Entity?", + 'Private Entity?'), + + ////onvhange private sector + onChanged: + (value) { + setState(() { + sponsorAgencyIsPrivate = + value!; + selectedSponsorAgency = Agency( + category: + selectedSponsorAgency + ?.category, + id: selectedSponsorAgency + ?.id, + name: selectedSponsorAgency! + .name, + privateEntity: + selectedSponsorAgency?.privateEntity); + sponsorAgencyCategoryFocusNode + .unfocus(); + }); + }, + + name: + 'sponsorAgencyPrivate', + validator: + FormBuilderValidators + .required(), + ) + : const SizedBox()), + ]), + ) + : const SizedBox(), + ), + ], + ); + }), + + const SizedBox( + height: 12, + ), + FormBuilderTextField( + validator: numericRequired, + name: "total_hours_attended", + keyboardType: TextInputType.number, + decoration: normalTextFieldStyle( + "Total Hours Attended *", + "Total Hours Attended *"), + ), + ], ), - const SizedBox( - height: 12, + ), + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + child: const Text(submit), + onPressed: () { + if (formKey.currentState!.saveAndValidate()) { + ConductedTraining? training; + Agency? sponsor; + Venue venue; + ////Address + if (overseas) { + venue = Venue( + id: null, + country: selectedCountry, + barangay: null, + category: null, + areaClass: null, + cityMunicipality: null); + } else { + venue = Venue( + id: null, + country: Country( + id: 175, + name: 'Philippines', + code: 'PH'), + barangay: selectedBarangay, + areaClass: null, + category: null, + cityMunicipality: selectedMunicipality); + } + if (showTrainingDetails) { + training = ConductedTraining( + locked: false, + id: selectedConductedTraining!.id, + venue: Venue( + country: selectedConductedTraining! + .venue!.country)); + } else { + training = ConductedTraining( + title: selectedTraining, + topic: selectedTopic, + id: null, + locked: false, + venue: venue, + toDate: + DateTime.parse(toDateController.text), + fromDate: DateTime.parse( + fromDateController.text), + totalHours: double.parse(formKey + .currentState!.value['total_hours']), + conductedBy: selectedConductedByAgency, + sessionsAttended: [], + learningDevelopmentType: + selectedLearningDevelopmentType); + } + if (hasSponsor) { + sponsor = selectedSponsorAgency; + } + LearningDevelopement learningDevelopement = + LearningDevelopement( + attachments: null, + sponsoredBy: sponsor, + conductedTraining: training, + totalHoursAttended: double.parse(formKey + .currentState! + .value['total_hours_attended'])); + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add( + AddLearningAndDevelopment( + learningDevelopement: + learningDevelopement, + profileId: widget.profileId, + token: widget.token)); + } + }, ), - FormBuilderTextField( - validator: numericRequired, - name: "total_hours_attended", - keyboardType: TextInputType.number, - decoration: normalTextFieldStyle( - "Total Hours Attended *", - "Total Hours Attended *"), - ), - ], - ); - }), - const SizedBox( - height: 12, - ), - SizedBox( - height: 60, - child: ElevatedButton( - style: - mainBtnStyle(primary, Colors.transparent, second), - child: const Text(submit), - onPressed: () { - if (formKey.currentState!.saveAndValidate()) { - ConductedTraining? training; - Agency? sponsor; - Venue venue; - ////Address - if (overseas) { - venue = Venue( - id: null, - country: selectedCountry, - barangay: null, - category: null, - areaClass: null, - cityMunicipality: null); - } else { - venue = Venue( - id: null, - country: Country( - id: 175, name: 'Philippines', code: 'PH'), - barangay: selectedBarangay, - areaClass: null, - category: null, - cityMunicipality: selectedMunicipality); - } - if (showTrainingDetails) { - training = ConductedTraining( - locked: false, - id: selectedConductedTraining!.id, - venue: Venue( - country: selectedConductedTraining! - .venue!.country)); - } else { - training = ConductedTraining( - title: selectedTraining, - topic: selectedTopic, - id: null, - locked: false, - venue: venue, - toDate: DateTime.parse(toDateController.text), - fromDate: - DateTime.parse(fromDateController.text), - totalHours: double.parse(formKey - .currentState!.value['total_hours']), - conductedBy: selectedConductedByAgency, - sessionsAttended: [], - learningDevelopmentType: - selectedLearningDevelopmentType); - } - if (hasSponsor) { - sponsor = selectedSponsorAgency; - } - LearningDevelopement learningDevelopement = - LearningDevelopement( - attachments: null, - sponsoredBy: sponsor, - conductedTraining: training, - totalHoursAttended: double.parse(formKey - .currentState! - .value['total_hours_attended'])); - final progress = ProgressHUD.of(context); - progress!.showWithText("Loading..."); - context.read().add( - AddLearningAndDevelopment( - learningDevelopement: learningDevelopement, - profileId: widget.profileId, - token: widget.token)); - } - }, - ), - ) - ], - )), - ); + ), + ], + ); + }), + ), + )); } return const Center( child: Text("ds"), diff --git a/lib/screens/profile/components/learning_development/edit_modal.dart b/lib/screens/profile/components/learning_development/edit_modal.dart index 58f5426..7951297 100644 --- a/lib/screens/profile/components/learning_development/edit_modal.dart +++ b/lib/screens/profile/components/learning_development/edit_modal.dart @@ -7,17 +7,12 @@ import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:intl/intl.dart'; import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; -import 'package:searchable_paginated_dropdown/searchable_paginated_dropdown.dart'; import 'package:searchfield/searchfield.dart'; import 'package:unit2/bloc/profile/learningDevelopment/learning_development_bloc.dart'; import 'package:unit2/model/profile/learning_development.dart'; -import 'package:unit2/screens/profile/components/learning_development/display_details.dart'; -import 'package:unit2/screens/profile/components/learning_development/training_details.dart'; -import 'package:unit2/sevices/profile/learningDevelopment_service.dart'; import 'package:unit2/theme-data.dart/btn-style.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/utils/validators.dart'; - import '../../../../model/location/barangay.dart'; import '../../../../model/location/city.dart'; import '../../../../model/location/country.dart'; @@ -28,6 +23,7 @@ import '../../../../model/utils/category.dart'; import '../../../../theme-data.dart/box_shadow.dart'; import '../../../../theme-data.dart/colors.dart'; import '../../../../theme-data.dart/form-style.dart'; +import '../../../../utils/formatters.dart'; import '../../../../utils/global.dart'; import '../../../../utils/location_utilities.dart'; import '../../shared/add_for_empty_search.dart'; @@ -108,6 +104,28 @@ class _EditLearningAndDevelopmentScreenState final selectedConductedByController = TextEditingController(); final selectedTrainingController = TextEditingController(); + DateTime? from; + DateTime? to; + @override + void dispose() { + fromDateController.dispose(); + toDateController.dispose(); + addTrainingController.dispose(); + topicFocusNode.dispose(); + addTopicController.dispose(); + currentTopicController.dispose(); + addSponsorAgencyController.dispose(); + sponsorByFocusNode.dispose(); + sponsorAgencyCategoryFocusNode.dispose(); + selectedSponsorAgencyController.dispose(); + conductedByFocusNode.dispose(); + conductedByAgencyCategoryFocusNode.dispose(); + addConductedByController.dispose(); + selectedConductedByController.dispose(); + + selectedTrainingController.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { @@ -163,1232 +181,1400 @@ class _EditLearningAndDevelopmentScreenState } else { enabled = true; } - return Padding( - padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 24), - child: FormBuilder( - key: formKey, - child: ListView( - children: [ - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - const SizedBox( - height: 10, - ), - SizedBox( - ////Training selected Textfield - child: FormBuilderTextField( - onChanged: (value) { - selectedTraining = LearningDevelopmentType( - id: null, title: selectedTrainingController.text); - }, - enabled: enabled, - maxLines: 5, - controller: selectedTrainingController, - name: "", - decoration: normalTextFieldStyle("", "").copyWith( - labelText: "Training", - filled: !enabled, - fillColor: Colors.grey.shade300), - )), - SizedBox( - child: SizedBox( - child: Column(children: [ - const SizedBox( - height: 12, - ), - ////learning development type - FormBuilderDropdown( - enabled: enabled, - name: "types", - decoration: normalTextFieldStyle( - "Learning Development Type *", "") - .copyWith( - filled: !enabled, - fillColor: Colors.grey.shade300), - validator: FormBuilderValidators.required( - errorText: "This field is required"), - items: state.types - .map((e) => DropdownMenuItem< - LearningDevelopmentType>( - value: e, child: Text(e.title!))) - .toList(), - onChanged: (value) { - selectedLearningDevelopmentType = value; - }, - initialValue: selectedLearningDevelopmentType, - ), + from = state.learningDevelopement.conductedTraining?.fromDate; + to = state.learningDevelopement.conductedTraining?.toDate; + return FormBuilder( + key: formKey, + child: SizedBox( + height: screenHeight * 90, + child: Padding( + padding: const EdgeInsets.all(24), + child: Column( + children: [ + Flexible( + child: ListView( + children: [ + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + const SizedBox( + height: 10, + ), + SizedBox( + ////Training selected Textfield + child: FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], + onChanged: (value) { + selectedTraining = + LearningDevelopmentType( + id: null, + title: selectedTrainingController + .text); + }, + enabled: enabled, + maxLines: 5, + controller: selectedTrainingController, + name: "", + decoration: normalTextFieldStyle("", "") + .copyWith( + labelText: "Training", + filled: !enabled, + fillColor: Colors.grey.shade300), + )), + SizedBox( + child: SizedBox( + child: Column(children: [ + const SizedBox( + height: 12, + ), + ////learning development type + FormBuilderDropdown< + LearningDevelopmentType>( + enabled: enabled, + name: "types", + decoration: normalTextFieldStyle( + "Learning Development Type *", + "") + .copyWith( + filled: !enabled, + fillColor: + Colors.grey.shade300), + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + items: state.types + .map((e) => DropdownMenuItem< + LearningDevelopmentType>( + value: e, + child: Text(e.title!))) + .toList(), + onChanged: (value) { + selectedLearningDevelopmentType = + value; + }, + initialValue: + selectedLearningDevelopmentType, + ), - //// learning development topics - const SizedBox( - height: 12, - ), - StatefulBuilder(builder: (context, setState) { - return SearchField( - enabled: enabled, - controller: currentTopicController, - focusNode: topicFocusNode, - itemHeight: 100, - suggestionsDecoration: box1(), - suggestions: state.topics - .map((LearningDevelopmentType topic) => - SearchFieldListItem(topic.title!, - item: topic, - child: Padding( - padding: const EdgeInsets - .symmetric( - horizontal: 10), - child: ListTile( - title: Text( + //// learning development topics + const SizedBox( + height: 12, + ), + StatefulBuilder( + builder: (context, setState) { + return SearchField( + inputFormatters: [ + UpperCaseTextFormatter() + ], + + enabled: enabled, + controller: currentTopicController, + focusNode: topicFocusNode, + itemHeight: 100, + suggestionsDecoration: box1(), + suggestions: state.topics + .map((LearningDevelopmentType + topic) => + SearchFieldListItem( topic.title!, - softWrap: true, - ), - )))) - .toList(), + item: topic, + child: Padding( + padding: + const EdgeInsets + .symmetric( + horizontal: + 10), + child: ListTile( + title: Text( + topic.title!, + softWrap: true, + ), + )))) + .toList(), - searchInputDecoration: - normalTextFieldStyle("Topic *", "") - .copyWith( - filled: !enabled, - fillColor: Colors.grey.shade300, - suffixIcon: const Icon( - Icons.arrow_drop_down)), - onSuggestionTap: (topic) { - if (topic.item?.id != null) { - selectedTopic = LearningDevelopmentType( - id: topic.item!.id, title: null); - } else { - selectedTopic = LearningDevelopmentType( - id: null, title: topic.item!.title); - } - setState(() { - topicFocusNode.unfocus(); - }); - }, - ////EMPTY WIDGET - emptyWidget: EmptyWidget( - title: "Add Topic", - controller: addTopicController, - onpressed: () { - setState(() { - LearningDevelopmentType newTopic = - LearningDevelopmentType( - id: null, - title: addTopicController.text - .toUpperCase()); - state.topics.insert(0, newTopic); - topicFocusNode.unfocus(); - addTopicController.text = ""; - Navigator.pop(context); - }); + searchInputDecoration: + normalTextFieldStyle("Topic *", + "") + .copyWith( + filled: !enabled, + fillColor: + Colors.grey.shade300, + suffixIcon: IconButton( + icon: const Icon(Icons + .arrow_drop_down), + onPressed: () { + topicFocusNode + .unfocus(); + }, + )), + onSuggestionTap: (topic) { + if (topic.item?.id != null) { + selectedTopic = + LearningDevelopmentType( + id: topic.item!.id, + title: null); + } else { + selectedTopic = + LearningDevelopmentType( + id: null, + title: topic.item!.title); + } + setState(() { + topicFocusNode.unfocus(); + }); + }, + ////EMPTY WIDGET + emptyWidget: EmptyWidget( + title: "Add Topic", + controller: addTopicController, + onpressed: () { + setState(() { + LearningDevelopmentType + newTopic = + LearningDevelopmentType( + id: null, + title: + addTopicController + .text + .toUpperCase()); + state.topics + .insert(0, newTopic); + topicFocusNode.unfocus(); + addTopicController.text = ""; + Navigator.pop(context); + }); + }), + validator: (position) { + if (position!.isEmpty) { + return "This field is required"; + } + return null; + }, + ); }), - validator: (position) { - if (position!.isEmpty) { - return "This field is required"; - } - return null; - }, - ); - }), - const SizedBox( - height: 12, - ), - SizedBox( - width: screenWidth, - child: Row( - children: [ - //// FROM DATE - Flexible( - flex: 1, - child: DateTimePicker( - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - use24HourFormat: false, - icon: const Icon(Icons.date_range), - controller: fromDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), - timeHintText: - "Date of Examination/Conferment", - decoration: normalTextFieldStyle( - "From *", "From *") - .copyWith( + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + //// FROM DATE + Flexible( + flex: 1, + child: DateTimePicker( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + use24HourFormat: false, + icon: const Icon( + Icons.date_range), + controller: + fromDateController, + firstDate: DateTime(1990), + lastDate: DateTime(2100), + selectableDayPredicate: + (date) { + if (to != null && + to!.microsecondsSinceEpoch <= + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, + onChanged: (value) { + setState(() { + from = + DateTime.parse(value); + }); + }, + initialDate: to == null + ? DateTime.now() + : to!.subtract( + const Duration( + days: 1)), + timeHintText: + "Date of Examination/Conferment", + decoration: + normalTextFieldStyle( + "From *", "From *") + .copyWith( + filled: !enabled, + fillColor: Colors + .grey + .shade300, + prefixIcon: + const Icon( + Icons + .date_range, + color: Colors + .black87, + )), + )), + const SizedBox( + width: 12, + ), + //// TO DATE + Flexible( + flex: 1, + child: DateTimePicker( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + controller: toDateController, + firstDate: DateTime(1990), + lastDate: DateTime(2100), + decoration: + normalTextFieldStyle( + "To *", "To *") + .copyWith( filled: !enabled, fillColor: Colors.grey.shade300, prefixIcon: const Icon( Icons.date_range, color: Colors.black87, - )), - initialValue: null, - )), - const SizedBox( - width: 12, - ), - //// TO DATE - Flexible( - flex: 1, - child: DateTimePicker( - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - controller: toDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), - decoration: - normalTextFieldStyle("To *", "To *") - .copyWith( - filled: !enabled, - fillColor: Colors.grey.shade300, - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - ), - ), - initialValue: null, - ), - ), - ], - ), - ), - const SizedBox( - height: 12, - ), - //// total hours conducted - FormBuilderTextField( - initialValue: state.learningDevelopement - .conductedTraining!.totalHours - .toString(), - validator: numericRequired, - name: "total_hours", - decoration: normalTextFieldStyle( - "Total Hours Conducted *", - "0", - ).copyWith( - filled: !enabled, - fillColor: Colors.grey.shade300), - keyboardType: TextInputType.number, - ), - const SizedBox( - height: 12, - ), - ////Address - - const SizedBox( - height: 12, - ), - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - setState(() { - overseas = value!; - }); - }, - decoration: normalTextFieldStyle( - "Overseas Address?", ''), - name: 'overseas', - title: Text(overseas ? "YES" : "NO"), - ), - SizedBox( - height: overseas == true ? 8 : 0, - ), - SizedBox( - child: overseas == false - ? Column( - children: [ - const SizedBox( - height: 12, - ), - ////REGION DROPDOWN - FormBuilderDropdown( - enabled: !enabled - ? overseas - : true, - name: "region", - isExpanded: true, - initialValue: selectedRegion, - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - onChanged: - (Region? region) async { - if (selectedRegion != - region) { - setState(() { - provinceCall = true; - }); - selectedRegion = region; - //// GET PROVINCES - provinces = await LocationUtils - .instance - .getProvinces( - regionCode: - selectedRegion! - .code - .toString()); - selectedProvince = - provinces![0]; - setState(() { - provinceCall = false; - cityCall = true; - }); - //// GET CITIES - citymuns = await LocationUtils - .instance - .getCities( - code: - selectedProvince! - .code!); - selectedMunicipality = - citymuns![0]; - setState(() { - cityCall = false; - barangayCall = true; - }); - //// GET BARANGAY - barangays = await LocationUtils - .instance - .getBarangay( - code: - selectedMunicipality! - .code!); - selectedBarangay = - barangays![0]; - setState(() { - barangayCall = false; - }); - ////GET CITY MUNICIPALITY - citymuns = await LocationUtils - .instance - .getCities( - code: - selectedProvince! - .code!); - selectedMunicipality = - citymuns![0]; - setState(() { - cityCall = false; - barangayCall = true; - }); - //// GET BARANGAYS - barangays = await LocationUtils - .instance - .getBarangay( - code: - selectedMunicipality! - .code!); - selectedBarangay = - barangays![0]; - setState(() { - barangayCall = false; - }); - } - }, - decoration: - normalTextFieldStyle( - "Region*", - "Region") - .copyWith( - filled: !enabled - ? !overseas - : false, - fillColor: Colors - .grey - .shade300), - items: state.regions.map< - DropdownMenuItem< - Region>>( - (Region region) { - return DropdownMenuItem< - Region>( - value: region, - child: Text(region - .description!)); - }).toList(), - ), - const SizedBox( - height: 8, - ), - //// PROVINCE DROPDOWN - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: provinceCall, - child: FormBuilderDropdown< - Province?>( - enabled: !enabled - ? overseas - : true, - name: "province", - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - initialValue: - selectedProvince, - onChanged: (Province? - province) async { - if (selectedProvince != - province) { - selectedProvince = - province; - setState(() { - cityCall = true; - }); - - //// GET CITIES - citymuns = await LocationUtils - .instance - .getCities( - code: selectedProvince! - .code!); - selectedMunicipality = - citymuns![0]; - setState(() { - cityCall = false; - barangayCall = - true; - }); - //// GET BARANGAY - barangays = await LocationUtils - .instance - .getBarangay( - code: selectedMunicipality! - .code!); - selectedBarangay = - barangays![0]; - setState(() { - barangayCall = - false; - }); - } - }, - items: provinces == null - ? [] - : provinces! - .map>( - (Province - province) { - return DropdownMenuItem( - value: - province, - child: - FittedBox( - child: Text( - province - .description!), - )); - }).toList(), - decoration: normalTextFieldStyle( - "Province*", "Province") - .copyWith( - filled: !enabled - ? !overseas - : false, - fillColor: Colors - .grey - .shade300)), ), ), - ////CITY MUNICIPALITY - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: cityCall, - child: FormBuilderDropdown< - CityMunicipality>( - enabled: !enabled - ? overseas - : true, - name: "city", - validator: - FormBuilderValidators - .required( - errorText: - "This field is required"), - isExpanded: true, - onChanged: - (CityMunicipality? - city) async { - if (selectedMunicipality != - city) { - setState(() { - barangayCall = true; - }); - selectedMunicipality = - city; - selectedMunicipality = - city; - //// GET BARANGAYS - barangays = - await LocationUtils + selectableDayPredicate: (date) { + if (from != null && + from!.microsecondsSinceEpoch >= + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, + onChanged: (value) { + setState(() { + to = DateTime.parse(value); + }); + }, + initialDate: from == null + ? DateTime.now() + : from!.add(const Duration( + days: 1)), + ), + ), + ], + ), + ), + const SizedBox( + height: 12, + ), + //// total hours conducted + FormBuilderTextField( + initialValue: state.learningDevelopement + .conductedTraining!.totalHours + .toString(), + validator: numericRequired, + name: "total_hours", + decoration: normalTextFieldStyle( + "Total Hours Conducted *", + "0", + ).copyWith( + filled: !enabled, + fillColor: Colors.grey.shade300), + keyboardType: TextInputType.number, + ), + const SizedBox( + height: 12, + ), + ////Address + + const SizedBox( + height: 12, + ), + StatefulBuilder( + builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: normalTextFieldStyle( + "Overseas Address?", ''), + name: 'overseas', + title: + Text(overseas ? "YES" : "NO"), + ), + SizedBox( + height: overseas == true ? 8 : 0, + ), + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + FormBuilderDropdown< + Region?>( + enabled: !enabled + ? overseas + : true, + name: "region", + isExpanded: true, + initialValue: + selectedRegion, + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: + FormBuilderValidators + .required( + errorText: + "This field is required"), + onChanged: (Region? + region) async { + if (selectedRegion != + region) { + setState(() { + provinceCall = + true; + }); + selectedRegion = + region; + //// GET PROVINCES + provinces = await LocationUtils + .instance + .getProvinces( + regionCode: selectedRegion! + .code + .toString()); + selectedProvince = + provinces![0]; + setState(() { + provinceCall = + false; + cityCall = true; + }); + //// GET CITIES + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code!); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = + false; + barangayCall = + true; + }); + //// GET BARANGAY + barangays = await LocationUtils .instance .getBarangay( code: selectedMunicipality! .code!); - selectedBarangay = - barangays![0]; - setState(() { - barangayCall = - false; - }); - } - }, - decoration: normalTextFieldStyle( - "Municipality*", - "Municipality") - .copyWith( - filled: !enabled - ? !overseas - : false, - fillColor: Colors - .grey - .shade300), - initialValue: - selectedMunicipality, - items: citymuns == null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality - c) { - return DropdownMenuItem( - value: c, - child: Text(c - .description!)); - }).toList(), - ), - ), - ), - //// BARANGAY - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: barangayCall, - child: FormBuilderDropdown< - Barangay>( - enabled: !enabled - ? overseas - : true, - name: "barangay", - isExpanded: true, - onChanged: - (Barangay? baragay) { - selectedBarangay = - baragay; - }, - decoration: normalTextFieldStyle( - "Barangay*", - "Barangay") - .copyWith( - filled: !enabled - ? !overseas - : false, - fillColor: Colors - .grey - .shade300), - initialValue: - selectedBarangay, - items: barangays == null - ? [] - : barangays!.map< - DropdownMenuItem< - Barangay>>( - (Barangay - barangay) { - return DropdownMenuItem( - value: - barangay, - child: Text( - barangay - .description!)); - }).toList(), - ), - ), - ), - ], - ) - //// COUNTRY DROPDOWN - : SizedBox( - height: 60, - child: - FormBuilderDropdown( - enabled: overseas, - initialValue: selectedCountry, - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - items: state.countries.map< - DropdownMenuItem< - Country>>( - (Country country) { - return DropdownMenuItem< - Country>( - value: country, - child: FittedBox( - child: Text( - country.name!))); - }).toList(), - name: 'country', - decoration: - normalTextFieldStyle( - "Country*", - "Country") - .copyWith( - filled: !overseas, - fillColor: Colors - .grey.shade300), - onChanged: (Country? value) { - selectedCountry = value; - }, - ), - ), - ), - ], - ); - }), + selectedBarangay = + barangays![0]; + setState(() { + barangayCall = + false; + }); + ////GET CITY MUNICIPALITY + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code!); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = + false; + barangayCall = + true; + }); + //// GET BARANGAYS + barangays = await LocationUtils + .instance + .getBarangay( + code: selectedMunicipality! + .code!); + selectedBarangay = + barangays![0]; + setState(() { + barangayCall = + false; + }); + } + }, + decoration: normalTextFieldStyle( + "Region*", + "Region") + .copyWith( + filled: !enabled + ? !overseas + : false, + fillColor: Colors + .grey + .shade300), + items: state.regions.map< + DropdownMenuItem< + Region>>((Region + region) { + return DropdownMenuItem< + Region>( + value: region, + child: Text(region + .description!)); + }).toList(), + ), + const SizedBox( + height: 8, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: + ModalProgressHUD( + color: Colors + .transparent, + inAsyncCall: + provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: + true, + value: + selectedProvince, + onChanged: + (Province? + province) async { + if (selectedProvince != + province) { + selectedProvince = + province; + setState( + () { + cityCall = + true; + }); - ////Conducted By - StatefulBuilder(builder: (context, setState) { - ////has sponsor switch - return Column( - children: [ - ////Add Conducted Agency============ - SizedBox( - child: SizedBox( - child: Column(children: [ - SearchField( - enabled: enabled, - controller: - selectedConductedByController, - suggestionDirection: - SuggestionDirection.up, - itemHeight: 70, - focusNode: conductedByFocusNode, - suggestions: state.conductedBy - .map((Agency agency) => - SearchFieldListItem( - agency.name!, - item: agency, - child: ListTile( - title: Text( - agency.name!, - overflow: - TextOverflow - .ellipsis, + //// GET CITIES + citymuns = await LocationUtils + .instance + .getCities( + code: + selectedProvince!.code!); + selectedMunicipality = + citymuns![ + 0]; + setState( + () { + cityCall = + false; + barangayCall = + true; + }); + //// GET BARANGAY + barangays = await LocationUtils + .instance + .getBarangay( + code: + selectedMunicipality!.code!); + selectedBarangay = + barangays![ + 0]; + setState( + () { + barangayCall = + false; + }); + } + }, + items: + provinces == + null + ? [] + : provinces! + .map>((Province province) { + return DropdownMenuItem( + enabled: !enabled ? overseas : true, + value: province, + child: FittedBox( + child: Text(province.description!), + )); + }).toList(), + decoration: normalTextFieldStyle("Province*", "Province").copyWith(filled: !enabled ? !overseas : false, fillColor: Colors.grey.shade300)), ), - subtitle: Text(agency - .privateEntity == - true - ? "Private" - : agency.privateEntity == - false - ? "Government" - : ""), - ))) - .toList(), - searchInputDecoration: - normalTextFieldStyle( - " Conducted By *", "") - .copyWith( - suffixIcon: - GestureDetector( + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: + ModalProgressHUD( + color: Colors.white, + inAsyncCall: + cityCall, + child: DropdownButtonFormField< + CityMunicipality>( + + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + isExpanded: true, + onChanged: + (CityMunicipality? + city) async { + if (selectedMunicipality != + city) { + setState(() { + barangayCall = + true; + }); + selectedMunicipality = + city; + + //// GET BARANGAYS + barangays = await LocationUtils + .instance + .getBarangay( + code: + selectedMunicipality!.code!); + selectedBarangay = + barangays![ + 0]; + setState(() { + barangayCall = + false; + }); + } + }, + decoration: normalTextFieldStyle( + "Municipality*", + "Municipality") + .copyWith( + filled: !enabled + ? !overseas + : false, + fillColor: Colors + .grey + .shade300), + value: + selectedMunicipality, + items: citymuns == + null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality + c) { + return DropdownMenuItem( + enabled: !enabled + ? overseas + : true, + value: + c, + child: + Text(c.description!)); + }).toList(), + ), + ), + ), + //// BARANGAY + SizedBox( + height: 60, + child: + ModalProgressHUD( + color: Colors.white, + inAsyncCall: + barangayCall, + child: + DropdownButtonFormField< + Barangay>( + isExpanded: true, + onChanged: + (Barangay? + baragay) { + selectedBarangay = + baragay; + }, + decoration: normalTextFieldStyle( + "Barangay*", + "Barangay") + .copyWith( + filled: !enabled + ? !overseas + : false, + fillColor: Colors + .grey + .shade300), + value: + selectedBarangay, + items: barangays == + null + ? [] + : barangays!.map< + DropdownMenuItem< + Barangay>>((Barangay + barangay) { + return DropdownMenuItem( + enabled: !enabled + ? overseas + : true, + value: + barangay, + child: + Text(barangay.description!)); + }).toList(), + ), + ), + ), + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( + height: 60, + child: + FormBuilderDropdown< + Country>( + enabled: overseas, + initialValue: + selectedCountry + ?.id == + 175 + ? null + : selectedCountry, + validator: + FormBuilderValidators + .required( + errorText: + "This field is required"), + items: state.countries.map< + DropdownMenuItem< + Country>>( + (Country country) { + return DropdownMenuItem< + Country>( + value: country, + child: FittedBox( + child: Text( + country + .name!))); + }).toList(), + name: 'country', + decoration: normalTextFieldStyle( + "Country*", + "Country") + .copyWith( + filled: + !overseas, + fillColor: Colors + .grey + .shade300), + onChanged: + (Country? value) { + selectedCountry = + value; + }, + ), + ), + ), + ], + ); + }), + + ////Conducted By + StatefulBuilder( + builder: (context, setState) { + ////has sponsor switch + return Column( + children: [ + ////Add Conducted Agency============ + SizedBox( + child: SizedBox( + child: Column(children: [ + SearchField( + inputFormatters: [ + UpperCaseTextFormatter() + ], + enabled: enabled, + controller: + selectedConductedByController, + itemHeight: 100, + focusNode: + conductedByFocusNode, + suggestions: state + .conductedBy + .map((Agency agency) => + SearchFieldListItem( + agency.name!, + item: agency, + child: ListTile( + title: Text( + agency + .name!, + overflow: + TextOverflow + .visible, + ), + subtitle: Text(agency + .privateEntity == + true + ? "Private" + : agency.privateEntity == + false + ? "Government" + : ""), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle( + " Conducted By *", + "") + .copyWith( + suffixIcon: + GestureDetector( + child: + const Icon( + Icons + .arrow_drop_down, + ), + onTap: () => + conductedByFocusNode + .unfocus(), + ), + filled: + !enabled, + fillColor: Colors + .grey + .shade300), + ////SELETECTED + onSuggestionTap: (agency) { + setState(() { + if (agency.item?.id != + null) { + selectedConductedByAgency = + Agency( + name: null, + id: agency + .item! + .id); + } else { + selectedConductedByAgency = + Agency( + id: null, + name: agency + .item! + .name); + } + + if (agency.item! + .privateEntity == + null) { + showConductedByAgencyCategory = + true; + showConductedByAgencyPrivateRadio = + true; + } else { + showConductedByAgencyCategory = + false; + showConductedByAgencyPrivateRadio = + false; + } + conductedByFocusNode + .unfocus(); + }); + }, + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + ////conducter empty widget + emptyWidget: EmptyWidget( + controller: + addConductedByController, + onpressed: () { + setState(() { + Agency newAgency = Agency( + id: null, + name: addConductedByController + .text + .toUpperCase(), + category: null, + privateEntity: + null); + state.conductedBy + .insert(0, + newAgency); + + addConductedByController + .text = ""; + Navigator.pop( + context); + }); + }, + title: + "Add Conducted By Agency")), + SizedBox( + height: + showConductedByAgencyCategory + ? 12 + : 0, + ), + ////Conducted By Agency Category + SizedBox( + child: + showConductedByAgencyCategory + ? SearchField( + focusNode: + conductedByAgencyCategoryFocusNode, + itemHeight: 70, + suggestions: state + .agencyCategory + .map((Category + category) => + SearchFieldListItem( + category + .name!, + item: + category, + child: + ListTile( + title: + Text(category.name!), + subtitle: + Text(category.industryClass!.name!), + ))) + .toList(), + emptyWidget: + Container( + height: 100, + decoration: + box1(), + child: const Center( + child: Text( + "No result found ...")), + ), + onSuggestionTap: + (agencyCategory) { + setState(() { + selectedConductedByAgencyCategory = + agencyCategory + .item; + selectedConductedByAgency = Agency( + id: selectedConductedByAgency + ?.id, + name: selectedConductedByAgency! + .name, + category: + selectedConductedByAgencyCategory, + privateEntity: + showConductedByAgencyPrivateRadio); + conductedByAgencyCategoryFocusNode + .unfocus(); + }); + }, + searchInputDecoration: + normalTextFieldStyle( + "Category *", + "") + .copyWith( + suffixIcon: + IconButton( + icon: const Icon( + Icons + .arrow_drop_down), + onPressed: () { + conductedByAgencyCategoryFocusNode + .unfocus(); + }, + )), + validator: + (value) { + if (value! + .isEmpty) { + return "This field is required"; + } + return null; + }, + ) + : const SizedBox(), + ), + + ////Sponsor Agency Private Radio + SizedBox( + height: + showConductedByAgencyPrivateRadio + ? 12 + : 0), + SizedBox( + child: + showConductedByAgencyPrivateRadio + ? FormBuilderSwitch( + initialValue: + conductedByCategoryIsPrivate, + title: Text( + conductedByCategoryIsPrivate + ? "YES" + : "NO"), + decoration: normalTextFieldStyle( + "Private Entity?", + 'Private Entity?'), + + ////onvhange private sector + onChanged: + (value) { + setState(() { + conductedByCategoryIsPrivate = + value!; + selectedConductedByAgency = Agency( + category: + selectedConductedByAgency + ?.category, + id: selectedConductedByAgency + ?.id, + name: selectedConductedByAgency! + .name, + privateEntity: + conductedByCategoryIsPrivate); + conductedByAgencyCategoryFocusNode + .unfocus(); + }); + }, + name: + 'sponsorAgencyPrivate', + validator: + FormBuilderValidators + .required(), + ) + : const SizedBox()), + ]), + )), + ], + ); + }), + ]), + )), + const SizedBox( + height: 12, + ), + + ////Sponsor + StatefulBuilder(builder: (context, setState) { + ////has sponsor switch + return Column( + children: [ + FormBuilderSwitch( + initialValue: hasSponsor, + activeColor: second, + onChanged: (value) { + setState(() { + hasSponsor = value!; + }); + }, + decoration: normalTextFieldStyle( + "Has Sponsor?", ''), + name: 'sponsor', + title: + Text(hasSponsor ? "YES" : "NO"), + ), + ////Add Sponsor Agency============ + SizedBox( + child: hasSponsor + ? SizedBox( + child: Column(children: [ + const SizedBox( + height: 12, + ), + SearchField( + inputFormatters: [ + UpperCaseTextFormatter() + ], + controller: + selectedSponsorAgencyController, + itemHeight: 70, + focusNode: + sponsorByFocusNode, + suggestions: state + .sponsorAgencies + .map((Agency + agency) => + SearchFieldListItem( + agency + .name!, + item: + agency, + child: + ListTile( + title: + Text( + agency + .name!, + overflow: + TextOverflow.ellipsis, + ), + subtitle: Text(agency.privateEntity == + true + ? "Private" + : agency.privateEntity == false + ? "Government" + : ""), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle( + " Sponsor Agency *", + "") + .copyWith( + suffixIcon: + GestureDetector( child: const Icon( Icons .arrow_drop_down, ), onTap: () => - conductedByFocusNode + sponsorByFocusNode .unfocus(), - ), - filled: !enabled, - fillColor: Colors - .grey.shade300), - ////SELETECTED - onSuggestionTap: (agency) { - setState(() { - if (agency.item?.id != null) { - selectedConductedByAgency = - Agency( - name: null, - id: agency.item!.id); - } else { - selectedConductedByAgency = - Agency( - id: null, - name: agency - .item!.name); - } - - if (agency - .item!.privateEntity == - null) { - showConductedByAgencyCategory = - true; - showConductedByAgencyPrivateRadio = - true; - } else { - showConductedByAgencyCategory = - false; - showConductedByAgencyPrivateRadio = - false; - } - conductedByFocusNode.unfocus(); - }); - }, - validator: (agency) { - if (agency!.isEmpty) { - return "This field is required"; - } - return null; - }, - ////conducter empty widget - emptyWidget: EmptyWidget( - controller: - addConductedByController, - onpressed: () { - setState(() { - Agency newAgency = Agency( - id: null, - name: - addConductedByController - .text - .toUpperCase(), - category: null, - privateEntity: null); - state.conductedBy - .insert(0, newAgency); - - addConductedByController - .text = ""; - Navigator.pop(context); - }); - }, - title: - "Add Conducted By Agency")), - SizedBox( - height: showConductedByAgencyCategory - ? 12 - : 0, - ), - ////Sponsor Agency Category - SizedBox( - child: showConductedByAgencyCategory - ? SearchField( - suggestionDirection: - SuggestionDirection.up, - focusNode: - conductedByAgencyCategoryFocusNode, - itemHeight: 70, - suggestions: state - .agencyCategory - .map((Category - category) => - SearchFieldListItem( - category.name!, - item: category, - child: ListTile( - title: Text( - category - .name!), - subtitle: Text( - category - .industryClass! - .name!), - ))) - .toList(), - emptyWidget: Container( - height: 100, - decoration: box1(), - child: const Center( - child: Text( - "No result found ...")), - ), - onSuggestionTap: - (agencyCategory) { - setState(() { - selectedConductedByAgencyCategory = - agencyCategory.item; - selectedConductedByAgency = Agency( - id: selectedConductedByAgency - ?.id, - name: - selectedConductedByAgency! - .name, - category: - selectedConductedByAgencyCategory, - privateEntity: - showConductedByAgencyPrivateRadio); - conductedByAgencyCategoryFocusNode - .unfocus(); - }); - }, - searchInputDecoration: - normalTextFieldStyle( - "Category *", "") - .copyWith( - suffixIcon: - const Icon(Icons - .arrow_drop_down)), - validator: (value) { - if (value!.isEmpty) { - return "This field is required"; - } - return null; - }, - ) - : const SizedBox(), - ), - - ////Sponsor Agency Private Radio - SizedBox( - height: - showConductedByAgencyPrivateRadio - ? 12 - : 0), - SizedBox( - child: - showConductedByAgencyPrivateRadio - ? FormBuilderSwitch( - initialValue: - conductedByCategoryIsPrivate, - title: Text( - conductedByCategoryIsPrivate - ? "YES" - : "NO"), - decoration: - normalTextFieldStyle( - "Private Entity?", - 'Private Entity?'), - - ////onvhange private sector - onChanged: (value) { + )), + ////SELETECTED + onSuggestionTap: + (agency) { setState(() { - conductedByCategoryIsPrivate = - value!; - selectedConductedByAgency = Agency( - category: - selectedConductedByAgency - ?.category, - id: selectedConductedByAgency - ?.id, - name: - selectedConductedByAgency! - .name, - privateEntity: - conductedByCategoryIsPrivate); - conductedByAgencyCategoryFocusNode + if (agency + .item?.id != + null) { + selectedSponsorAgency = + Agency( + name: + null, + id: agency + .item! + .id); + } else { + selectedSponsorAgency = + Agency( + id: null, + name: agency + .item! + .name); + } + if (agency.item! + .privateEntity == + null) { + showSponsorCategoryAgency = + true; + showSponsorAgencyPrivateRadio = + true; + } else { + showSponsorCategoryAgency = + false; + showSponsorAgencyPrivateRadio = + false; + } + sponsorByFocusNode .unfocus(); }); }, - name: - 'sponsorAgencyPrivate', - validator: - FormBuilderValidators - .required(), - ) - : const SizedBox()), - ]), - )), - ], - ); - }), - ]), - )), - const SizedBox( - height: 12, - ), + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + ////sponsor empty widget + emptyWidget: + EmptyWidget( + controller: + addSponsorAgencyController, + onpressed: () { + setState(() { + Agency newAgency = Agency( + id: + null, + name: addSponsorAgencyController + .text + .toUpperCase(), + category: + null, + privateEntity: + null); + state + .sponsorAgencies + .insert( + 0, + newAgency); - ////Sponsor - StatefulBuilder(builder: (context, setState) { - ////has sponsor switch - return Column( - children: [ - FormBuilderSwitch( - initialValue: hasSponsor, - activeColor: second, - onChanged: (value) { - setState(() { - hasSponsor = value!; - }); - }, - decoration: - normalTextFieldStyle("Has Sponsor?", ''), - name: 'sponsor', - title: Text(hasSponsor ? "YES" : "NO"), - ), - ////Add Sponsor Agency============ - SizedBox( - child: hasSponsor - ? SizedBox( - child: Column(children: [ - const SizedBox( - height: 12, - ), - SearchField( - controller: - selectedSponsorAgencyController, - suggestionDirection: - SuggestionDirection.up, - itemHeight: 70, - focusNode: sponsorByFocusNode, - suggestions: state - .sponsorAgencies - .map((Agency agency) => - SearchFieldListItem( - agency.name!, - item: agency, - child: ListTile( - title: Text( - agency.name!, - overflow: - TextOverflow - .ellipsis, - ), - subtitle: Text(agency - .privateEntity == - true - ? "Private" - : agency.privateEntity == - false - ? "Government" - : ""), - ))) - .toList(), - searchInputDecoration: - normalTextFieldStyle( - " Sponsor Agency *", - "") - .copyWith( - suffixIcon: - GestureDetector( - child: const Icon( - Icons.arrow_drop_down, - ), - onTap: () => - sponsorByFocusNode - .unfocus(), - )), - ////SELETECTED - onSuggestionTap: (agency) { - setState(() { - if (agency.item?.id != - null) { - selectedSponsorAgency = - Agency( - name: null, - id: agency - .item!.id); - } else { - selectedSponsorAgency = - Agency( - id: null, - name: agency - .item!.name); - } - if (agency.item! - .privateEntity == - null) { - showSponsorCategoryAgency = - true; - showSponsorAgencyPrivateRadio = - true; - } else { - showSponsorCategoryAgency = - false; - showSponsorAgencyPrivateRadio = - false; - } - sponsorByFocusNode - .unfocus(); - }); - }, - validator: (agency) { - if (agency!.isEmpty) { - return "This field is required"; - } - return null; - }, - ////sponsor empty widget - emptyWidget: EmptyWidget( - controller: - addSponsorAgencyController, - onpressed: () { - setState(() { - Agency newAgency = Agency( - id: null, - name: addSponsorAgencyController - .text - .toUpperCase(), - category: null, - privateEntity: - null); - state.sponsorAgencies - .insert( - 0, newAgency); + addSponsorAgencyController + .text = ""; + Navigator.pop( + context); + }); + }, + title: + "Add Sponsor Agency")), + SizedBox( + height: + showSponsorCategoryAgency + ? 12 + : 0, + ), + ////Sponsor Agency Category + SizedBox( + child: + showSponsorCategoryAgency + ? SearchField( + suggestionDirection: + SuggestionDirection + .up, + focusNode: + sponsorAgencyCategoryFocusNode, + itemHeight: + 70, + suggestions: state + .agencyCategory + .map((Category category) => SearchFieldListItem( + category + .name!, + item: + category, + child: + ListTile( + title: + Text(category.name!), + subtitle: + Text(category.industryClass!.name!), + ))) + .toList(), + emptyWidget: + Container( + height: 100, + decoration: + box1(), + child: const Center( + child: Text( + "No result found ...")), + ), + onSuggestionTap: + (agencyCategory) { + setState( + () { + selectedSponsorAgencyCategory = + agencyCategory + .item; + selectedSponsorAgency = Agency( + id: selectedSponsorAgency + ?.id, + name: selectedSponsorAgency! + .name, + category: + selectedSponsorAgencyCategory, + privateEntity: + sponsorAgencyIsPrivate); + sponsorAgencyCategoryFocusNode + .unfocus(); + }); + }, + searchInputDecoration: normalTextFieldStyle( + "Category *", + "") + .copyWith( + suffixIcon: + IconButton( + icon: const Icon( + Icons + .arrow_drop_down), + onPressed: + () { + sponsorAgencyCategoryFocusNode + .unfocus(); + }, + )), + validator: + (value) { + if (value! + .isEmpty) { + return "This field is required"; + } + return null; + }, + ) + : const SizedBox(), + ), - addSponsorAgencyController - .text = ""; - Navigator.pop(context); - }); - }, - title: - "Add Sponsor Agency")), - SizedBox( - height: showSponsorCategoryAgency - ? 12 - : 0, - ), - ////Sponsor Agency Category - SizedBox( - child: showSponsorCategoryAgency - ? SearchField( - suggestionDirection: - SuggestionDirection - .up, - focusNode: - sponsorAgencyCategoryFocusNode, - itemHeight: 70, - suggestions: state - .agencyCategory - .map((Category - category) => - SearchFieldListItem( - category - .name!, - item: - category, - child: - ListTile( - title: Text( - category - .name!), - subtitle: Text(category - .industryClass! - .name!), - ))) - .toList(), - emptyWidget: Container( - height: 100, - decoration: box1(), - child: const Center( - child: Text( - "No result found ...")), - ), - onSuggestionTap: - (agencyCategory) { - setState(() { - selectedSponsorAgencyCategory = - agencyCategory - .item; - selectedSponsorAgency = Agency( - id: selectedSponsorAgency - ?.id, - name: - selectedSponsorAgency! - .name, - category: - selectedSponsorAgencyCategory, - privateEntity: - sponsorAgencyIsPrivate); - sponsorAgencyCategoryFocusNode - .unfocus(); - }); - }, - searchInputDecoration: - normalTextFieldStyle( - "Category *", - "") - .copyWith( - suffixIcon: - const Icon( - Icons - .arrow_drop_down)), - validator: (value) { - if (value!.isEmpty) { - return "This field is required"; - } - return null; - }, - ) - : const SizedBox(), - ), + ////Sponsor Agency Private Radio + SizedBox( + height: + showSponsorAgencyPrivateRadio + ? 12 + : 0), + SizedBox( + child: showSponsorAgencyPrivateRadio + ? FormBuilderSwitch( + initialValue: + sponsorAgencyIsPrivate, + title: Text( + sponsorAgencyIsPrivate + ? "YES" + : "NO"), + decoration: normalTextFieldStyle( + "Private Entity?", + 'Private Entity?'), - ////Sponsor Agency Private Radio - SizedBox( - height: - showSponsorAgencyPrivateRadio - ? 12 - : 0), - SizedBox( - child: - showSponsorAgencyPrivateRadio - ? FormBuilderSwitch( - initialValue: - sponsorAgencyIsPrivate, - title: Text( - sponsorAgencyIsPrivate - ? "YES" - : "NO"), - decoration: normalTextFieldStyle( - "Private Entity?", - 'Private Entity?'), + ////onvhange private sector + onChanged: + (value) { + setState(() { + sponsorAgencyIsPrivate = + value!; + selectedSponsorAgency = Agency( + category: + selectedSponsorAgency + ?.category, + id: selectedSponsorAgency + ?.id, + name: selectedSponsorAgency! + .name, + privateEntity: + selectedSponsorAgency?.privateEntity); + sponsorAgencyCategoryFocusNode + .unfocus(); + }); + }, - ////onvhange private sector - onChanged: (value) { - setState(() { - sponsorAgencyIsPrivate = - value!; - selectedSponsorAgency = Agency( - category: - selectedSponsorAgency - ?.category, - id: selectedSponsorAgency - ?.id, - name: selectedSponsorAgency! - .name, - privateEntity: - selectedSponsorAgency - ?.privateEntity); - sponsorAgencyCategoryFocusNode - .unfocus(); - }); - }, - - name: - 'sponsorAgencyPrivate', - validator: - FormBuilderValidators - .required(), - ) - : const SizedBox()), - ]), - ) - : const SizedBox(), - ), - ], - ); - }), - const SizedBox( - height: 12, - ), - const SizedBox( - height: 12, - ), - FormBuilderTextField( - initialValue: state - .learningDevelopement.totalHoursAttended - .toString(), - validator: numericRequired, - name: "total_hours_attended", - keyboardType: TextInputType.number, - decoration: normalTextFieldStyle( - "Total Hours Attended *", - "Total Hours Attended *"), - ), - ], - ); - }), - const SizedBox( - height: 12, - ), - SizedBox( - height: 60, - child: ElevatedButton( - style: - mainBtnStyle(primary, Colors.transparent, second), - child: const Text(submit), - onPressed: () { - if (formKey.currentState!.saveAndValidate()) { - ConductedTraining? training; - Agency? sponsor; - Venue venue; - ////Address - if (overseas) { - venue = Venue( - id: state.learningDevelopement - .conductedTraining!.venue!.id, - country: selectedCountry, - barangay: null, - category: null, - areaClass: null, - cityMunicipality: null); - } else { - venue = Venue( - id: state.learningDevelopement - .conductedTraining!.venue!.id, - country: Country( - id: 175, name: 'Philippines', code: 'PH'), - barangay: selectedBarangay, - areaClass: null, - category: null, - cityMunicipality: selectedMunicipality); - } - - training = ConductedTraining( - title: selectedTraining, - topic: selectedTopic, - id: state - .learningDevelopement.conductedTraining!.id, - locked: state.learningDevelopement - .conductedTraining?.locked, - venue: venue, - toDate: DateTime.parse(toDateController.text), - fromDate: - DateTime.parse(fromDateController.text), - totalHours: double.parse( - formKey.currentState!.value['total_hours']), - conductedBy: selectedConductedByAgency, - sessionsAttended: [], - learningDevelopmentType: - selectedLearningDevelopmentType); - - if (hasSponsor) { - sponsor = selectedSponsorAgency; - } - LearningDevelopement learningDevelopement = - LearningDevelopement( - attachments: null, - sponsoredBy: sponsor, - conductedTraining: training, - totalHoursAttended: double.parse(formKey - .currentState! - .value['total_hours_attended'])); - final progress = ProgressHUD.of(context); - progress!.showWithText("Loading..."); - context.read().add( - UpdateLearningDevelopment( - learningDevelopement: learningDevelopement, - profileId: widget.profileId, - token: widget.token)); - } - }, + name: + 'sponsorAgencyPrivate', + validator: + FormBuilderValidators + .required(), + ) + : const SizedBox()), + ]), + ) + : const SizedBox(), + ), + ], + ); + }), + const SizedBox( + height: 12, + ), + const SizedBox( + height: 12, + ), + FormBuilderTextField( + initialValue: state + .learningDevelopement.totalHoursAttended + .toString(), + validator: numericRequired, + name: "total_hours_attended", + keyboardType: TextInputType.number, + decoration: normalTextFieldStyle( + "Total Hours Attended *", + "Total Hours Attended *"), + ), + ], + ); + }), + ], + ), ), - ) - ], - )), - ); + SizedBox( + height: 60, + width: double.infinity, + child: ElevatedButton( + style: + mainBtnStyle(primary, Colors.transparent, second), + child: const Text(submit), + onPressed: () { + if (formKey.currentState!.saveAndValidate()) { + ConductedTraining? training; + Agency? sponsor; + Venue venue; + ////Address + if (overseas) { + venue = Venue( + id: state.learningDevelopement + .conductedTraining!.venue!.id, + country: selectedCountry, + barangay: null, + category: null, + areaClass: null, + cityMunicipality: null); + } else { + venue = Venue( + id: state.learningDevelopement + .conductedTraining!.venue!.id, + country: Country( + id: 175, + name: 'Philippines', + code: 'PH'), + barangay: selectedBarangay, + areaClass: null, + category: null, + cityMunicipality: selectedMunicipality); + } + + training = ConductedTraining( + title: selectedTraining, + topic: selectedTopic, + id: state.learningDevelopement + .conductedTraining!.id, + locked: state.learningDevelopement + .conductedTraining?.locked, + venue: venue, + toDate: DateTime.parse(toDateController.text), + fromDate: + DateTime.parse(fromDateController.text), + totalHours: double.parse(formKey + .currentState!.value['total_hours']), + conductedBy: selectedConductedByAgency, + sessionsAttended: [], + learningDevelopmentType: + selectedLearningDevelopmentType); + + if (hasSponsor) { + sponsor = selectedSponsorAgency; + } + LearningDevelopement learningDevelopement = + LearningDevelopement( + attachments: null, + sponsoredBy: sponsor, + conductedTraining: training, + totalHoursAttended: double.parse(formKey + .currentState! + .value['total_hours_attended'])); + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add( + UpdateLearningDevelopment( + learningDevelopement: + learningDevelopement, + profileId: widget.profileId, + token: widget.token)); + } + }, + ), + ) + ], + ), + ), + )); } return const Center( child: Text("ds"), diff --git a/lib/screens/profile/components/loading_screen.dart b/lib/screens/profile/components/loading_screen.dart index 2747101..fbb1b21 100644 --- a/lib/screens/profile/components/loading_screen.dart +++ b/lib/screens/profile/components/loading_screen.dart @@ -18,7 +18,6 @@ class LoadingScreen extends StatelessWidget { Widget build(BuildContext context) { return Stack( children: [ - Container( padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 12), child: ListView( @@ -133,30 +132,29 @@ class LoadingScreen extends StatelessWidget { height: screenHeight, color: Colors.white70, ), - Center( - child: Container( - height: 120, - width: 120, - decoration:const BoxDecoration( - color: Colors.black87, - borderRadius: BorderRadius.all(Radius.circular(8)) - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: const[ - - SpinKitFadingCircle( - size: 42, - color: Colors.white), - - SizedBox(height: 12,), - Text("Please wait..",style: TextStyle(color: Colors.white),), - - ], - ), - ), + Center( + child: Container( + height: 120, + width: 120, + decoration: const BoxDecoration( + color: Colors.black87, + borderRadius: BorderRadius.all(Radius.circular(8))), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: const [ + SpinKitFadingCircle(size: 42, color: Colors.white), + SizedBox( + height: 12, ), + Text( + "Please wait..", + style: TextStyle(color: Colors.white), + ), + ], + ), + ), + ), ], ); } diff --git a/lib/screens/profile/components/other_information/non_academic/add_modal.dart b/lib/screens/profile/components/other_information/non_academic/add_modal.dart index 08793bd..163b5de 100644 --- a/lib/screens/profile/components/other_information/non_academic/add_modal.dart +++ b/lib/screens/profile/components/other_information/non_academic/add_modal.dart @@ -9,6 +9,7 @@ import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/other_information/non_acedimic_recognition.dart'; import 'package:unit2/theme-data.dart/form-style.dart'; +import 'package:unit2/utils/formatters.dart'; import 'package:unit2/utils/global.dart'; import '../../../../../model/utils/agency.dart'; @@ -42,326 +43,373 @@ class _AddNonAcademicRecognitionScreenState final _formKey = GlobalKey(); int? profileId; String? token; + @override + void dispose() { + agencyFocusNode.dispose(); + agencyCategoryFocusNode.dispose(); + addAgencyController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return BlocBuilder( builder: (context, state) { if (state is UserLoggedIn) { - token = state.userData!.user!.login!.token; - profileId = state.userData!.user!.login!.user!.profileId!; + token = state.userData!.user!.login!.token; + profileId = state.userData!.user!.login!.user!.profileId!; return BlocBuilder( builder: (context, state) { if (state is ProfileLoaded) { - return BlocBuilder( builder: (context, state) { if (state is AddNonAcademeRecognitionState) { return SizedBox( - height: blockSizeVertical * 90, - child: SingleChildScrollView( - child: FormBuilder( - key: _formKey, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 25,horizontal: 18), - child: Column( - children: [ - FormBuilderTextField(name: 'title', - decoration: normalTextFieldStyle("Recognition / Award Title *", "Recognition / Award Title"), - validator: FormBuilderValidators.required(errorText: "this field is required"), - ), - const SizedBox(height: 12,), - StatefulBuilder( - builder: (context, setState) { - //// AGENCY SEARCHFIELD - return Column( - children: [ - SearchField( - itemHeight: 70, - suggestions: state.agencies - .map((Agency agency) => - SearchFieldListItem( - agency.name!, - item: agency, - child: ListTile( - title: Text( - agency.name! - .toUpperCase(), - overflow: - TextOverflow - .ellipsis, - ), - subtitle: Text(agency - .privateEntity == - true - ? "Private" - : agency.privateEntity == - false - ? "Government" - : ""), - ))) - .toList(), - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - focusNode: agencyFocusNode, - searchInputDecoration: - normalTextFieldStyle( - "Agency *", "") - .copyWith( - suffixIcon: - const Icon(Icons - .arrow_drop_down)), - ////agency suggestion tap - onSuggestionTap: (agency) { - setState(() { - selectedAgency = agency.item; - agencyFocusNode.unfocus(); - if (selectedAgency - ?.category == - null) { - showAgencyCategory = true; - showIsPrivateRadio = true; - } else { - showAgencyCategory = false; - showIsPrivateRadio = false; - } - }); - }, - emptyWidget: Container( - decoration: box1(), - height: 100, - child: Column( - mainAxisAlignment: - MainAxisAlignment - .center, - crossAxisAlignment: - CrossAxisAlignment - .center, - children: [ - const SizedBox( - height: 20, - ), - const Text( - "No result found..."), - const SizedBox( - height: 10, - ), - TextButton( - //// Add agency onpressed - onPressed: () { - showDialog( - context: - context, - builder: - (BuildContext - context) { - return AlertDialog( - title: const Text( - "Add Agency?"), - content: - SizedBox( - height: - 130, - child: - Column( - children: [ - TextFormField( - controller: - addAgencyController, - decoration: - normalTextFieldStyle("", ""), - ), - const SizedBox( - height: - 12, - ), - SizedBox( - width: double.infinity, - height: 50, - child: ElevatedButton( - style: mainBtnStyle(primary, Colors.transparent, second), - //// onpressed - onPressed: () { - setState(() { - newAgency = Agency(id: null, name: addAgencyController.text.toUpperCase(), category: null, privateEntity: null); - state.agencies.insert(0, newAgency!); - addAgencyController.clear(); - Navigator.pop(context); - }); - }, - child: const Text("Add"))), - ], - ), - ), - ); - }); - }, - child: const Text( - "Add position")) - ]), - ), - ), - const SizedBox( - height: 8, - ), - SizedBox( - child: showAgencyCategory - ? SearchField( - focusNode: - agencyCategoryFocusNode, - itemHeight: 70, - suggestions: state - .agencyCategories - .map((Category - category) => - SearchFieldListItem( - category - .name!, - item: - category, - child: - ListTile( - title: Text( - category - .name!), - subtitle: Text(category - .industryClass! - .name!), - ))) - .toList(), - emptyWidget: Container( - height: 100, - decoration: box1(), - child: const Center( - child: Text( - "No result found ...")), - ), - ////agency controller suggestion tap - onSuggestionTap: - (agencyCategory) { - setState(() { - selectedCategory = - agencyCategory - .item; - - agencyCategoryFocusNode - .unfocus(); - }); - }, - searchInputDecoration: - normalTextFieldStyle( - "Category *", - "") - .copyWith( - suffixIcon: - const Icon( - Icons - .arrow_drop_down)), - validator: - FormBuilderValidators - .required( - errorText: - "This field is required"), - ) - : const SizedBox(), - ), - - ////PRVIATE SECTOR - SizedBox( - child: showIsPrivateRadio - ? FormBuilderRadioGroup( - decoration: - InputDecoration( - border: - InputBorder.none, - label: Row( - children: [ - Text( - "Is this private sector? ", - style: Theme.of( - context) - .textTheme - .headlineSmall! - .copyWith( - fontSize: - 24), + height: blockSizeVertical * 90, + child: SingleChildScrollView( + child: FormBuilder( + key: _formKey, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 32, horizontal: 24), + child: Column( + children: [ + FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], + name: 'title', + decoration: normalTextFieldStyle( + "Recognition / Award Title *", + "Recognition / Award Title"), + validator: + FormBuilderValidators.required( + errorText: + "this field is required"), + ), + const SizedBox( + height: 12, + ), + StatefulBuilder( + builder: (context, setState) { + //// AGENCY SEARCHFIELD + return Column( + children: [ + SearchField( + inputFormatters: [ + UpperCaseTextFormatter() + ], + itemHeight: 100, + suggestions: state.agencies + .map((Agency agency) => + SearchFieldListItem( + agency.name!, + item: agency, + child: ListTile( + title: Text( + agency.name! + .toUpperCase(), + overflow: + TextOverflow + .visible, ), - const Icon(FontAwesome - .help_circled) - ], - ), + subtitle: Text(agency + .privateEntity == + true + ? "Private" + : agency.privateEntity == + false + ? "Government" + : ""), + ))) + .toList(), + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + focusNode: agencyFocusNode, + searchInputDecoration: + normalTextFieldStyle( + "Agency *", "") + .copyWith( + suffixIcon: + GestureDetector( + child: const Icon( + Icons.arrow_drop_down, + ), + onTap: () => + agencyFocusNode.unfocus(), + )), + ////agency suggestion tap + onSuggestionTap: (agency) { + setState(() { + selectedAgency = agency.item; + agencyFocusNode.unfocus(); + if (selectedAgency + ?.category == + null) { + showAgencyCategory = true; + showIsPrivateRadio = true; + } else { + showAgencyCategory = false; + showIsPrivateRadio = false; + } + }); + }, + emptyWidget: Container( + decoration: box1(), + height: 100, + child: Column( + mainAxisAlignment: + MainAxisAlignment + .center, + crossAxisAlignment: + CrossAxisAlignment + .center, + children: [ + const SizedBox( + height: 20, ), - - ////onvhange private sector - onChanged: (value) { + const Text( + "No result found..."), + const SizedBox( + height: 10, + ), + TextButton( + //// Add agency onpressed + onPressed: () { + showDialog( + context: + context, + builder: + (BuildContext + context) { + return AlertDialog( + title: const Text( + "Add Agency?"), + content: + SizedBox( + height: + 130, + child: + Column( + children: [ + TextFormField( + inputFormatters: [ + UpperCaseTextFormatter() + ], + controller: + addAgencyController, + decoration: + normalTextFieldStyle("", ""), + ), + const SizedBox( + height: + 12, + ), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + style: mainBtnStyle(primary, Colors.transparent, second), + //// onpressed + onPressed: () { + setState(() { + newAgency = Agency(id: null, name: addAgencyController.text.toUpperCase(), category: null, privateEntity: null); + state.agencies.insert(0, newAgency!); + addAgencyController.clear(); + Navigator.pop(context); + }); + }, + child: const Text("Add"))), + ], + ), + ), + ); + }); + }, + child: const Text( + "Add Agency")) + ]), + ), + ), + const SizedBox( + height: 8, + ), + SizedBox( + child: showAgencyCategory + ? SearchField( + focusNode: + agencyCategoryFocusNode, + itemHeight: 70, + suggestions: state + .agencyCategories + .map((Category + category) => + SearchFieldListItem( + category + .name!, + item: + category, + child: + ListTile( + title: Text( + category + .name!), + subtitle: Text(category + .industryClass! + .name!), + ))) + .toList(), + emptyWidget: Container( + height: 100, + decoration: box1(), + child: const Center( + child: Text( + "No result found ...")), + ), + ////agency controller suggestion tap + onSuggestionTap: + (agencyCategory) { setState(() { - if (value - .toString() == - "YES") { - isPrivate = true; - } else { - isPrivate = false; - } + selectedCategory = + agencyCategory + .item; + + agencyCategoryFocusNode + .unfocus(); }); }, - - name: 'isPrivate', + searchInputDecoration: + normalTextFieldStyle( + "Category *", + "") + .copyWith( + suffixIcon: + GestureDetector( + child: const Icon( + Icons.arrow_drop_down, + ), + onTap: () => + agencyCategoryFocusNode + .unfocus(), + )), validator: FormBuilderValidators .required( errorText: "This field is required"), - options: ["YES", "NO"] - .map((lang) => - FormBuilderFieldOption( - value: - lang)) - .toList( - growable: - false), ) - : const SizedBox()), - ], - ); - }), - const SizedBox( - height: 24, - ), - SizedBox( - height: 60, - width: double.infinity, - child: ElevatedButton( - style: mainBtnStyle(primary, - Colors.transparent, second), - onPressed: () { - - if (_formKey.currentState! - .saveAndValidate()) { - String title = _formKey.currentState!.value['title']; - - if(selectedAgency?.privateEntity != null){ - newAgency = selectedAgency; - }else{ + : const SizedBox(), + ), + + ////PRVIATE SECTOR + SizedBox( + child: showIsPrivateRadio + ? FormBuilderRadioGroup( + decoration: + InputDecoration( + border: + InputBorder.none, + label: Row( + children: [ + Text( + "Is this private sector? ", + style: Theme.of( + context) + .textTheme + .headlineSmall! + .copyWith( + fontSize: + 24), + ), + const Icon(FontAwesome + .help_circled) + ], + ), + ), + + ////onvhange private sector + onChanged: (value) { + setState(() { + if (value + .toString() == + "YES") { + isPrivate = true; + } else { + isPrivate = false; + } + }); + }, + + name: 'isPrivate', + validator: + FormBuilderValidators + .required( + errorText: + "This field is required"), + options: ["YES", "NO"] + .map((lang) => + FormBuilderFieldOption( + value: + lang)) + .toList( + growable: + false), + ) + : const SizedBox()), + ], + ); + }), + const SizedBox( + height: 24, + ), + SizedBox( + height: 60, + width: double.infinity, + child: ElevatedButton( + style: mainBtnStyle(primary, + Colors.transparent, second), + onPressed: () { + if (_formKey.currentState! + .saveAndValidate()) { + String title = _formKey + .currentState! + .value['title']; + + if (selectedAgency + ?.privateEntity != + null) { + newAgency = selectedAgency; + } else { newAgency = Agency( - id: selectedAgency?.id, - name: selectedAgency!.name, - category: - selectedCategory, - privateEntity: isPrivate); + id: selectedAgency?.id, + name: + selectedAgency!.name, + category: + selectedCategory, + privateEntity: isPrivate); + } + nonAcademicRecognition = + NonAcademicRecognition( + id: null, + title: title, + presenter: newAgency); + context + .read< + NonAcademicRecognitionBloc>() + .add(AddNonAcademeRecognition( + nonAcademicRecognition: + nonAcademicRecognition!, + profileId: profileId!, + token: token!)); } - nonAcademicRecognition = NonAcademicRecognition(id: null, title:title,presenter: newAgency ); - context.read().add(AddNonAcademeRecognition(nonAcademicRecognition: nonAcademicRecognition!, profileId: profileId!, token: token!)); - } - }, - child: const Text(submit)), - ) - ], - ), - )), - ) - ); + }, + child: const Text(submit)), + ) + ], + ), + )), + )); } return Container(); }, diff --git a/lib/screens/profile/components/other_information/non_academic/edit_modal.dart b/lib/screens/profile/components/other_information/non_academic/edit_modal.dart index 3d43d3c..91a8a9c 100644 --- a/lib/screens/profile/components/other_information/non_academic/edit_modal.dart +++ b/lib/screens/profile/components/other_information/non_academic/edit_modal.dart @@ -16,6 +16,7 @@ import '../../../../../model/utils/category.dart'; import '../../../../../theme-data.dart/box_shadow.dart'; import '../../../../../theme-data.dart/btn-style.dart'; import '../../../../../theme-data.dart/colors.dart'; +import '../../../../../utils/formatters.dart'; import '../../../../../utils/text_container.dart'; class EditNonAcademicRecognitionScreen extends StatefulWidget { @@ -43,6 +44,15 @@ class _EditNonAcademicRecognitionScreenState final _formKey = GlobalKey(); int? profileId; String? token; + + @override + void dispose() { + agencyFocusNode.dispose(); + agencyCategoryFocusNode.dispose(); + addAgencyController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return BlocBuilder( @@ -93,8 +103,11 @@ class _EditNonAcademicRecognitionScreenState return Column( children: [ SearchField( + inputFormatters: [ + UpperCaseTextFormatter() + ], controller: oldAgencyController, - itemHeight: 70, + itemHeight: 100, suggestions: state.agencies .map((Agency agency) => SearchFieldListItem( @@ -106,7 +119,7 @@ class _EditNonAcademicRecognitionScreenState .toUpperCase(), overflow: TextOverflow - .ellipsis, + .visible, ), subtitle: Text(agency .privateEntity == @@ -129,8 +142,14 @@ class _EditNonAcademicRecognitionScreenState "Agency *", "") .copyWith( suffixIcon: - const Icon(Icons - .arrow_drop_down)), + GestureDetector( + child: const Icon( + Icons.arrow_drop_down, + ), + onTap: () => + agencyCategoryFocusNode + .unfocus(), + )), ////agency suggestion tap onSuggestionTap: (agency) { setState(() { @@ -274,9 +293,14 @@ class _EditNonAcademicRecognitionScreenState "") .copyWith( suffixIcon: - const Icon( - Icons - .arrow_drop_down)), + GestureDetector( + child: const Icon( + Icons.arrow_drop_down, + ), + onTap: () => + agencyCategoryFocusNode + .unfocus(), + )), validator: FormBuilderValidators .required( @@ -375,7 +399,9 @@ class _EditNonAcademicRecognitionScreenState } nonAcademicRecognition = NonAcademicRecognition( - id: state.nonAcademicRecognition.id, + id: state + .nonAcademicRecognition + .id, title: title, presenter: newAgency); context diff --git a/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart b/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart index 56bac7f..11a7ab9 100644 --- a/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart +++ b/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart @@ -13,9 +13,11 @@ import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; +import 'package:unit2/widgets/error_state.dart'; import '../../../../bloc/profile/other_information/non_academic_recognition.dart/non_academic_recognition_bloc.dart'; import '../../../../utils/alerts.dart'; +import '../../../../widgets/Leadings/close_leading.dart'; class NonAcademicRecognitionScreen extends StatelessWidget { const NonAcademicRecognitionScreen({ @@ -28,17 +30,38 @@ class NonAcademicRecognitionScreen extends StatelessWidget { String? token; return Scaffold( appBar: AppBar( - title: const Text(nonAcademicRecTitle), - centerTitle: true, - backgroundColor: primary, - actions: [ - AddLeading(onPressed: () { - context - .read() - .add(ShowAddNonAcademeRecognitionForm()); - }) - ], - ), + title: context.watch().state + is AddNonAcademeRecognitionState + ? const FittedBox(child: Text("Add $nonAcademicRecTitle")) + : context.watch().state + is EditNonAcademeRecognitionState + ? const FittedBox( + child: Text("Edit $nonAcademicRecTitle"), + ) + : const FittedBox(child: Text(nonAcademicRecTitle)), + centerTitle: true, + backgroundColor: primary, + actions: (context.watch().state + is NonAcademicRecognitionLoadedState) + ? [ + AddLeading(onPressed: () { + context + .read() + .add(ShowAddNonAcademeRecognitionForm()); + }) + ] + : (context.watch().state + is AddNonAcademeRecognitionState || + context.watch().state + is EditNonAcademeRecognitionState) + ? [ + CloseLeading(onPressed: () { + context + .read() + .add(const LoadNonAcademeRecognition()); + }) + ] + : []), body: ProgressHUD( padding: const EdgeInsets.all(24), backgroundColor: Colors.black87, @@ -60,7 +83,9 @@ class NonAcademicRecognitionScreen extends StatelessWidget { } if (state is NonAcademicRecognitionLoadedState || state is NonAcademicRecognitionErrorState || - state is AddNonAcademeRecognitionState || state is EditNonAcademeRecognitionState || state is NonAcademeRecognitionEditedState) { + state is AddNonAcademeRecognitionState || + state is EditNonAcademeRecognitionState || + state is NonAcademeRecognitionEditedState) { final progress = ProgressHUD.of(context); progress!.dismiss(); } @@ -89,39 +114,39 @@ class NonAcademicRecognitionScreen extends StatelessWidget { if (state is NonAcademeRecognitionDeletedState) { if (state.success) { successAlert(context, "Deletion Successfull", - "Work has been deleted successfully", () { + "Non Academic Recognition has been deleted successfully", () { Navigator.of(context).pop(); context .read() - .add(const GetNonAcademicRecognition()); + .add(const LoadNonAcademeRecognition()); }); } else { errorAlert(context, "Deletion Failed", - "Error deleting Work History", () { + "Error deleting Non Academic Recognition", () { Navigator.of(context).pop(); context .read() - .add(const GetNonAcademicRecognition()); + .add(const LoadNonAcademeRecognition()); }); } } ////EDITED STATE - if (state is NonAcademeRecognitionEditedState) { + if (state is NonAcademeRecognitionEditedState) { if (state.response['success']) { successAlert(context, "Update Successfull", state.response['message'], () { Navigator.of(context).pop(); context .read() - .add( LoadNonAcademeRecognition(nonAcademicRecognitions: state.nonAcademicRecognitions)); + .add(const LoadNonAcademeRecognition()); }); } else { errorAlert(context, "Update Failed", - state.response['message'], () { + state.response['message'], () { Navigator.of(context).pop(); context .read() - .add(LoadNonAcademeRecognition(nonAcademicRecognitions: state.nonAcademicRecognitions)); + .add(const LoadNonAcademeRecognition()); }); } } @@ -165,10 +190,12 @@ class NonAcademicRecognitionScreen extends StatelessWidget { .titleMedium! .copyWith( fontWeight: - FontWeight - .w500), + FontWeight.w500, + color: primary), + ), + const SizedBox( + height: 8, ), - const Divider(), Text(presenter), ], )), @@ -176,7 +203,6 @@ class NonAcademicRecognitionScreen extends StatelessWidget { offset: const Offset(-10, -10), elevation: 3, onSelected: (value) { - ////delete non academic recognition-= = = = = = = = =>> if (value == 1) { confirmAlert(context, () { @@ -209,12 +235,12 @@ class NonAcademicRecognitionScreen extends StatelessWidget { }, menuItems: [ popMenuItem( - text: "Delete", - value: 1, - icon: Icons.delete), - popMenuItem( - text: "Edit", + text: "Update", value: 2, + icon: Icons.edit), + popMenuItem( + text: "Remove", + value: 1, icon: Icons.delete), ], icon: const Icon( @@ -233,14 +259,25 @@ class NonAcademicRecognitionScreen extends StatelessWidget { ); }); } else { - const EmptyData( + return const EmptyData( message: "You don't have any Non Academic Recognition added. Please click + to add"); } } + if (state is NonAcademicRecognitionErrorState) { + return SomethingWentWrong( + message: state.message, + onpressed: () { + context + .read() + .add(GetNonAcademicRecognition( + profileId: profileId, token: token)); + }); + } if (state is AddNonAcademeRecognitionState) { return const AddNonAcademicRecognitionScreen(); - }if(state is EditNonAcademeRecognitionState){ + } + if (state is EditNonAcademeRecognitionState) { return const EditNonAcademicRecognitionScreen(); } return Container(); diff --git a/lib/screens/profile/components/other_information/org_membership/add_modal.dart b/lib/screens/profile/components/other_information/org_membership/add_modal.dart index b2e60df..a8cfd0d 100644 --- a/lib/screens/profile/components/other_information/org_membership/add_modal.dart +++ b/lib/screens/profile/components/other_information/org_membership/add_modal.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:fluttericon/font_awesome_icons.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:searchfield/searchfield.dart'; @@ -12,6 +13,7 @@ import '../../../../../theme-data.dart/box_shadow.dart'; import '../../../../../theme-data.dart/btn-style.dart'; import '../../../../../theme-data.dart/colors.dart'; import '../../../../../theme-data.dart/form-style.dart'; +import '../../../../../utils/formatters.dart'; class AddOrgMemberShipScreen extends StatefulWidget { final int profileId; @@ -34,287 +36,312 @@ Agency? newAgency; bool showIsPrivateRadio = false; bool? isPrivate = false; final _formKey = GlobalKey(); + class _AddOrgMemberShipScreenState extends State { + @override + void dispose() { + super.dispose(); + } + @override Widget build(BuildContext context) { return BlocBuilder( builder: (context, state) { if (state is AddOrgMembershipState) { - return SingleChildScrollView( - child: FormBuilder( - key: _formKey, - child: Padding( - padding: - const EdgeInsets.symmetric(vertical: 25, horizontal: 18), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const SizedBox( - height: 100, - ), - StatefulBuilder(builder: (context, setState) { - //// AGENCY SEARCHFIELD - return Column( - children: [ - SearchField( - itemHeight: 70, - suggestions: state.agencies - .map((Agency agency) => - SearchFieldListItem(agency.name!, - item: agency, - child: ListTile( - title: Text( - agency.name!.toUpperCase(), - overflow: TextOverflow.ellipsis, - ), - subtitle: Text( - agency.privateEntity == true - ? "Private" - : agency.privateEntity == - false - ? "Government" - : ""), - ))) - .toList(), - validator: FormBuilderValidators.required( - errorText: "This field is required"), - focusNode: agencyFocusNode, - searchInputDecoration: normalTextFieldStyle( - "Agency *", "") - .copyWith( - suffixIcon: - const Icon(Icons.arrow_drop_down)), - ////agency suggestion tap - onSuggestionTap: (agency) { - setState(() { - selectedAgency = agency.item; - agencyFocusNode.unfocus(); - if (selectedAgency?.category == null) { - showAgencyCategory = true; - showIsPrivateRadio = true; - } else { - showAgencyCategory = false; - showIsPrivateRadio = false; - } - }); - }, - emptyWidget: Container( - decoration: box1(), - height: 100, - child: Column( - mainAxisAlignment: - MainAxisAlignment.center, - crossAxisAlignment: - CrossAxisAlignment.center, - children: [ - const SizedBox( - height: 20, - ), - const Text("No result found..."), - const SizedBox( - height: 10, - ), - TextButton( - //// Add agency onpressed - onPressed: () { - showDialog( - context: context, - builder: - (BuildContext context) { - return AlertDialog( - title: const Text( - "Add Agency?"), - content: SizedBox( - height: 130, - child: Column( - children: [ - TextFormField( - controller: - addAgencyController, - decoration: - normalTextFieldStyle( - "", ""), - ), - const SizedBox( - height: 12, - ), - SizedBox( - width: double - .infinity, - height: 50, - child: - ElevatedButton( - style: mainBtnStyle( - primary, - Colors - .transparent, - second), - //// onpressed - onPressed: - () { - setState( - () { - newAgency = Agency( - id: null, - name: addAgencyController.text.toUpperCase(), - category: null, - privateEntity: null); - state.agencies.insert(0, - newAgency!); - addAgencyController.clear(); - Navigator.pop(context); - }); - }, - child: const Text( - "Add"))), - ], - ), - ), - ); - }); - }, - child: const Text("Add position")) - ]), - ), - ), - const SizedBox( - height: 8, - ), - SizedBox( - child: showAgencyCategory - ? SearchField( - focusNode: agencyCategoryFocusNode, - itemHeight: 70, - suggestions: state.agencyCategories - .map((Category category) => - SearchFieldListItem( - category.name!, - item: category, - child: ListTile( - title: - Text(category.name!), - subtitle: Text(category - .industryClass! - .name!), - ))) - .toList(), - emptyWidget: Container( - height: 100, - decoration: box1(), - child: const Center( - child: - Text("No result found ...")), - ), - ////agency controller suggestion tap - onSuggestionTap: (agencyCategory) { - setState(() { - selectedCategory = - agencyCategory.item; - - agencyCategoryFocusNode.unfocus(); - }); - }, - searchInputDecoration: - normalTextFieldStyle( - "Category *", "") - .copyWith( - suffixIcon: const Icon( - Icons.arrow_drop_down)), - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - ) - : const SizedBox(), - ), - - ////PRVIATE SECTOR - SizedBox( - child: showIsPrivateRadio - ? FormBuilderRadioGroup( - decoration: InputDecoration( - border: InputBorder.none, - label: Row( - children: [ - Text( - "Is this private sector? ", - style: Theme.of(context) - .textTheme - .headlineSmall! - .copyWith(fontSize: 24), - ), - const Icon( - FontAwesome.help_circled) - ], + return Padding( + padding: const EdgeInsets.all(24), + child: Card( + + child: SingleChildScrollView( + child: FormBuilder( + key: _formKey, + child: Container( + padding: + const EdgeInsets.symmetric(vertical:24, horizontal: 24), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + + StatefulBuilder(builder: (context, setState) { + //// AGENCY SEARCHFIELD + return Column( + children: [ + SearchField( + inputFormatters: [UpperCaseTextFormatter()], + + itemHeight: 100, + suggestions: state.agencies + .map((Agency agency) => + SearchFieldListItem(agency.name!, + item: agency, + child: ListTile( + title: Text( + agency.name!.toUpperCase(), + overflow: TextOverflow.visible, + ), + subtitle: Text( + agency.privateEntity == true + ? "Private" + : agency.privateEntity == + false + ? "Government" + : ""), + ))) + .toList(), + validator: FormBuilderValidators.required( + errorText: "This field is required"), + focusNode: agencyFocusNode, + searchInputDecoration: + normalTextFieldStyle("Agency *", "") + .copyWith( + suffixIcon: GestureDetector( + child: const Icon( + Icons.arrow_drop_down, + ), + onTap: () => agencyFocusNode.unfocus(), + )), + ////agency suggestion tap + onSuggestionTap: (agency) { + setState(() { + selectedAgency = agency.item; + agencyFocusNode.unfocus(); + if (selectedAgency?.category == null) { + showAgencyCategory = true; + showIsPrivateRadio = true; + } else { + showAgencyCategory = false; + showIsPrivateRadio = false; + } + }); + }, + emptyWidget: Container( + decoration: box1(), + height: 100, + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.center, + children: [ + const SizedBox( + height: 20, ), - ), - - ////onvhange private sector - onChanged: (value) { - setState(() { - if (value.toString() == "YES") { - isPrivate = true; - } else { - isPrivate = false; - } - }); - }, - - name: 'isPrivate', - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - options: ["YES", "NO"] - .map((lang) => - FormBuilderFieldOption( - value: lang)) - .toList(growable: false), - ) - : const SizedBox()), - ], - ); - }), - ////SHOW CATEGORY AGENCY - - const SizedBox( - height: 24, - ), - SizedBox( - height: 60, - width: double.infinity, - child: ElevatedButton( - style: mainBtnStyle( - primary, Colors.transparent, second), - onPressed: () { - if (_formKey.currentState!.saveAndValidate()) { - if (selectedAgency?.privateEntity != null) { - newAgency = selectedAgency; - } else { - newAgency = Agency( - id: selectedAgency?.id, - name: selectedAgency!.name, - category: selectedCategory, - privateEntity: isPrivate); - } - - context - .read() - .add(AddOrgMembership( - agency: newAgency!, - profileId: widget.profileId, - token: widget.token)); - setState(() { - showAgencyCategory = false; - showIsPrivateRadio = false; - }); - } - }, - child: const Text(submit)), - ) - ]), - )), + const Text("No result found..."), + const SizedBox( + height: 10, + ), + TextButton( + //// Add agency onpressed + onPressed: () { + showDialog( + context: context, + builder: + (BuildContext context) { + return AlertDialog( + title: const Text( + "Add Agency?"), + content: SizedBox( + height: 130, + child: Column( + children: [ + TextFormField( + inputFormatters: [ + UpperCaseTextFormatter() + ], + controller: + addAgencyController, + decoration: + normalTextFieldStyle( + "", ""), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: double + .infinity, + height: 50, + child: + ElevatedButton( + style: mainBtnStyle( + primary, + Colors + .transparent, + second), + //// onpressed + onPressed: + () { + setState( + () { + newAgency = Agency( + id: null, + name: addAgencyController.text.toUpperCase(), + category: null, + privateEntity: null); + state.agencies.insert(0, + newAgency!); + addAgencyController.clear(); + Navigator.pop(context); + }); + }, + child: const Text( + "Add"))), + ], + ), + ), + ); + }); + }, + child: const Text("Add position")) + ]), + ), + ), + const SizedBox( + height: 8, + ), + SizedBox( + child: showAgencyCategory + ? SearchField( + focusNode: agencyCategoryFocusNode, + itemHeight: 70, + suggestions: state.agencyCategories + .map((Category category) => + SearchFieldListItem( + category.name!, + item: category, + child: ListTile( + title: + Text(category.name!), + subtitle: Text(category + .industryClass! + .name!), + ))) + .toList(), + emptyWidget: Container( + height: 100, + decoration: box1(), + child: const Center( + child: + Text("No result found ...")), + ), + ////agency controller suggestion tap + onSuggestionTap: (agencyCategory) { + setState(() { + selectedCategory = + agencyCategory.item; + + agencyCategoryFocusNode.unfocus(); + }); + }, + searchInputDecoration: + normalTextFieldStyle( + "Category *", "") + .copyWith( + suffixIcon: GestureDetector( + child: const Icon( + Icons.arrow_drop_down, + ), + onTap: () => + agencyCategoryFocusNode.unfocus(), + )), + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + ) + : const SizedBox(), + ), + + ////PRVIATE SECTOR + SizedBox( + child: showIsPrivateRadio + ? FormBuilderRadioGroup( + decoration: InputDecoration( + border: InputBorder.none, + label: Row( + children: [ + Text( + "Is this private sector? ", + style: Theme.of(context) + .textTheme + .headlineSmall! + .copyWith(fontSize: 24), + ), + const Icon( + FontAwesome.help_circled) + ], + ), + ), + + ////onvhange private sector + onChanged: (value) { + setState(() { + if (value.toString() == "YES") { + isPrivate = true; + } else { + isPrivate = false; + } + }); + }, + + name: 'isPrivate', + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + options: ["YES", "NO"] + .map((lang) => + FormBuilderFieldOption( + value: lang)) + .toList(growable: false), + ) + : const SizedBox()), + ], + ); + }), + ////SHOW CATEGORY AGENCY + + const SizedBox( + height: 24, + ), + SizedBox( + height: 60, + width: double.infinity, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + if (_formKey.currentState!.saveAndValidate()) { + if (selectedAgency?.privateEntity != null) { + newAgency = selectedAgency; + } else { + newAgency = Agency( + id: selectedAgency?.id, + name: selectedAgency!.name, + category: selectedCategory, + privateEntity: isPrivate); + } + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context + .read() + .add(AddOrgMembership( + agency: newAgency!, + profileId: widget.profileId, + token: widget.token)); + setState(() { + showAgencyCategory = false; + showIsPrivateRadio = false; + }); + } + }, + child: const Text(submit)), + ) + ]), + )), + ), + ), ); } return Container(); diff --git a/lib/screens/profile/components/other_information/org_membership_screen.dart b/lib/screens/profile/components/other_information/org_membership_screen.dart index 68cdc20..133f615 100644 --- a/lib/screens/profile/components/other_information/org_membership_screen.dart +++ b/lib/screens/profile/components/other_information/org_membership_screen.dart @@ -28,19 +28,31 @@ class OrgMembershipsScreen extends StatelessWidget { int profileId; return Scaffold( appBar: AppBar( - title: const Text(orgMembershipTitle), - backgroundColor: primary, - centerTitle: true, - actions: context.watch().state is OrganizationMembershipLoaded?[ - AddLeading(onPressed: () { - context - .read() - .add(ShowAddOrgMembershipForm()); - }) - ]: context.watch().state is AddOrgMembershipState ?[CloseLeading(onPressed: (){ - context.read().add(const GetOrganizationMembership()); - })]:[] - ), + title: context.watch().state + is AddOrgMembershipState + ? const FittedBox(child: Text("Add $orgMembershipTitle")) + : const FittedBox(child: Text(" $orgMembershipTitle")), + backgroundColor: primary, + centerTitle: true, + actions: context.watch().state + is OrganizationMembershipLoaded + ? [ + AddLeading(onPressed: () { + context + .read() + .add(ShowAddOrgMembershipForm()); + }) + ] + : context.watch().state + is AddOrgMembershipState + ? [ + CloseLeading(onPressed: () { + context + .read() + .add(const GetOrganizationMembership()); + }) + ] + : []), body: ProgressHUD( padding: const EdgeInsets.all(24), backgroundColor: Colors.black87, @@ -62,126 +74,136 @@ class OrgMembershipsScreen extends StatelessWidget { } if (state is OrganizationMembershipLoaded || state is OrganizationMembershipErrorState || - state is AddOrgMembershipState || state is OrgMembershipDeletedState) { + state is AddOrgMembershipState || + state is OrgMembershipDeletedState) { final progress = ProgressHUD.of(context); progress!.dismiss(); } - + ////ADDED STATE if (state is OrgMembershipAddedState) { if (state.response['success']) { successAlert(context, "Adding Successfull!", state.response['message'], () { Navigator.of(context).pop(); - context.read().add( - LoadOrganizationMemberships( - )); + context + .read() + .add(LoadOrganizationMemberships()); }); } else { errorAlert(context, "Adding Failed", "Something went wrong. Please try again.", () { Navigator.of(context).pop(); - context.read().add( - LoadOrganizationMemberships( - )); + context + .read() + .add(LoadOrganizationMemberships()); }); } } - ////DELETED STATE + ////DELETED STATE if (state is OrgMembershipDeletedState) { if (state.success) { successAlert(context, "Deletion Successfull", - "Work has been deleted successfully", () { + "Organization Membership has been deleted successfully", () { Navigator.of(context).pop(); - context.read().add( - LoadOrganizationMemberships( - )); + context + .read() + .add(LoadOrganizationMemberships()); }); } else { errorAlert(context, "Deletion Failed", - "Error deleting Work History", () { + "Error deleting Organization Membership", () { Navigator.of(context).pop(); - context.read().add( - LoadOrganizationMemberships( - )); + context + .read() + .add(LoadOrganizationMemberships()); }); } } - }, builder: (context, state) { if (state is OrganizationMembershipLoaded) { - return ListView.builder( - itemCount: state.orgMemberships.length, - padding: const EdgeInsets.symmetric( - vertical: 8, horizontal: 10), - itemBuilder: (BuildContext context, int index) { - String entity = state.orgMemberships[index] - .agency!.privateEntity == - false - ? governmentText.toUpperCase() - : privateText.toUpperCase(); - String agencyName = - state.orgMemberships[index].agency!.name!; - return Column( - children: [ - Container( - width: screenWidth, - decoration: box1(), - padding: const EdgeInsets.symmetric( - horizontal: 12, vertical: 8), - child: Row(children: [ - Expanded( - child: Column( - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - agencyName, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith( - fontWeight: - FontWeight.w500), - ), - const Divider(), - Text( - entity, - style: Theme.of(context) - .textTheme - .labelLarge, - ), - ], - )), - AppPopupMenu( + if (state.orgMemberships.isNotEmpty) { + return ListView.builder( + itemCount: state.orgMemberships.length, + padding: const EdgeInsets.symmetric( + vertical: 8, horizontal: 10), + itemBuilder: + (BuildContext context, int index) { + String entity = state.orgMemberships[index] + .agency!.privateEntity == + false + ? governmentText.toUpperCase() + : privateText.toUpperCase(); + String agencyName = state + .orgMemberships[index].agency!.name!; + return Column( + children: [ + Container( + width: screenWidth, + decoration: box1(), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + child: Row(children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + agencyName, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight.w500, + color: primary), + ), + const SizedBox( + height: 8, + ), + Text( + entity, + style: Theme.of(context) + .textTheme + .labelMedium, + ), + ], + )), + AppPopupMenu( offset: const Offset(-10, -10), elevation: 3, onSelected: (value) { - ////delete orgmembership-= = = = = = = = =>> if (value == 1) { confirmAlert(context, () { - final progress = - ProgressHUD.of(context); - progress! - .showWithText("Loading..."); - context.read().add(DeleteOrgMemberShip(profileId: profileId, token: token!, org: state.orgMemberships[index])); + final progress = + ProgressHUD.of(context); + progress!.showWithText( + "Loading..."); + context + .read< + OrganizationMembershipBloc>() + .add(DeleteOrgMemberShip( + profileId: + profileId, + token: token!, + org: state + .orgMemberships[ + index])); }, "Delete?", "Confirm Delete?"); } - }, menuItems: [ - popMenuItem( text: "Delete", value: 1, icon: Icons.delete), - ], icon: const Icon( Icons.more_vert, @@ -189,23 +211,36 @@ class OrgMembershipsScreen extends StatelessWidget { ), tooltip: "Options", ) - ]), - ), - const SizedBox( - height: 5, - ), - ], - ); - }); + ]), + ), + const SizedBox( + height: 5, + ), + ], + ); + }); + } else { + return const EmptyData( + message: + "You don't have any Orgazational Membership added. Please click + to add"); + } } if (state is AddOrgMembershipState) { - return AlertDialog( - content: AddOrgMemberShipScreen(profileId: profileId,token: token!,) - ); - }if(state is OrganizationMembershipErrorState){ - return SomethingWentWrong(message: state.message, onpressed: (){ - context.read().add(GetOrganizationMembership(token: token,profileId: profileId)); - }); + return + AddOrgMemberShipScreen( + profileId: profileId, + token: token!, + ); + } + if (state is OrganizationMembershipErrorState) { + return SomethingWentWrong( + message: state.message, + onpressed: () { + context + .read() + .add(GetOrganizationMembership( + token: token, profileId: profileId)); + }); } return Container(); }, @@ -221,6 +256,7 @@ class OrgMembershipsScreen extends StatelessWidget { )); } } + PopupMenuItem popMenuItem({String? text, int? value, IconData? icon}) { return PopupMenuItem( value: value, diff --git a/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart b/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart index 53a28a7..aaccdbd 100644 --- a/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart +++ b/lib/screens/profile/components/other_information/skills_and_hobbies_screen.dart @@ -11,6 +11,7 @@ import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; +import 'package:unit2/widgets/error_state.dart'; import '../../../../bloc/profile/other_information/hobbies/hoobies_bloc.dart'; import '../../../../theme-data.dart/btn-style.dart'; @@ -35,7 +36,7 @@ class _SkillHobbiesScreenState extends State { final FocusNode focusNode = FocusNode(); return Scaffold( appBar: AppBar( - title: const Text(skillAndHobbiesTitle), + title:context.watch().state is AddHobbySkillState? const Text("Add $skillAndHobbiesTitle"):const Text(skillAndHobbiesTitle), backgroundColor: primary, centerTitle: true, actions: context.watch().state is AddHobbySkillState @@ -93,11 +94,10 @@ class _SkillHobbiesScreenState extends State { ElevatedButton( onPressed: () { keySimpleChipsInput.currentState!.save(); - Navigator.of(context, rootNavigator: true).pop('dialog'); - bloc.add( - GetAddedHobbiesSkills( - addedHobbiesSkills: output)); - + Navigator.of(context, rootNavigator: true) + .pop('dialog'); + bloc.add(GetAddedHobbiesSkills( + addedHobbiesSkills: output)); }, style: mainBtnStyle( primary, Colors.transparent, second), @@ -110,9 +110,7 @@ class _SkillHobbiesScreenState extends State { ] : [ AddLeading(onPressed: () { - bloc.add(const ShowHobbySkillAddForm( - )); - + bloc.add(const ShowHobbySkillAddForm()); }) ]), body: ProgressHUD( @@ -188,7 +186,6 @@ class _SkillHobbiesScreenState extends State { }, builder: (context, state) { if (state is HobbiesLoadedState) { - if (state.skillsAndHobbies.isNotEmpty) { return Padding( padding: const EdgeInsets.all(12), @@ -252,7 +249,7 @@ class _SkillHobbiesScreenState extends State { }).toList()), ); } else { - const EmptyData( + return const EmptyData( message: "You don't have any Skills and Hobbies added. Please click + to add"); } @@ -263,6 +260,15 @@ class _SkillHobbiesScreenState extends State { token: token, ); } + if (state is HobbiesErrorState) { + return SomethingWentWrong( + message: state.message, + onpressed: () { + context.read().add( + GetSkillsHobbies( + profileId: profileId, token: token)); + }); + } // if (state is ShowAddModalState) { // return AddModal(bloc: bloc); // } @@ -280,81 +286,3 @@ class _SkillHobbiesScreenState extends State { )); } } - -// class AddModal extends StatefulWidget { -// final HoobiesBloc bloc; -// const AddModal({super.key, required this.bloc}); - -// @override -// State createState() => _AddModalState(); -// } - - - -// class _AddModalState extends State { -// @override -// Widget build(BuildContext context) { -// return BlocBuilder( -// builder: (context, state) { -// return AlertDialog( -// title: const Text("Add Skills and Hobbies"), -// content: SimpleChipsInput( -// separatorCharacter: ",", -// createCharacter: ",", -// focusNode: focusNode, -// validateInput: true, -// autoFocus: true, -// formKey: keySimpleChipsInput, -// onSubmitted: (p0) { -// setState(() { -// output = p0; -// }); -// }, -// onChipDeleted: (p0, p1) { -// setState(() { -// deletedChip = p0; -// deletedChipIndex = p1.toString(); -// }); -// }, -// onSaved: ((p0) { -// setState(() { -// output = p0; -// }); -// }), -// chipTextStyle: const TextStyle( -// color: Colors.white, -// fontSize: 16, -// ), -// deleteIcon: const Icon( -// Icons.delete, -// size: 14.0, -// color: second, -// ), -// widgetContainerDecoration: BoxDecoration( -// color: Colors.white, -// borderRadius: BorderRadius.circular(16.0), -// border: Border.all(color: Colors.blue[100]!), -// ), -// chipContainerDecoration: BoxDecoration( -// color: second, -// borderRadius: BorderRadius.circular(50), -// ), -// placeChipsSectionAbove: false, -// ), -// actions: [ -// ElevatedButton( -// onPressed: () { -// keySimpleChipsInput.currentState!.save(); -// context -// .read() -// .add(GetAddedHobbiesSkills(addedHobbiesSkills: output)); -// }, -// style: mainBtnStyle(primary, Colors.transparent, second), -// child: const Text(submit), -// ) -// ], -// ); -// }, -// ); -// } -// } diff --git a/lib/screens/profile/components/other_information/skills_hobbies/add_modal.dart b/lib/screens/profile/components/other_information/skills_hobbies/add_modal.dart index 8633278..a296125 100644 --- a/lib/screens/profile/components/other_information/skills_hobbies/add_modal.dart +++ b/lib/screens/profile/components/other_information/skills_hobbies/add_modal.dart @@ -1,9 +1,6 @@ -import 'dart:math'; import 'package:filter_list/filter_list.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:unit2/bloc/profile/other_information/hobbies/hoobies_bloc.dart'; import 'package:unit2/model/profile/other_information/skills_and_hobbies.dart'; diff --git a/lib/screens/profile/components/reference/add_modal.dart b/lib/screens/profile/components/reference/add_modal.dart index 6195f9c..5883898 100644 --- a/lib/screens/profile/components/reference/add_modal.dart +++ b/lib/screens/profile/components/reference/add_modal.dart @@ -16,6 +16,7 @@ import '../../../../model/location/country.dart'; import '../../../../model/location/provinces.dart'; import '../../../../model/location/region.dart'; import '../../../../theme-data.dart/colors.dart'; +import '../../../../utils/formatters.dart'; import '../../../../utils/location_utilities.dart'; import '../../../../utils/text_container.dart'; @@ -46,378 +47,401 @@ class _AddReferenceScreenState extends State { Barangay? selectedBarangay; Country? selectedCountry; AddressCategory? selectedCategory; + @override Widget build(BuildContext context) { return BlocBuilder( builder: (context, state) { if (state is AddReferenceState) { return SingleChildScrollView( - child: SizedBox( - height: screenHeight * .95, - child: ProgressHUD( - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 18), - child: FormBuilder( - key: formKey, - child: Column( - children: [ - const SizedBox(height: 25), - Row( + child: FormBuilder( + key: formKey, + child: SizedBox( + height: screenHeight * .90, + child: Padding( + padding: const EdgeInsets.all(28), + child: Column( + + children: [ + const SizedBox(height: 15,), + Flexible( + child: ListView( children: [ - ////LAST NAME - Flexible( - flex: 1, - child: FormBuilderTextField( - decoration: normalTextFieldStyle( - "Last name *", "Last name *"), - name: "lastname", - validator: FormBuilderValidators.required( - errorText: "This field is required"), - ), + + Row( + children: [ + ////LAST NAME + Flexible( + flex: 1, + child: FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], + decoration: normalTextFieldStyle( + "Last name *", "Last name *"), + name: "lastname", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + ), + ), + const SizedBox( + width: 8, + ), + ////FIRST NAME + Flexible( + flex: 1, + child: FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], + decoration: normalTextFieldStyle( + "First name *", "First name *"), + name: "firstname", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + ), + ), + ], ), const SizedBox( - width: 5, + height: 12, ), - ////FIRST NAME - Flexible( - flex: 1, - child: FormBuilderTextField( - decoration: normalTextFieldStyle( - "First name *", "First name *"), - name: "firstname", - validator: FormBuilderValidators.required( - errorText: "This field is required"), - ), - ), - ], - ), - const SizedBox( - height: 8, - ), - Row( - children: [ - Flexible( - flex: 1, - child: FormBuilderTextField( - decoration: normalTextFieldStyle( - "Middle name *", "Midlle name *"), - name: "middlename", - validator: FormBuilderValidators.required( - errorText: "This field is required"), - ), + Row( + children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + inputFormatters: [ + UpperCaseTextFormatter() + ], + + decoration: normalTextFieldStyle( + "Middle name ", ""), + name: "middlename", + + ), + ), + const SizedBox( + width: 8, + ), + ////Mobile + Flexible( + flex: 1, + child: FormBuilderTextField( + name: "mobile", + decoration: normalTextFieldStyle( + "Tel./Mobile *", "Tel./Mobile"), + validator: FormBuilderValidators.required( + errorText: "This field is required"), + ), + ), + ], ), const SizedBox( - width: 5, + height: 12, ), - ////CATEGORY - Flexible( - flex: 1, - child: FormBuilderDropdown( - name: 'category', - validator: - FormBuilderValidators.required(errorText: ""), - decoration: - normalTextFieldStyle("Category", "Category"), - items: state.categories - .map>( - (AddressCategory cat) { - return DropdownMenuItem( - value: cat, - child: Text(cat.name!), - ); - }).toList(), - onChanged: (value) { - setState(() { - selectedCategory = value; - }); - }, - ), + FormBuilderDropdown( + name: 'category', + validator: + FormBuilderValidators.required(errorText: "This field is required"), + decoration: normalTextFieldStyle( + "Address Category", "Address Category"), + items: state.categories + .map>( + (AddressCategory cat) { + return DropdownMenuItem( + value: cat, + child: Text(cat.name!), + ); + }).toList(), + onChanged: (value) { + setState(() { + selectedCategory = value; + }); + }, ), - ], - ), - const SizedBox( - height: 8, - ), - - ////OVERSEAS ADDRESS - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - setState(() { - overseas = value!; - }); - }, - decoration: normalTextFieldStyle("", ''), - name: 'overseas', - title: const Text("Overseas Address?"), - ), - SizedBox( - height: overseas == true ? 8 : 0, - ), - SizedBox( - child: overseas == false - ? Column( - children: [ - const SizedBox( - height: 12, - ), - ////REGION DROPDOWN - FormBuilderDropdown( - autovalidateMode: - AutovalidateMode.onUserInteraction, - validator: FormBuilderValidators.required( - errorText: "This field is required"), - onChanged: (Region? region) async { - if(selectedRegion != region){ - setState(() { - provinceCall = true; - }); - selectedRegion = region; - getProvinces(); - } - }, - initialValue: null, - decoration: normalTextFieldStyle( - "Region*", "Region"), - name: 'region', - items: state.regions - .map>( - (Region region) { - return DropdownMenuItem( - value: region, - child: Text(region.description!)); - }).toList(), - ), - const SizedBox( - height: 8, - ), - //// PROVINCE DROPDOWN - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: provinceCall, - child: DropdownButtonFormField( - autovalidateMode: AutovalidateMode - .onUserInteraction, - validator: (value) => - value == null ? 'required' : null, - isExpanded: true, - value: selectedProvince, - onChanged: (Province? province) { - if(selectedProvince != province){ - setState(() { - cityCall = true; - }); - selectedProvince = province; - getCities(); - } - }, - items: provinces == null - ? [] - : provinces!.map< - DropdownMenuItem< - Province>>( - (Province province) { - return DropdownMenuItem( - value: province, - child: FittedBox( - child: Text(province - .description!), - )); - }).toList(), - decoration: normalTextFieldStyle( - "Province*", "Province")), - ), - ), - ////CITY MUNICIPALITY - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: cityCall, - child: DropdownButtonFormField< - CityMunicipality>( + const SizedBox( + height: 12, + ), + ////OVERSEAS ADDRESS + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Overseas Address?"), + ), + SizedBox( + height: overseas == true ? 12 : 0, + ), + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + FormBuilderDropdown( + autovalidateMode: + AutovalidateMode.onUserInteraction, validator: FormBuilderValidators.required( errorText: "This field is required"), - isExpanded: true, - onChanged: (CityMunicipality? city) { - if(selectedMunicipality != city){ - setState(() { - barangayCall = true; - }); - selectedMunicipality = city; - getBarangays(); - } + onChanged: (Region? region) async { + if (selectedRegion != region) { + setState(() { + provinceCall = true; + }); + selectedRegion = region; + getProvinces(); + } }, + initialValue: null, decoration: normalTextFieldStyle( - "Municipality*", "Municipality"), - value: selectedMunicipality, - items: citymuns == null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality c) { - return DropdownMenuItem( - value: c, - child: - Text(c.description!)); - }).toList(), + "Region*", "Region"), + name: 'region', + items: state.regions + .map>( + (Region region) { + return DropdownMenuItem( + value: region, + child: Text(region.description!)); + }).toList(), ), - ), - ), - //// BARANGAY - SizedBox( + const SizedBox( + height: 12, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null + ? 'This field is required' + : null, + isExpanded: true, + value: selectedProvince, + onChanged: (Province? province) { + if (selectedProvince != + province) { + setState(() { + cityCall = true; + }); + selectedProvince = province; + getCities(); + } + }, + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province province) { + return DropdownMenuItem( + value: province, + child: FittedBox( + child: Text(province + .description!), + )); + }).toList(), + decoration: normalTextFieldStyle( + "Province*", "Province")), + ), + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: DropdownButtonFormField< + CityMunicipality>( + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + isExpanded: true, + onChanged: + (CityMunicipality? city) { + if (selectedMunicipality != + city) { + setState(() { + barangayCall = true; + }); + selectedMunicipality = city; + getBarangays(); + } + }, + decoration: normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality c) { + return DropdownMenuItem( + value: c, + child: Text( + c.description!)); + }).toList(), + ), + ), + ), + //// BARANGAY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: barangayCall, + child: + DropdownButtonFormField( + isExpanded: true, + onChanged: (Barangay? baragay) { + selectedBarangay = baragay; + }, + decoration: normalTextFieldStyle( + "Barangay*", "Barangay"), + value: selectedBarangay, + items: barangays == null + ? [] + : barangays!.map< + DropdownMenuItem< + Barangay>>( + (Barangay barangay) { + return DropdownMenuItem( + value: barangay, + child: Text(barangay + .description!)); + }).toList(), + ), + ), + ), + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: barangayCall, - child: DropdownButtonFormField( - - isExpanded: true, - onChanged: (Barangay? baragay) { - selectedBarangay = baragay; - }, - decoration: normalTextFieldStyle( - "Barangay*", "Barangay"), - value: selectedBarangay, - items: barangays == null - ? [] - : barangays!.map< - DropdownMenuItem>( - (Barangay barangay) { - return DropdownMenuItem( - value: barangay, - child: Text( - barangay.description!)); - }).toList(), - ), + child: FormBuilderDropdown( + initialValue: null, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + items: state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox( + child: Text(country.name!))); + }).toList(), + name: 'country', + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) { + selectedCountry = value; + }, ), ), - ], - ) - //// COUNTRY DROPDOWN - : SizedBox( - height: 60, - child: FormBuilderDropdown( - initialValue: null, - validator: FormBuilderValidators.required( - errorText: "This field is required"), - items: state.countries - .map>( - (Country country) { - return DropdownMenuItem( - value: country, - child: FittedBox( - child: Text(country.name!))); - }).toList(), - name: 'country', - decoration: normalTextFieldStyle( - "Country*", "Country"), - onChanged: (Country? value) { - selectedCountry = value; - }, - ), - ), + ), + + + + ], ), - - FormBuilderTextField( - name: "mobile", - decoration: normalTextFieldStyle( - "Tel./Mobile *", "Tel./Mobile"), - validator: FormBuilderValidators.required( - errorText: "This field is required"), - ), - const Expanded( - child: SizedBox(), - ), - SizedBox( - width: double.infinity, - height: 60, - child: ElevatedButton( - style: - mainBtnStyle(primary, Colors.transparent, second), - child: const Text(submit), - onPressed: () { - PersonalReference? personalReference; - if (formKey.currentState!.saveAndValidate()) { - String lastname = - formKey.currentState!.value['lastname']; - String firstname = - formKey.currentState!.value['firstname']; - String middlename = - formKey.currentState!.value['middlename']; - String mobile = - formKey.currentState!.value['mobile']; - - Region? region = selectedRegion; - Province? province = Province( - code: selectedProvince?.code, - description: selectedProvince?.description, - region: region, - psgcCode: selectedProvince?.psgcCode, - shortname: selectedProvince?.shortname); - CityMunicipality? city = CityMunicipality( - code: selectedMunicipality?.code, - description: - selectedMunicipality?.description, - province: province, - psgcCode: selectedMunicipality?.psgcCode, - zipcode: selectedMunicipality?.zipcode); - - Address address = Address( - id: null, - addressCategory: selectedCategory, - country: selectedCountry, - barangay: selectedBarangay, - addressClass: null, - cityMunicipality: city); - - if (selectedCountry != null) { - personalReference = PersonalReference( - id: null, - address: Address( + ), + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + child: const Text(submit), + onPressed: () { + PersonalReference? personalReference; + if (formKey.currentState!.saveAndValidate()) { + String lastname = + formKey.currentState!.value['lastname']; + String firstname = + formKey.currentState!.value['firstname']; + String? middlename = + formKey.currentState?.value['middlename']; + String mobile = + formKey.currentState!.value['mobile']; + + Region? region = selectedRegion; + Province? province = Province( + code: selectedProvince?.code, + description: + selectedProvince?.description, + region: region, + psgcCode: selectedProvince?.psgcCode, + shortname: selectedProvince?.shortname); + CityMunicipality? city = CityMunicipality( + code: selectedMunicipality?.code, + description: + selectedMunicipality?.description, + province: province, + psgcCode: selectedMunicipality?.psgcCode, + zipcode: selectedMunicipality?.zipcode); + + Address address = Address( + id: null, + addressCategory: selectedCategory, + country: selectedCountry, + barangay: selectedBarangay, + addressClass: null, + cityMunicipality: city); + + if (selectedCountry != null) { + personalReference = PersonalReference( id: null, - addressCategory: selectedCategory, - country: selectedCountry, - barangay: null, - cityMunicipality: null, - addressClass: null), - lastName: lastname, - contactNo: mobile, - firstName: firstname, - middleName: middlename); - } else { - personalReference = PersonalReference( - id: null, - address: address, - lastName: lastname, - contactNo: mobile, - firstName: firstname, - middleName: middlename); - } - final progress = ProgressHUD.of(context); - progress!.showWithText("Please wait..."); - context.read().add(AddReference( - profileId: widget.profileId, - reference: personalReference, - token: widget.token)); - } - }, - ), - ), - const SizedBox( - height: 20, - ), - ], - )), - ), - ), - ), + address: Address( + id: null, + addressCategory: selectedCategory, + country: selectedCountry, + barangay: null, + cityMunicipality: null, + addressClass: null), + lastName: lastname, + contactNo: mobile, + firstName: firstname, + middleName: middlename); + } else { + personalReference = PersonalReference( + id: null, + address: address, + lastName: lastname, + contactNo: mobile, + firstName: firstname, + middleName: middlename); + } + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + context.read().add( + AddReference( + profileId: widget.profileId, + reference: personalReference, + token: widget.token)); + } + }, + ), + ), + + ], + ), + ), + )), ); } return Container(); diff --git a/lib/screens/profile/components/reference/edit_modal.dart b/lib/screens/profile/components/reference/edit_modal.dart index 136081e..803d7cb 100644 --- a/lib/screens/profile/components/reference/edit_modal.dart +++ b/lib/screens/profile/components/reference/edit_modal.dart @@ -15,13 +15,16 @@ import '../../../../model/profile/references.dart'; import '../../../../theme-data.dart/btn-style.dart'; import '../../../../theme-data.dart/colors.dart'; import '../../../../theme-data.dart/form-style.dart'; +import '../../../../utils/formatters.dart'; +import '../../../../utils/global.dart'; import '../../../../utils/location_utilities.dart'; import '../../../../utils/text_container.dart'; class EditReferenceScreen extends StatefulWidget { final String token; final int profileId; - const EditReferenceScreen({super.key,required this.profileId, required this.token}); + const EditReferenceScreen( + {super.key, required this.profileId, required this.token}); @override State createState() => _EditReferenceScreenState(); @@ -46,188 +49,250 @@ class _EditReferenceScreenState extends State { @override Widget build(BuildContext context) { - - return BlocBuilder( - buildWhen: (previous, current) => false, - builder: (context, state) { - if (state is EditReferenceState) { - overseas = state.isOverseas; - selectedCategory = state.selectedCategory; - ////if not overseas address - //// set initial values - if (!overseas) { - selectedRegion = state.selectedRegion; - provinces = state.provinces; - selectedProvince = state.selectedProvince; - citymuns = state.cities; - selectedMunicipality = state.selectedCity; - barangays = state.barangays; - selectedBarangay = state.selectedBarangay; - } else { - selectedCountry = state.selectedCountry; - } + return BlocBuilder( + buildWhen: (previous, current) => false, + builder: (context, state) { + if (state is EditReferenceState) { + overseas = state.isOverseas; + selectedCategory = state.selectedCategory; + ////if not overseas address + //// set initial values + if (!overseas) { + selectedRegion = state.selectedRegion; + provinces = state.provinces; + selectedProvince = state.selectedProvince; + citymuns = state.cities; + selectedMunicipality = state.selectedCity; + barangays = state.barangays; + selectedBarangay = state.selectedBarangay; + } else { + selectedCountry = state.selectedCountry; + } - return ProgressHUD( - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 25, horizontal: 18), - child: FormBuilder( - key: formKey, - child: Column( + return FormBuilder( + key: formKey, + child: SizedBox( + height: screenHeight * .90, + child: Padding( + padding: const EdgeInsets.all(28), + child: Column( + children: [ + Flexible( + child: Column( + children: [ + const SizedBox(height: 25), + Row( + children: [ + ////LAST NAME + Flexible( + flex: 1, + child: FormBuilderTextField( + inputFormatters: [ + UpperCaseTextFormatter() + ], + initialValue: state.ref.lastName, + decoration: normalTextFieldStyle( + "Last name *", "Last name *"), + name: "lastname", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + ), + ), + const SizedBox( + width: 8, + ), + ////FIRST NAME + Flexible( + flex: 1, + child: FormBuilderTextField( + inputFormatters: [ + UpperCaseTextFormatter() + ], + + initialValue: state.ref.firstName, + decoration: normalTextFieldStyle( + "First name *", "First name *"), + name: "firstname", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + ), + ), + ], + ), + const SizedBox( + height: 12, + ), + Row( + children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + inputFormatters: [ + UpperCaseTextFormatter() + ], + + initialValue: state.ref.middleName, + decoration: normalTextFieldStyle( + "Middle name *", "Midlle name *"), + name: "middlename", + + ), + ), + const SizedBox( + width: 8, + ), + ////CATEGORY + Flexible( + flex: 1, + child: FormBuilderTextField( + initialValue: state.ref.contactNo, + name: "mobile", + decoration: normalTextFieldStyle( + "Tel./Mobile *", "Tel./Mobile"), + validator: FormBuilderValidators.required( + errorText: "This field is required"), + ), + ), + ], + ), + const SizedBox( + height: 12, + ), + FormBuilderDropdown( + name: 'category', + validator: + FormBuilderValidators.required(errorText: "This field is required"), + decoration: normalTextFieldStyle( + "Address Category", "Address Category"), + items: state.categories + .map>( + (AddressCategory cat) { + return DropdownMenuItem( + value: cat, + child: Text(cat.name!), + ); + }).toList(), + initialValue: selectedCategory, + onChanged: (value) { + selectedCategory = value; + }, + ), + const SizedBox( + height: 12, + ), + ////OVERSEAS ADDRESS + StatefulBuilder(builder: (context, setState) { + return Column( children: [ - const SizedBox(height: 25), - Row( - children: [ - ////LAST NAME - Flexible( - flex: 1, - child: FormBuilderTextField( - initialValue: state.ref.lastName, - decoration: normalTextFieldStyle( - "Last name *", "Last name *"), - name: "lastname", - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - ), - ), - const SizedBox( - width: 5, - ), - ////FIRST NAME - Flexible( - flex: 1, - child: FormBuilderTextField( - initialValue: state.ref.firstName, - decoration: normalTextFieldStyle( - "First name *", "First name *"), - name: "firstname", - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - ), - ), - ], + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Overseas Address?"), ), - const SizedBox( - height: 8, + SizedBox( + height: overseas == true ? 12 : 0, ), - Row( - children: [ - Flexible( - flex: 1, - child: FormBuilderTextField( - initialValue: state.ref.middleName, - decoration: normalTextFieldStyle( - "Middle name *", "Midlle name *"), - name: "middlename", - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - ), - ), - const SizedBox( - width: 5, - ), - ////CATEGORY - Flexible( - flex: 1, - child: FormBuilderDropdown< - AddressCategory>( - name: 'category', - validator: - FormBuilderValidators.required( - errorText: ""), - decoration: normalTextFieldStyle( - "Category", "Category"), - items: state.categories.map< - DropdownMenuItem< - AddressCategory>>( - (AddressCategory cat) { - return DropdownMenuItem< - AddressCategory>( - value: cat, - child: Text(cat.name!), - ); - }).toList(), - initialValue: selectedCategory, - onChanged: (value) { - selectedCategory = value; - }, - ), - ), - ], - ), - const SizedBox( - height: 8, - ), - - ////OVERSEAS ADDRESS - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - setState(() { - overseas = value!; - }); - }, - decoration: - normalTextFieldStyle("", ''), - name: 'overseas', - title: - const Text("Overseas Address?"), - ), - SizedBox( - height: overseas == true ? 8 : 0, - ), - SizedBox( - child: overseas == false - ? Column( - children: [ - const SizedBox( - height: 12, - ), - ////REGION DROPDOWN - DropdownButtonFormField< - Region?>( - isExpanded: true, + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + DropdownButtonFormField( + isExpanded: true, + autovalidateMode: AutovalidateMode + .onUserInteraction, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + onChanged: (Region? region) async { + setState(() { + provinceCall = true; + + selectedRegion = region; + }); + //// GET PROVINCES + provinces = await LocationUtils + .instance + .getProvinces( + regionCode: selectedRegion! + .code + .toString()); + selectedProvince = provinces![0]; + setState(() { + provinceCall = false; + cityCall = true; + }); + ////GET CITY MUNICIPALITY + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code!); + selectedMunicipality = citymuns![0]; + setState(() { + cityCall = false; + barangayCall = true; + }); + //// GET BARANGAYS + barangays = await LocationUtils + .instance + .getBarangay( + code: selectedMunicipality! + .code!); + selectedBarangay = barangays![0]; + setState(() { + barangayCall = false; + }); + }, + value: selectedRegion, + decoration: normalTextFieldStyle( + "Region*", "Region"), + items: state.regions + .map>( + (Region region) { + return DropdownMenuItem( + value: region, + child: + Text(region.description!)); + }).toList(), + ), + const SizedBox( + height: 12, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( autovalidateMode: AutovalidateMode .onUserInteraction, - validator: - FormBuilderValidators - .required( - errorText: - "This field is required"), - onChanged: (Region? - region) async { + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + onChanged: + (Province? province) async { + selectedProvince = province; setState(() { - provinceCall = true; - - selectedRegion = - region; - }); - //// GET PROVINCES - provinces = await LocationUtils - .instance - .getProvinces( - regionCode: - selectedRegion! - .code - .toString()); - selectedProvince = - provinces![0]; - setState(() { - provinceCall = false; cityCall = true; }); - ////GET CITY MUNICIPALITY + //// GET CITIES citymuns = await LocationUtils .instance .getCities( @@ -240,12 +305,12 @@ class _EditReferenceScreenState extends State { cityCall = false; barangayCall = true; }); - //// GET BARANGAYS - barangays = - await LocationUtils - .instance - .getBarangay( - code: selectedMunicipality! + //// GET BARANGAY + barangays = await LocationUtils + .instance + .getBarangay( + code: + selectedMunicipality! .code!); selectedBarangay = barangays![0]; @@ -253,373 +318,236 @@ class _EditReferenceScreenState extends State { barangayCall = false; }); }, - value: selectedRegion, + value: selectedProvince, + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province province) { + return DropdownMenuItem( + value: province, + child: FittedBox( + child: Text(province + .description!), + )); + }).toList(), decoration: normalTextFieldStyle( - "Region*", - "Region"), - items: state.regions.map< - DropdownMenuItem< - Region>>( - (Region region) { - return DropdownMenuItem< - Region>( - value: region, - child: Text(region - .description!)); - }).toList(), - ), - const SizedBox( - height: 8, - ), - //// PROVINCE DROPDOWN - SizedBox( - height: 60, - child: ModalProgressHUD( - color: - Colors.transparent, - inAsyncCall: - provinceCall, - child: DropdownButtonFormField< - Province?>( - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - onChanged: (Province? - province) async { - selectedProvince = - province; - setState(() { - cityCall = true; - }); - //// GET CITIES - citymuns = await LocationUtils - .instance - .getCities( - code: selectedProvince! - .code!); - selectedMunicipality = - citymuns![0]; - setState(() { - cityCall = - false; - barangayCall = - true; - }); - //// GET BARANGAY - barangays = await LocationUtils - .instance - .getBarangay( - code: selectedMunicipality! - .code!); - selectedBarangay = - barangays![0]; - setState(() { - barangayCall = - false; - }); - }, - value: - selectedProvince, - items: provinces == - null - ? [] - : provinces!.map< - DropdownMenuItem< - Province>>((Province - province) { - return DropdownMenuItem( - value: - province, - child: - FittedBox( - child: - Text(province.description!), - )); - }).toList(), - decoration: - normalTextFieldStyle( - "Province*", - "Province")), - ), - ), - ////CITY MUNICIPALITY - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: cityCall, - child: - DropdownButtonFormField< - CityMunicipality>( - validator: - FormBuilderValidators - .required( - errorText: - "This field is required"), - isExpanded: true, - onChanged: - (CityMunicipality? - city) async { - setState(() { - barangayCall = - true; - }); - selectedMunicipality = - city; - //// GET BARANGAYS - barangays = await LocationUtils - .instance - .getBarangay( - code: selectedMunicipality! - .code!); - selectedBarangay = - barangays![0]; - setState(() { - barangayCall = - false; - }); - }, - decoration: - normalTextFieldStyle( - "Municipality*", - "Municipality"), - value: - selectedMunicipality, - items: citymuns == - null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality - c) { - return DropdownMenuItem( - value: c, - child: Text( - c.description!)); - }).toList(), - ), - ), - ), - //// BARANGAY - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: - barangayCall, - child: - DropdownButtonFormField< - Barangay>( - validator: - FormBuilderValidators - .required( - errorText: - "This field is required"), - isExpanded: true, - onChanged: (Barangay? - baragay) { - selectedBarangay = - baragay; - }, - decoration: - normalTextFieldStyle( - "Barangay*", - "Barangay"), - value: - selectedBarangay, - items: barangays == - null - ? [] - : barangays!.map< - DropdownMenuItem< - Barangay>>((Barangay - barangay) { - return DropdownMenuItem( - value: - barangay, - child: Text( - barangay - .description!)); - }).toList(), - ), - ), - ), - ], - ) - //// COUNTRY DROPDOWN - : SizedBox( - height: 60, - child: - DropdownButtonFormField< - Country>( - isExpanded: true, - value: selectedCountry, + "Province*", + "Province")), + ), + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: DropdownButtonFormField< + CityMunicipality>( validator: FormBuilderValidators .required( errorText: "This field is required"), - items: state.countries.map< - DropdownMenuItem< - Country>>( - (Country country) { - return DropdownMenuItem< - Country>( - value: country, - child: FittedBox( - child: Text( - country - .name!))); - }).toList(), - - decoration: - normalTextFieldStyle( - "Country*", - "Country"), - //// country dropdown - onChanged: - (Country? value) { - selectedCountry = value; + isExpanded: true, + onChanged: (CityMunicipality? + city) async { + setState(() { + barangayCall = true; + }); + selectedMunicipality = city; + //// GET BARANGAYS + barangays = await LocationUtils + .instance + .getBarangay( + code: + selectedMunicipality! + .code!); + selectedBarangay = + barangays![0]; + setState(() { + barangayCall = false; + }); }, + decoration: normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality c) { + return DropdownMenuItem( + value: c, + child: Text( + c.description!)); + }).toList(), ), ), - ), - ], - ); - }), - - FormBuilderTextField( - initialValue: state.ref.contactNo, - name: "mobile", - decoration: normalTextFieldStyle( - "Tel./Mobile *", "Tel./Mobile"), - validator: FormBuilderValidators.required( - errorText: "This field is required"), - ), - const Expanded( - child: SizedBox(), - ), - SizedBox( - width: double.infinity, - height: 60, - child: ElevatedButton( - style: mainBtnStyle( - primary, Colors.transparent, second), - child: const Text(submit), - onPressed: () { - PersonalReference? personalReference; - if (formKey.currentState! - .saveAndValidate()) { - final progress = - ProgressHUD.of(context); - progress!.showWithText( - "Please wait..."); - String lastname = formKey - .currentState!.value['lastname']; - String firstname = formKey - .currentState!.value['firstname']; - String middlename = formKey - .currentState! - .value['middlename']; - String mobile = formKey - .currentState!.value['mobile']; - - Region? region = selectedRegion; - Province? province = Province( - code: selectedProvince?.code, - description: - selectedProvince?.description, - region: region, - psgcCode: - selectedProvince?.psgcCode, - shortname: - selectedProvince?.shortname); - CityMunicipality? city = - CityMunicipality( - code: selectedMunicipality - ?.code, - description: - selectedMunicipality - ?.description, - province: province, - psgcCode: selectedMunicipality - ?.psgcCode, - zipcode: selectedMunicipality - ?.zipcode); - - ////IF IS OVERSEAS - if (overseas) { - personalReference = - PersonalReference( - id: state.ref.id, - address: Address( - id: state - .ref.address!.id, - addressCategory: - selectedCategory, - country: - selectedCountry, - barangay: null, - cityMunicipality: null, - addressClass: null), - lastName: lastname, - contactNo: mobile, - firstName: firstname, - middleName: middlename); - } else { - //// IF NOT OVERSEAS - personalReference = - PersonalReference( - id: state.ref.id, - address: Address( - id: state - .ref.address!.id, - addressCategory: - selectedCategory, - country: Country( - id: 175, - code: null, - name: null), - barangay: - selectedBarangay, - cityMunicipality: city, - addressClass: state - .ref - .address - ?.addressClass), - lastName: lastname, - contactNo: mobile, - firstName: firstname, - middleName: middlename); - } - - context.read().add( - EditReference( - profileId: - widget.profileId, - reference: personalReference, - token: widget.token)); - } - }, - ), - ), - const SizedBox( - height: 20, + ), + //// BARANGAY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: barangayCall, + child: DropdownButtonFormField< + Barangay>( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + isExpanded: true, + onChanged: (Barangay? baragay) { + selectedBarangay = baragay; + }, + decoration: normalTextFieldStyle( + "Barangay*", "Barangay"), + value: selectedBarangay, + items: barangays == null + ? [] + : barangays!.map< + DropdownMenuItem< + Barangay>>( + (Barangay barangay) { + return DropdownMenuItem( + value: barangay, + child: Text(barangay + .description!)); + }).toList(), + ), + ), + ), + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( + height: 60, + child: DropdownButtonFormField( + isExpanded: true, + value: selectedCountry?.id==175?null:selectedCountry, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + items: state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox( + child: Text(country.name!))); + }).toList(), + + decoration: normalTextFieldStyle( + "Country*", "Country"), + //// country dropdown + onChanged: (Country? value) { + selectedCountry = value; + }, + ), + ), ), ], - )), + ); + }), + + + + const SizedBox( + height: 20, + ), + ], ), - ); - } - return Container(); - }, - + ), + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: + mainBtnStyle(primary, Colors.transparent, second), + child: const Text(submit), + onPressed: () { + PersonalReference? personalReference; + if (formKey.currentState!.saveAndValidate()) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + String lastname = + formKey.currentState!.value['lastname']; + String firstname = + formKey.currentState!.value['firstname']; + String? middlename = + formKey.currentState?.value['middlename']; + String mobile = + formKey.currentState!.value['mobile']; + + Region? region = selectedRegion; + Province? province = Province( + code: selectedProvince?.code, + description: selectedProvince?.description, + region: region, + psgcCode: selectedProvince?.psgcCode, + shortname: selectedProvince?.shortname); + CityMunicipality? city = CityMunicipality( + code: selectedMunicipality?.code, + description: + selectedMunicipality?.description, + province: province, + psgcCode: selectedMunicipality?.psgcCode, + zipcode: selectedMunicipality?.zipcode); + + ////IF IS OVERSEAS + if (overseas) { + personalReference = PersonalReference( + id: state.ref.id, + address: Address( + id: state.ref.address!.id, + addressCategory: selectedCategory, + country: selectedCountry, + barangay: null, + cityMunicipality: null, + addressClass: null), + lastName: lastname, + contactNo: mobile, + firstName: firstname, + middleName: middlename); + } else { + //// IF NOT OVERSEAS + personalReference = PersonalReference( + id: state.ref.id, + address: Address( + id: state.ref.address!.id, + addressCategory: selectedCategory, + country: Country( + id: 175, code: null, name: null), + barangay: selectedBarangay, + cityMunicipality: city, + addressClass: + state.ref.address?.addressClass), + lastName: lastname, + contactNo: mobile, + firstName: firstname, + middleName: middlename); + } + + context.read().add(EditReference( + profileId: widget.profileId, + reference: personalReference, + token: widget.token)); + } + }, + ), + ), + ], + ), + ), + )); + } + return Container(); + }, ); } } diff --git a/lib/screens/profile/components/references_screen.dart b/lib/screens/profile/components/references_screen.dart index f684f6c..1f53c27 100644 --- a/lib/screens/profile/components/references_screen.dart +++ b/lib/screens/profile/components/references_screen.dart @@ -3,7 +3,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; -import 'package:fluttericon/font_awesome_icons.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/screens/profile/components/reference/add_modal.dart'; @@ -13,20 +12,18 @@ import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/Leadings/close_leading.dart'; +import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; - import '../../../bloc/profile/references/references_bloc.dart'; import '../../../utils/alerts.dart'; - class ReferencesScreen extends StatelessWidget { const ReferencesScreen({super.key}); - @override Widget build(BuildContext context) { int? profileId; String? token; return Scaffold( - resizeToAvoidBottomInset: true, + resizeToAvoidBottomInset: true, appBar: AppBar( title: context.watch().state is AddReferenceState ? const Text("Add Personal Reference") @@ -43,7 +40,7 @@ class ReferencesScreen extends StatelessWidget { //// close button CloseLeading(onPressed: () { context.read().add( - GetReferences(profileId: profileId!, token: token!)); + LoadReferences()); }), ] : @@ -93,18 +90,18 @@ class ReferencesScreen extends StatelessWidget { successAlert(context, "Adding Successfull!", state.response['message'], () { Navigator.of(context).pop(); - context.read().add( - LoadReferences( - )); + context + .read() + .add(LoadReferences()); }); } else { errorAlert(context, "Adding Failed", "Something went wrong. Please try again.", () { Navigator.of(context).pop(); - context.read().add( - LoadReferences( - )); + context + .read() + .add(LoadReferences()); }); } } @@ -114,18 +111,18 @@ class ReferencesScreen extends StatelessWidget { successAlert(context, "Update Successfull!", state.response['message'], () { Navigator.of(context).pop(); - context.read().add( - LoadReferences( - )); + context + .read() + .add(LoadReferences()); }); } else { errorAlert(context, "Update Failed", "Something went wrong. Please try again.", () { Navigator.of(context).pop(); - context.read().add( - LoadReferences( - )); + context + .read() + .add(LoadReferences()); }); } } @@ -134,20 +131,20 @@ class ReferencesScreen extends StatelessWidget { if (state is DeleteReferenceState) { if (state.success) { successAlert(context, "Deletion Successfull", - "Eligibility has been deleted successfully", + "Personal reference has been deleted successfully", () { Navigator.of(context).pop(); - context.read().add( - LoadReferences( -)); + context + .read() + .add(LoadReferences()); }); } else { errorAlert(context, "Deletion Failed", - "Error deleting eligibility", () { + "Error Deleting Personal reference", () { Navigator.of(context).pop(); - context.read().add( - LoadReferences( - )); + context + .read() + .add(LoadReferences()); }); } } @@ -163,8 +160,9 @@ class ReferencesScreen extends StatelessWidget { itemCount: state.references.length, itemBuilder: (BuildContext context, int index) { + String? middlename = state.references[index].middleName ?? ""; String fullname = - "${state.references[index].firstName} ${state.references[index].middleName} ${state.references[index].lastName}"; + "${state.references[index].firstName} $middlename ${state.references[index].lastName}"; String addres = state.references[index] .address!.country?.id != 175 @@ -196,10 +194,11 @@ class ReferencesScreen extends StatelessWidget { .copyWith( fontWeight: FontWeight - .w500)), - const Divider(), + .w500, + color: + primary)), const SizedBox( - height: 5, + height: 8, ), Text(addres, style: Theme.of(context) @@ -226,12 +225,12 @@ class ReferencesScreen extends StatelessWidget { onSelected: (value) { ////delete eligibilty-= = = = = = = = =>> if (value == 2) { - confirmAlert(context, () { - final progress = - ProgressHUD.of(context); - progress!.showWithText( - "Please wait..."); + final progress = + ProgressHUD.of( + context); + progress!.showWithText( + "Please wait..."); context .read< ReferencesBloc>() @@ -250,7 +249,7 @@ class ReferencesScreen extends StatelessWidget { } if (value == 1) { ////edit reference-= = = = = = = = =>> - final progress = + final progress = ProgressHUD.of(context); progress!.showWithText( "Please wait..."); @@ -262,19 +261,20 @@ class ReferencesScreen extends StatelessWidget { index])); } }, - menuItems: [ + menuItems: [ popMenuItem( - text: "Edit", + text: "Update", value: 1, icon: Icons.edit), popMenuItem( - text: "Delete", + text: "Remove", value: 2, icon: Icons.delete), - popMenuItem( - text: "Attachment", - value: 3, - icon: FontAwesome.attach) + popMenuItem( + text: "Attach", + value: 2, + icon: Icons.attach_file), + ], icon: const Icon( Icons.more_vert, @@ -291,18 +291,30 @@ class ReferencesScreen extends StatelessWidget { ], ); }); + }else{ + return const EmptyData(message: "You don't have any references added. Please click + to add."); } } if (state is AddReferenceState) { - return AddReferenceScreen(profileId: profileId!, token: token!,); + return AddReferenceScreen( + profileId: profileId!, + token: token!, + ); } if (state is EditReferenceState) { - return EditReferenceScreen(profileId: profileId!,token: token!,); + return EditReferenceScreen( + profileId: profileId!, + token: token!, + ); } if (state is ReferencesErrorState) { return SomethingWentWrong( - message: state.message, onpressed: () { - context.read().add(GetReferences(profileId: profileId!, token: token!)); + message: state.message, + onpressed: () { + context.read().add( + GetReferences( + profileId: profileId!, + token: token!)); }); } return Container(); diff --git a/lib/screens/profile/components/voluntary_works/add_modal.dart b/lib/screens/profile/components/voluntary_works/add_modal.dart index a2b9c7e..808364e 100644 --- a/lib/screens/profile/components/voluntary_works/add_modal.dart +++ b/lib/screens/profile/components/voluntary_works/add_modal.dart @@ -1,18 +1,13 @@ import 'package:date_time_picker/date_time_picker.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; -import 'package:fluttericon/font_awesome_icons.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; import 'package:searchfield/searchfield.dart'; import 'package:unit2/bloc/profile/voluntary_works/voluntary_work_bloc.dart'; import 'package:unit2/utils/global.dart'; - -import '../../../../model/location/barangay.dart'; import '../../../../model/location/city.dart'; import '../../../../model/location/country.dart'; import '../../../../model/location/provinces.dart'; @@ -25,6 +20,7 @@ import '../../../../theme-data.dart/box_shadow.dart'; import '../../../../theme-data.dart/btn-style.dart'; import '../../../../theme-data.dart/colors.dart'; import '../../../../theme-data.dart/form-style.dart'; +import '../../../../utils/formatters.dart'; import '../../../../utils/location_utilities.dart'; import '../../../../utils/text_container.dart'; import '../../shared/add_for_empty_search.dart'; @@ -69,579 +65,673 @@ class _AddVoluntaryWorkScreenState extends State { Province? selectedProvince; CityMunicipality? selectedMunicipality; Country? selectedCountry; + DateTime? from; + DateTime? to; + @override + void dispose() { + addPositionController.dispose(); + addAgencyController.dispose(); + toDateController.dispose(); + fromDateController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return BlocBuilder( builder: (context, state) { if (state is AddVoluntaryWorkState) { - return Center( - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 18), - child: FormBuilder( - key: formKey, - child: ListView( - children: [ - ////POSITIONS - StatefulBuilder(builder: (context, setState) { - return SearchField( - itemHeight: 50, - suggestionsDecoration: box1(), - suggestions: state.positions - .map((Position position) => SearchFieldListItem( - position.title!, - item: position, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10), - child: ListTile( - title: Text(position.title!), - )))) - .toList(), - focusNode: positionFocusNode, - searchInputDecoration: - normalTextFieldStyle("Position *", "").copyWith( - suffixIcon: GestureDetector( - child: const Icon(Icons.arrow_drop_down), - onTap: () => positionFocusNode.unfocus(), - )), - onSuggestionTap: (position) { - setState(() { - selectedPosition = position.item; - positionFocusNode.unfocus(); - }); - }, - ////EMPTY WIDGET - emptyWidget: EmptyWidget( - title: "Add Position", - controller: addPositionController, - onpressed: () { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 32, horizontal: 28), + child: FormBuilder( + key: formKey, + child: Column( + children: [ + Flexible( + child: ListView( + children: [ + ////POSITIONS + StatefulBuilder(builder: (context, setState) { + return SearchField( + inputFormatters: [UpperCaseTextFormatter()], + itemHeight: 70, + suggestionsDecoration: box1(), + suggestions: state.positions + .map((Position position) => + SearchFieldListItem(position.title!, + item: position, + child: Padding( + padding: + const EdgeInsets.symmetric( + horizontal: 10), + child: ListTile( + title: Text( + position.title!, + overflow: + TextOverflow.visible, + ), + )))) + .toList(), + focusNode: positionFocusNode, + searchInputDecoration: + normalTextFieldStyle("Position *", "") + .copyWith( + suffixIcon: GestureDetector( + child: const Icon(Icons.arrow_drop_down), + onTap: () => positionFocusNode.unfocus(), + )), + onSuggestionTap: (position) { setState(() { - Position newAgencyPosition = Position( - id: null, - title: addPositionController.text - .toUpperCase()); - - state.positions.insert(0, newAgencyPosition); - - addPositionController.text = ""; - + selectedPosition = position.item; + positionFocusNode.unfocus(); }); - }), - validator: (position) { - if (position!.isEmpty) { - return "This field is required"; - } - return null; - }, - ); - }), - const SizedBox( - height: 12, - ), - ////AGENCY - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - SearchField( - itemHeight: 70, - focusNode: agencyFocusNode, - suggestions: state.agencies - .map((Agency agency) => - SearchFieldListItem(agency.name!, - item: agency, - child: ListTile( - title: Text( - agency.name!, - overflow: TextOverflow.ellipsis, - ), - subtitle: Text( - agency.privateEntity == true + }, + ////EMPTY WIDGET + emptyWidget: EmptyWidget( + title: "Add Position", + controller: addPositionController, + onpressed: () { + setState(() { + Position newAgencyPosition = Position( + id: null, + title: addPositionController.text + .toUpperCase()); + + state.positions + .insert(0, newAgencyPosition); + + addPositionController.text = ""; + Navigator.pop(context); + }); + }), + validator: (position) { + if (position!.isEmpty) { + return "This field is required"; + } + return null; + }, + ); + }), + const SizedBox( + height: 12, + ), + ////AGENCY + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + SearchField( + inputFormatters: [UpperCaseTextFormatter()], + itemHeight: 100, + focusNode: agencyFocusNode, + suggestions: state.agencies + .map((Agency agency) => + SearchFieldListItem(agency.name!, + item: agency, + child: ListTile( + title: Text( + agency.name!, + overflow: + TextOverflow.visible, + ), + subtitle: Text(agency + .privateEntity == + true ? "Private" : agency.privateEntity == false ? "Government" : ""), - ))) - .toList(), - searchInputDecoration: - normalTextFieldStyle("Agency *", "") - .copyWith( - suffixIcon: GestureDetector( - child: const Icon( - Icons.arrow_drop_down, - ), - onTap: () => agencyFocusNode.unfocus(), - )), - ////SELETECTED - onSuggestionTap: (agency) { - setState(() { - selectedAgency = agency.item; - if (selectedAgency!.privateEntity == null) { - showAgency = true; - showIsPrivateRadio = true; - } else { - showAgency = false; - showIsPrivateRadio = false; - } - agencyFocusNode.unfocus(); - }); - }, - validator: (agency) { - if (agency!.isEmpty) { - return "This field is required"; - } - return null; - }, - emptyWidget: EmptyWidget( - controller: addAgencyController, - onpressed: () { + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle("Agency *", "") + .copyWith( + suffixIcon: GestureDetector( + child: const Icon( + Icons.arrow_drop_down, + ), + onTap: () => agencyFocusNode.unfocus(), + )), + ////SELETECTED + onSuggestionTap: (agency) { setState(() { - Agency newAgency = Agency( - id: null, - name: addAgencyController.text - .toUpperCase(), - category: null, - privateEntity: null); - state.agencies.insert(0, newAgency); - - addAgencyController.text = ""; - Navigator.pop(context); + selectedAgency = agency.item; + if (selectedAgency!.privateEntity == + null) { + showAgency = true; + showIsPrivateRadio = true; + } else { + showAgency = false; + showIsPrivateRadio = false; + } + agencyFocusNode.unfocus(); }); }, - title: "Add Agency")), - - SizedBox( - height: showAgency ? 12 : 0, - ), - ////SHOW CATEGORY AGENCY - SizedBox( - child: showAgency - ? SearchField( - focusNode: agencyCategoryFocusNode, - itemHeight: 70, - suggestions: state.agencyCategory - .map((Category category) => - SearchFieldListItem( - category.name!, - item: category, - child: ListTile( - title: Text(category.name!), - subtitle: Text(category - .industryClass!.name!), - ))) - .toList(), - emptyWidget: Container( - height: 100, - decoration: box1(), - child: const Center( - child: Text("No result found ...")), - ), - onSuggestionTap: (agencyCategory) { - setState(() { - selectedCategoty = - agencyCategory.item; - agencyCategoryFocusNode.unfocus(); - }); - }, - searchInputDecoration: - normalTextFieldStyle("Category *", "") - .copyWith( - suffixIcon: const Icon( - Icons.arrow_drop_down)), - validator: (value) { - if (value!.isEmpty) { - return "This field is required"; - } - return null; - }, - ) - : const SizedBox(), - ), - SizedBox( - height: showIsPrivateRadio ? 12 : 0, - ), - ////PRVIATE SECTOR - SizedBox( - child: showIsPrivateRadio - ? FormBuilderSwitch( - initialValue: false, - title: Text(isPrivate ? "YES" : "NO"), - decoration: normalTextFieldStyle( - "Private Entity?", - 'Private Entity?'), - - ////onvhange private sector - onChanged: (value) { + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + emptyWidget: EmptyWidget( + controller: addAgencyController, + onpressed: () { setState(() { - isPrivate = value!; - agencyCategoryFocusNode.unfocus(); + Agency newAgency = Agency( + id: null, + name: addAgencyController.text + .toUpperCase(), + category: null, + privateEntity: null); + state.agencies.insert(0, newAgency); + + addAgencyController.text = ""; + Navigator.pop(context); }); }, + title: "Add Agency")), - name: 'isPrivate', - validator: - FormBuilderValidators.required(), - ) - : const SizedBox()), - const SizedBox( - height: 12, - ), - //// total hours - FormBuilderTextField( - validator: FormBuilderValidators.required( - errorText: "This Field is required"), - name: "total_hours", - keyboardType: TextInputType.number, - decoration: - normalTextFieldStyle("Total Hours*", "0"), - ), - const SizedBox( - height: 12, - ), - ////Currently Involved - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - FormBuilderSwitch( - initialValue: currentlyInvolved, - activeColor: second, - onChanged: (value) { - setState(() { - currentlyInvolved = value!; - }); - }, - decoration: normalTextFieldStyle( - "Currently Involved?", 'Graduated?'), - name: 'currently_involved', - title: - Text(currentlyInvolved ? "YES" : "NO"), - ), - const SizedBox( - height: 12, - ), - SizedBox( - width: screenWidth, - child: Row( - children: [ - //// FROM DATE - Flexible( - flex: 1, - child: DateTimePicker( + SizedBox( + height: showAgency ? 12 : 0, + ), + ////SHOW CATEGORY AGENCY + SizedBox( + child: showAgency + ? SearchField( + focusNode: agencyCategoryFocusNode, + itemHeight: 70, + suggestions: state.agencyCategory + .map((Category category) => + SearchFieldListItem( + category.name!, + item: category, + child: ListTile( + title: Text( + category.name!), + subtitle: Text(category + .industryClass! + .name!), + ))) + .toList(), + emptyWidget: Container( + height: 100, + decoration: box1(), + child: const Center( + child: Text( + "No result found ...")), + ), + onSuggestionTap: (agencyCategory) { + setState(() { + selectedCategoty = + agencyCategory.item; + agencyCategoryFocusNode.unfocus(); + }); + }, + searchInputDecoration: + normalTextFieldStyle( + "Category *", "") + .copyWith( + suffixIcon: IconButton( + icon: const Icon( + Icons.arrow_drop_down), + onPressed: () { + agencyCategoryFocusNode.unfocus(); + }, + )), + validator: (value) { + if (value!.isEmpty) { + return "This field is required"; + } + return null; + }, + ) + : const SizedBox(), + ), + SizedBox( + height: showIsPrivateRadio ? 12 : 0, + ), + ////PRVIATE SECTOR + SizedBox( + child: showIsPrivateRadio + ? FormBuilderSwitch( + initialValue: false, + title: + Text(isPrivate ? "YES" : "NO"), + decoration: normalTextFieldStyle( + "Private Entity?", + 'Private Entity?'), + + ////onvhange private sector + onChanged: (value) { + setState(() { + isPrivate = value!; + agencyCategoryFocusNode + .unfocus(); + }); + }, + + name: 'isPrivate', + validator: FormBuilderValidators + .required(), + ) + : const SizedBox()), + const SizedBox( + height: 12, + ), + //// total hours + FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This Field is required"), + name: "total_hours", + keyboardType: TextInputType.number, + decoration: + normalTextFieldStyle("Total Hours*", "0"), + ), + const SizedBox( + height: 12, + ), + ////Currently Involved + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: currentlyInvolved, + activeColor: second, + onChanged: (value) { + setState(() { + currentlyInvolved = value!; + }); + }, + decoration: normalTextFieldStyle( + "Currently Involved?", + 'Graduated?'), + name: 'currently_involved', + title: Text( + currentlyInvolved ? "YES" : "NO"), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + child: StatefulBuilder( + builder: (context, setState) { + return Row( + children: [ + //// FROM DATE + Flexible( + flex: 1, + child: DateTimePicker( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + use24HourFormat: false, + icon: const Icon( + Icons.date_range), + controller: + fromDateController, + firstDate: DateTime(1990), + lastDate: DateTime(2100), + selectableDayPredicate: + (date) { + if (to != null && + to!.microsecondsSinceEpoch <= + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, + onChanged: (value) { + setState(() { + from = DateTime.parse( + value); + }); + }, + initialDate: to == null + ? DateTime.now() + : to!.subtract( + const Duration( + days: 1)), + timeHintText: + "Date of Examination/Conferment", + decoration: + normalTextFieldStyle( + "From *", + "From *") + .copyWith( + prefixIcon: + const Icon( + Icons.date_range, + color: Colors.black87, + )), + initialValue: null, + )), + const SizedBox( + width: 12, + ), + //// TO DATE + Flexible( + flex: 1, + child: currentlyInvolved + ? TextFormField( + enabled: false, + initialValue: "PRESENT", + style: const TextStyle( + color: + Colors.black45), + decoration: + normalTextFieldStyle( + "", "") + .copyWith(), + ) + : DateTimePicker( + validator: + FormBuilderValidators + .required( + errorText: + "This field is required"), + controller: + toDateController, + selectableDayPredicate: + (date) { + if (from != null && + from!.microsecondsSinceEpoch >= + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, + onChanged: (value) { + setState(() { + to = DateTime.parse( + value); + }); + }, + initialDate: from == + null + ? DateTime.now() + : from!.add( + const Duration( + days: 1)), + firstDate: + DateTime(1990), + lastDate: + DateTime(2100), + decoration: normalTextFieldStyle( + "To *", "To *") + .copyWith( + prefixIcon: + const Icon( + Icons + .date_range, + color: Colors + .black87, + ), + prefixText: + currentlyInvolved + ? "PRESENT" + : ""), + initialValue: null, + ), + ), + ], + ); + }), + ), + ], + ); + }), + ], + ); + }), + const SizedBox( + height: 12, + ), + //// OVERSEAS + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: normalTextFieldStyle( + "Overseas Address?", ''), + name: 'overseas', + title: Text(overseas ? "YES" : "NO"), + ), + SizedBox( + height: overseas == true ? 12 : 0, + ), + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + FormBuilderDropdown( + autovalidateMode: AutovalidateMode + .onUserInteraction, validator: FormBuilderValidators .required( errorText: "This field is required"), - use24HourFormat: false, - icon: - const Icon(Icons.date_range), - controller: fromDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), - timeHintText: - "Date of Examination/Conferment", - decoration: normalTextFieldStyle( - "From *", "From *") - .copyWith( - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - )), + onChanged: + (Region? region) async { + if (selectedRegion != region) { + setState(() { + provinceCall = true; + }); + selectedRegion = region; + getProvinces(); + } + }, initialValue: null, - )), - const SizedBox( - width: 12, - ), - //// TO DATE - Flexible( - flex: 1, - child: currentlyInvolved - ? TextFormField( - enabled: false, - initialValue: "PRESENT", - style: const TextStyle( - color: Colors.black45), - decoration: - normalTextFieldStyle( - "", "") - .copyWith(), - ) - : DateTimePicker( + decoration: normalTextFieldStyle( + "Region*", "Region"), + name: 'region', + items: state.regions.map< + DropdownMenuItem>( + (Region region) { + return DropdownMenuItem( + value: region, + child: Text( + region.description!)); + }).toList(), + ), + const SizedBox( + height: 12, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + value: selectedProvince, + onChanged: + (Province? province) { + if (selectedProvince != + province) { + setState(() { + cityCall = true; + }); + selectedProvince = + province; + getCities(); + } + }, + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province + province) { + return DropdownMenuItem( + value: province, + child: + FittedBox( + child: Text( + province + .description!), + )); + }).toList(), + decoration: + normalTextFieldStyle( + "Province*", + "Province")), + ), + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: DropdownButtonFormField< + CityMunicipality>( validator: FormBuilderValidators .required( errorText: "This field is required"), - controller: toDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), - decoration: normalTextFieldStyle( - "To *", "To *") - .copyWith( - prefixIcon: - const Icon( - Icons.date_range, - color: - Colors.black87, - ), - prefixText: - currentlyInvolved - ? "PRESENT" - : ""), - initialValue: null, + isExpanded: true, + onChanged: + (CityMunicipality? city) { + if (selectedMunicipality != + city) { + selectedMunicipality = + city; + } + }, + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), ), - ), - ], - ), - ), - ], - ); - }), - ], - ); - }), - const SizedBox( - height: 12, - ), - //// OVERSEAS - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - setState(() { - overseas = value!; - }); - }, - decoration: - normalTextFieldStyle("Overseas Address?", ''), - name: 'overseas', - title: Text(overseas ? "YES" : "NO"), - ), - SizedBox( - height: overseas == true ? 8 : 0, - ), - SizedBox( - child: overseas == false - ? Column( - children: [ - const SizedBox( - height: 12, - ), - ////REGION DROPDOWN - FormBuilderDropdown( - autovalidateMode: AutovalidateMode - .onUserInteraction, - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - onChanged: (Region? region) async { - if (selectedRegion != region) { - setState(() { - provinceCall = true; - }); - selectedRegion = region; - getProvinces(); - } - }, - initialValue: null, - decoration: normalTextFieldStyle( - "Region*", "Region"), - name: 'region', - items: state.regions - .map>( - (Region region) { - return DropdownMenuItem( - value: region, - child: - Text(region.description!)); - }).toList(), - ), - const SizedBox( - height: 8, - ), - //// PROVINCE DROPDOWN - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: provinceCall, - child: DropdownButtonFormField< - Province?>( - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - value: selectedProvince, - onChanged: - (Province? province) { - if (selectedProvince != - province) { - setState(() { - cityCall = true; - }); - selectedProvince = province; - getCities(); - } - }, - items: provinces == null - ? [] - : provinces!.map< - DropdownMenuItem< - Province>>( - (Province province) { - return DropdownMenuItem( - value: province, - child: FittedBox( - child: Text(province - .description!), - )); - }).toList(), - decoration: - normalTextFieldStyle( - "Province*", - "Province")), - ), - ), - ////CITY MUNICIPALITY - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: cityCall, - child: DropdownButtonFormField< - CityMunicipality>( - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - isExpanded: true, - onChanged: - (CityMunicipality? city) { - if (selectedMunicipality != - city) { - selectedMunicipality = city; - } - }, - decoration: normalTextFieldStyle( - "Municipality*", - "Municipality"), - value: selectedMunicipality, - items: citymuns == null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality c) { - return DropdownMenuItem( - value: c, - child: Text( - c.description!)); - }).toList(), + ), ), + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( + height: 60, + child: FormBuilderDropdown( + initialValue: null, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + items: state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox( + child: + Text(country.name!))); + }).toList(), + name: 'country', + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) { + selectedCountry = value; + }, ), ), - ], - ) - //// COUNTRY DROPDOWN - : SizedBox( - height: 60, - child: FormBuilderDropdown( - initialValue: null, - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - items: state.countries - .map>( - (Country country) { - return DropdownMenuItem( - value: country, - child: FittedBox( - child: Text(country.name!))); - }).toList(), - name: 'country', - decoration: normalTextFieldStyle( - "Country*", "Country"), - onChanged: (Country? value) { - selectedCountry = value; - }, - ), - ), - ), - ], - ); - }), - Expanded( - child: SizedBox( - height: blockSizeVertical * 8, - )), - SizedBox( - width: double.infinity, - height: 60, - child: ElevatedButton( - style: mainBtnStyle( - primary, Colors.transparent, second), - onPressed: () { - if (formKey.currentState!.saveAndValidate()) { - Country country = selectedCountry ??= - Country( - id: 175, - name: 'Philippines', - code: 'PH'); - Address address = Address( - barangay: null, - id: null, - addressCategory: null, - addressClass: null, - cityMunicipality: selectedMunicipality, - country: country); - if (selectedCategoty != null) { - selectedAgency = Agency( - id: selectedAgency?.id, - name: selectedAgency!.name, - category: selectedCategoty, - privateEntity: isPrivate); - } - VoluntaryWork work = VoluntaryWork( - ////address - address: address, - //// agency - agency: selectedAgency, - //// - position: selectedPosition, - ////total hours - totalHours: double.parse(formKey - .currentState!.value["total_hours"]), - ////to date - toDate: toDateController.text.isEmpty || - toDateController.text.toUpperCase() == - "PRESENT" - ? null - : DateTime.parse(toDateController.text), - ////from date - fromDate: - DateTime.parse(fromDateController.text), - ); - final progress = ProgressHUD.of(context); - progress!.showWithText("Loading..."); - context.read().add( - AddVoluntaryWork( - profileId: widget.profileId, - token: widget.token, - work: work)); + ), + ], + ); + }), + ], + ), + ), + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: + mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState!.saveAndValidate()) { + Country country = selectedCountry ??= Country( + id: 175, name: 'Philippines', code: 'PH'); + Address address = Address( + barangay: null, + id: null, + addressCategory: null, + addressClass: null, + cityMunicipality: selectedMunicipality, + country: country); + if (selectedCategoty != null) { + selectedAgency = Agency( + id: selectedAgency?.id, + name: selectedAgency!.name, + category: selectedCategoty, + privateEntity: isPrivate); } - }, - child: const Text(submit)), - ) - ], - )), - ), + VoluntaryWork work = VoluntaryWork( + ////address + address: address, + //// agency + agency: selectedAgency, + //// + position: selectedPosition, + ////total hours + totalHours: double.parse( + formKey.currentState!.value["total_hours"]), + ////to date + toDate: toDateController.text.isEmpty || + toDateController.text.toUpperCase() == + "PRESENT" + ? null + : DateTime.parse(toDateController.text), + ////from date + fromDate: + DateTime.parse(fromDateController.text), + ); + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add( + AddVoluntaryWork( + profileId: widget.profileId, + token: widget.token, + work: work)); + } + }, + child: const Text(submit)), + ) + ], + )), ); } return const Placeholder(); diff --git a/lib/screens/profile/components/voluntary_works/edit_modal.dart b/lib/screens/profile/components/voluntary_works/edit_modal.dart index 3ad39ce..4ea1a6a 100644 --- a/lib/screens/profile/components/voluntary_works/edit_modal.dart +++ b/lib/screens/profile/components/voluntary_works/edit_modal.dart @@ -1,19 +1,14 @@ import 'package:date_time_picker/date_time_picker.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; -import 'package:fluttericon/font_awesome_icons.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; import 'package:searchfield/searchfield.dart'; import 'package:unit2/bloc/profile/voluntary_works/voluntary_work_bloc.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/global_context.dart'; - -import '../../../../model/location/barangay.dart'; import '../../../../model/location/city.dart'; import '../../../../model/location/country.dart'; import '../../../../model/location/provinces.dart'; @@ -26,6 +21,7 @@ import '../../../../theme-data.dart/box_shadow.dart'; import '../../../../theme-data.dart/btn-style.dart'; import '../../../../theme-data.dart/colors.dart'; import '../../../../theme-data.dart/form-style.dart'; +import '../../../../utils/formatters.dart'; import '../../../../utils/location_utilities.dart'; import '../../../../utils/text_container.dart'; import '../../shared/add_for_empty_search.dart'; @@ -62,6 +58,8 @@ class _EditVoluntaryWorkScreenState extends State { bool provinceCall = false; bool cityCall = false; bool isPrivate = false; + DateTime? from; + DateTime? to; ////Lists List? provinces; List? citymuns; @@ -74,6 +72,20 @@ class _EditVoluntaryWorkScreenState extends State { Province? selectedProvince; CityMunicipality? selectedMunicipality; Country? selectedCountry; + @override + void dispose() { + addPositionController.dispose(); + addAgencyController.dispose(); + toDateController.dispose(); + fromDateController.dispose(); + positionController.dispose(); + agencyController.dispose(); + positionFocusNode.dispose(); + agencyFocusNode.dispose(); + agencyCategoryFocusNode.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return BlocBuilder( @@ -98,473 +110,502 @@ class _EditVoluntaryWorkScreenState extends State { selectedRegion = state.currentRegion; selectedProvince = state.currentProvince; selectedMunicipality = state.currentCity; + from = state.work.fromDate; + to = state.work.toDate; return Center( child: Padding( - padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 18), + padding: const EdgeInsets.symmetric(vertical: 32, horizontal: 24), child: FormBuilder( key: formKey, - child: ListView( + child: Column( children: [ - ////POSITIONS - StatefulBuilder(builder: (context, setState) { - return SearchField( - controller: positionController, - itemHeight: 50, - suggestionsDecoration: box1(), - suggestions: state.positions - .map((Position position) => SearchFieldListItem( - position.title!, - item: position, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10), - child: ListTile( - title: Text(position.title!), - )))) - .toList(), - focusNode: positionFocusNode, - searchInputDecoration: - normalTextFieldStyle("Position *", "").copyWith( - suffixIcon: GestureDetector( - child: const Icon(Icons.arrow_drop_down), - onTap: () => positionFocusNode.unfocus(), - )), - onSuggestionTap: (position) { - setState(() { - selectedPosition = position.item; - positionFocusNode.unfocus(); - }); - }, - ////EMPTY WIDGET - emptyWidget: EmptyWidget( - title: "Add Position", - controller: addPositionController, - onpressed: () { - setState(() { - Position newAgencyPosition = Position( - id: null, - title: addPositionController.text - .toUpperCase()); - - state.positions.insert(0, newAgencyPosition); - - addPositionController.text = ""; - Navigator.pop(context); - }); - }), - validator: (position) { - if (position!.isEmpty) { - return "This field is required"; - } - return null; - }, - ); - }), - const SizedBox( - height: 12, - ), - ////AGENCY - StatefulBuilder(builder: (context, setState) { - return Column( + Flexible( + child: ListView( children: [ - SearchField( - controller: agencyController, + ////POSITIONS + StatefulBuilder(builder: (context, setState) { + return SearchField( + inputFormatters: [UpperCaseTextFormatter()], + controller: positionController, itemHeight: 70, - focusNode: agencyFocusNode, - suggestions: state.agencies - .map((Agency agency) => - SearchFieldListItem(agency.name!, - item: agency, - child: ListTile( - title: Text( - agency.name!, - overflow: TextOverflow.ellipsis, - ), - subtitle: Text( - agency.privateEntity == true - ? "Private" - : agency.privateEntity == - false - ? "Government" - : ""), - ))) + suggestionsDecoration: box1(), + suggestions: state.positions + .map((Position position) => + SearchFieldListItem(position.title!, + item: position, + child: Padding( + padding: + const EdgeInsets.symmetric( + horizontal: 10), + child: ListTile( + title: Text( + position.title!, + overflow: + TextOverflow.visible, + ), + )))) .toList(), + focusNode: positionFocusNode, searchInputDecoration: - normalTextFieldStyle("Agency *", "") + normalTextFieldStyle("Position *", "") .copyWith( suffixIcon: GestureDetector( - child: const Icon( - Icons.arrow_drop_down, - ), - onTap: () => agencyFocusNode.unfocus(), + child: const Icon(Icons.arrow_drop_down), + onTap: () => positionFocusNode.unfocus(), )), - ////SELETECTED - onSuggestionTap: (agency) { + onSuggestionTap: (position) { setState(() { - selectedAgency = agency.item; - if (selectedAgency!.privateEntity == null) { - showAgency = true; - showIsPrivateRadio = true; - } else { - showAgency = false; - showIsPrivateRadio = false; - } - agencyFocusNode.unfocus(); + selectedPosition = position.item; + positionFocusNode.unfocus(); }); }, - validator: (agency) { - if (agency!.isEmpty) { + ////EMPTY WIDGET + emptyWidget: EmptyWidget( + title: "Add Position", + controller: addPositionController, + onpressed: () { + setState(() { + Position newAgencyPosition = Position( + id: null, + title: addPositionController.text + .toUpperCase()); + + state.positions + .insert(0, newAgencyPosition); + + addPositionController.text = ""; + Navigator.pop(context); + }); + }), + validator: (position) { + if (position!.isEmpty) { return "This field is required"; } return null; }, - emptyWidget: EmptyWidget( - controller: addAgencyController, - onpressed: () { - setState(() { - Agency newAgency = Agency( - id: null, - name: addAgencyController.text - .toUpperCase(), - category: null, - privateEntity: null); - state.agencies.insert(0, newAgency); - - addAgencyController.text = ""; - Navigator.pop(context); - }); - }, - title: "Add Agency")), - - SizedBox( - height: showAgency ? 12 : 0, + ); + }), + const SizedBox( + height: 12, ), - ////SHOW CATEGORY AGENCY - SizedBox( - child: showAgency - ? SearchField( - focusNode: agencyCategoryFocusNode, - itemHeight: 70, - suggestions: state.agencyCategory - .map((Category category) => - SearchFieldListItem( - category.name!, - item: category, + ////AGENCY + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + SearchField( + inputFormatters: [ + UpperCaseTextFormatter() + ], + controller: agencyController, + itemHeight: 100, + focusNode: agencyFocusNode, + suggestions: state.agencies + .map((Agency agency) => + SearchFieldListItem(agency.name!, + item: agency, child: ListTile( - title: Text(category.name!), - subtitle: Text(category - .industryClass!.name!), + title: Text( + agency.name!, + overflow: + TextOverflow.visible, + ), + subtitle: Text(agency + .privateEntity == + true + ? "Private" + : agency.privateEntity == + false + ? "Government" + : ""), ))) .toList(), - emptyWidget: Container( - height: 100, - decoration: box1(), - child: const Center( - child: Text("No result found ...")), - ), - onSuggestionTap: (agencyCategory) { + searchInputDecoration: + normalTextFieldStyle("Agency *", "") + .copyWith( + suffixIcon: GestureDetector( + child: const Icon( + Icons.arrow_drop_down, + ), + onTap: () => agencyFocusNode.unfocus(), + )), + ////SELETECTED + onSuggestionTap: (agency) { setState(() { - selectedCategoty = - agencyCategory.item; - agencyCategoryFocusNode.unfocus(); + selectedAgency = agency.item; + if (selectedAgency!.privateEntity == + null) { + showAgency = true; + showIsPrivateRadio = true; + } else { + showAgency = false; + showIsPrivateRadio = false; + } + agencyFocusNode.unfocus(); }); }, - searchInputDecoration: - normalTextFieldStyle("Category *", "") - .copyWith( - suffixIcon: const Icon( - Icons.arrow_drop_down)), - validator: (value) { - if (value!.isEmpty) { + validator: (agency) { + if (agency!.isEmpty) { return "This field is required"; } return null; }, - ) - : const SizedBox(), - ), - SizedBox( - height: showIsPrivateRadio ? 12 : 0, - ), - ////PRVIATE SECTOR - SizedBox( - child: showIsPrivateRadio - ? FormBuilderSwitch( - initialValue: false, - title: Text(isPrivate ? "YES" : "NO"), - decoration: normalTextFieldStyle( - "Private Entity?", - 'Private Entity?'), + emptyWidget: EmptyWidget( + controller: addAgencyController, + onpressed: () { + setState(() { + Agency newAgency = Agency( + id: null, + name: addAgencyController.text + .toUpperCase(), + category: null, + privateEntity: null); + state.agencies + .insert(0, newAgency); - ////onvhange private sector - onChanged: (value) { - setState(() { - isPrivate = value!; - agencyCategoryFocusNode.unfocus(); - }); - }, + addAgencyController.text = ""; + Navigator.pop(context); + }); + }, + title: "Add Agency")), - name: 'isPrivate', - validator: - FormBuilderValidators.required(), - ) - : const SizedBox()), - const SizedBox( - height: 12, - ), - //// total hours - FormBuilderTextField( - initialValue: state.work.totalHours.toString(), - validator: FormBuilderValidators.required( - errorText: "This Field is required"), - name: "total_hours", - keyboardType: TextInputType.number, - decoration: - normalTextFieldStyle("Total Hours*", "0"), - ), - const SizedBox( - height: 12, - ), - ////Currently Involved - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - FormBuilderSwitch( - initialValue: currentlyInvolved, - activeColor: second, - onChanged: (value) { - setState(() { - currentlyInvolved = value!; - }); - }, + SizedBox( + height: showAgency ? 12 : 0, + ), + ////SHOW CATEGORY AGENCY + SizedBox( + child: showAgency + ? SearchField( + focusNode: agencyCategoryFocusNode, + itemHeight: 70, + suggestions: state.agencyCategory + .map((Category category) => + SearchFieldListItem( + category.name!, + item: category, + child: ListTile( + title: Text( + category.name!), + subtitle: Text(category + .industryClass! + .name!), + ))) + .toList(), + emptyWidget: Container( + height: 100, + decoration: box1(), + child: const Center( + child: Text( + "No result found ...")), + ), + onSuggestionTap: (agencyCategory) { + setState(() { + selectedCategoty = + agencyCategory.item; + agencyCategoryFocusNode + .unfocus(); + }); + }, + searchInputDecoration: + normalTextFieldStyle( + "Category *", "") + .copyWith( + suffixIcon: IconButton( + icon: const Icon( + Icons.arrow_drop_down), + onPressed: () { + agencyCategoryFocusNode + .unfocus(); + }, + )), + validator: (value) { + if (value!.isEmpty) { + return "This field is required"; + } + return null; + }, + ) + : const SizedBox(), + ), + SizedBox( + height: showIsPrivateRadio ? 12 : 0, + ), + ////PRVIATE SECTOR + SizedBox( + child: showIsPrivateRadio + ? FormBuilderSwitch( + initialValue: false, + title: Text( + isPrivate ? "YES" : "NO"), + decoration: normalTextFieldStyle( + "Private Entity?", + 'Private Entity?'), + + ////onvhange private sector + onChanged: (value) { + setState(() { + isPrivate = value!; + agencyCategoryFocusNode + .unfocus(); + }); + }, + + name: 'isPrivate', + validator: FormBuilderValidators + .required(), + ) + : const SizedBox()), + const SizedBox( + height: 12, + ), + //// total hours + FormBuilderTextField( + initialValue: + state.work.totalHours.toString(), + validator: FormBuilderValidators.required( + errorText: "This Field is required"), + name: "total_hours", + keyboardType: TextInputType.number, decoration: normalTextFieldStyle( - "Currently Involved?", 'Graduated?'), - name: 'currently_involved', - title: - Text(currentlyInvolved ? "YES" : "NO"), + "Total Hours*", "0"), ), const SizedBox( height: 12, ), - SizedBox( - width: screenWidth, - child: Row( + ////Currently Involved + StatefulBuilder(builder: (context, setState) { + return Column( children: [ - //// FROM DATE - Flexible( - flex: 1, - child: DateTimePicker( - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - use24HourFormat: false, - icon: - const Icon(Icons.date_range), - controller: fromDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), - timeHintText: - "Date of Examination/Conferment", - decoration: normalTextFieldStyle( - "From *", "From *") - .copyWith( - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - )), - initialValue: null, - )), - const SizedBox( - width: 12, + FormBuilderSwitch( + initialValue: currentlyInvolved, + activeColor: second, + onChanged: (value) { + setState(() { + currentlyInvolved = value!; + }); + }, + decoration: normalTextFieldStyle( + "Currently Involved?", + 'Graduated?'), + name: 'currently_involved', + title: Text( + currentlyInvolved ? "YES" : "NO"), ), - //// TO DATE - Flexible( - flex: 1, - child: currentlyInvolved - ? TextFormField( - enabled: false, - initialValue: "PRESENT", - style: const TextStyle( - color: Colors.black45), - decoration: - normalTextFieldStyle( - "", "") - .copyWith(), - ) - : DateTimePicker( - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - controller: toDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), - decoration: normalTextFieldStyle( - "To *", "To *") - .copyWith( - prefixIcon: - const Icon( - Icons.date_range, - color: - Colors.black87, - ), - prefixText: - currentlyInvolved - ? "PRESENT" - : ""), - initialValue: null, - ), - ), - ], - ), - ), - ], - ); - }), - ], - ); - }), - const SizedBox( - height: 12, - ), - //// OVERSEAS - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - setState(() { - overseas = value!; - }); - }, - decoration: - normalTextFieldStyle("Overseas Address?", ''), - name: 'overseas', - title: Text(overseas ? "YES" : "NO"), - ), - SizedBox( - height: overseas == true ? 8 : 0, - ), - SizedBox( - child: overseas == false - ? Column( - children: [ const SizedBox( height: 12, ), - ////REGION DROPDOWN - DropdownButtonFormField( - isExpanded: true, - autovalidateMode: AutovalidateMode - .onUserInteraction, - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - onChanged: (Region? region) async { - if (selectedRegion != region) { - setState(() { - provinceCall = true; - }); - selectedRegion = region; - try { - provinces = await LocationUtils - .instance - .getProvinces( - regionCode: - selectedRegion!.code - .toString()); - selectedProvince = - provinces![0]; - setState(() { - provinceCall = false; - cityCall = true; - }); - try { - citymuns = await LocationUtils - .instance - .getCities( - code: - selectedProvince! - .code - .toString()); - selectedMunicipality = - citymuns![0]; - setState(() { - cityCall = false; - }); - } catch (e) { - NavigationService.navigatorKey - .currentContext - ?.read< - VoluntaryWorkBloc>() - .add(ShowErrorState( - message: - e.toString())); - } - } catch (e) { - context - .read() - .add(ShowErrorState( - message: e.toString())); - } - } - }, - value: selectedRegion, - decoration: normalTextFieldStyle( - "Region*", "Region"), - items: state.regions - .map>( - (Region region) { - return DropdownMenuItem( - value: region, - child: - Text(region.description!)); - }).toList(), - ), - const SizedBox( - height: 8, - ), - //// PROVINCE DROPDOWN SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: provinceCall, - child: DropdownButtonFormField< - Province?>( + width: screenWidth, + child: Row( + children: [ + //// FROM DATE + Flexible( + flex: 1, + child: DateTimePicker( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + use24HourFormat: false, + icon: const Icon( + Icons.date_range), + controller: + fromDateController, + firstDate: DateTime(1990), + lastDate: DateTime(2100), + timeHintText: + "Date of Examination/Conferment", + decoration: + normalTextFieldStyle( + "From *", + "From *") + .copyWith( + prefixIcon: + const Icon( + Icons.date_range, + color: Colors.black87, + )), + selectableDayPredicate: + (date) { + if (to != null && + to!.microsecondsSinceEpoch <= + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, + onChanged: (value) { + setState(() { + from = DateTime.parse( + value); + }); + }, + initialDate: to == null + ? DateTime.now() + : to!.subtract( + const Duration( + days: 1)), + )), + const SizedBox( + width: 12, + ), + //// TO DATE + Flexible( + flex: 1, + child: currentlyInvolved + ? TextFormField( + enabled: false, + initialValue: "PRESENT", + style: const TextStyle( + color: + Colors.black45), + decoration: + normalTextFieldStyle( + "", "") + .copyWith(), + ) + : DateTimePicker( + validator: + FormBuilderValidators + .required( + errorText: + "This field is required"), + controller: + toDateController, + firstDate: + DateTime(1990), + lastDate: + DateTime(2100), + selectableDayPredicate: + (date) { + if (from != null && + from!.microsecondsSinceEpoch >= + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, + onChanged: (value) { + setState(() { + to = DateTime.parse( + value); + }); + }, + initialDate: from == + null + ? DateTime.now() + : from!.add( + const Duration( + days: 1)), + decoration: normalTextFieldStyle( + "To *", "To *") + .copyWith( + prefixIcon: + const Icon( + Icons + .date_range, + color: Colors + .black87, + ), + prefixText: + currentlyInvolved + ? "PRESENT" + : ""), + initialValue: null, + ), + ), + ], + ), + ), + ], + ); + }), + ], + ); + }), + const SizedBox( + height: 12, + ), + //// OVERSEAS + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: normalTextFieldStyle( + "Overseas Address?", ''), + name: 'overseas', + title: Text(overseas ? "YES" : "NO"), + ), + SizedBox( + height: overseas == true ? 12 : 0, + ), + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + DropdownButtonFormField( + isExpanded: true, autovalidateMode: AutovalidateMode .onUserInteraction, - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - value: selectedProvince, + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), onChanged: - (Province? province) async { - if (selectedProvince != - province) { + (Region? region) async { + if (selectedRegion != + region) { setState(() { - cityCall = true; + provinceCall = true; }); - selectedProvince = province; + selectedRegion = region; try { - citymuns = await LocationUtils + provinces = await LocationUtils .instance - .getCities( - code: selectedProvince! - .code - .toString()); - selectedMunicipality = - citymuns![0]; + .getProvinces( + regionCode: + selectedRegion! + .code + .toString()); + selectedProvince = + provinces![0]; setState(() { - cityCall = false; + provinceCall = false; + cityCall = true; }); + try { + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code + .toString()); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + }); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read< + VoluntaryWorkBloc>() + .add(ShowErrorState( + message: e + .toString())); + } } catch (e) { context .read< @@ -575,98 +616,178 @@ class _EditVoluntaryWorkScreenState extends State { } } }, - items: provinces == null - ? [] - : provinces!.map< - DropdownMenuItem< - Province>>( - (Province province) { - return DropdownMenuItem( - value: province, - child: FittedBox( - child: Text(province - .description!), - )); - }).toList(), + value: selectedRegion, decoration: normalTextFieldStyle( - "Province*", - "Province")), - ), - ), - ////CITY MUNICIPALITY - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: cityCall, + "Region*", "Region"), + items: state.regions.map< + DropdownMenuItem< + Region>>( + (Region region) { + return DropdownMenuItem< + Region>( + value: region, + child: Text( + region.description!)); + }).toList(), + ), + const SizedBox( + height: 12, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + value: selectedProvince, + onChanged: (Province? + province) async { + if (selectedProvince != + province) { + setState(() { + cityCall = true; + }); + selectedProvince = + province; + try { + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code + .toString()); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + }); + } catch (e) { + context + .read< + VoluntaryWorkBloc>() + .add(ShowErrorState( + message: e + .toString())); + } + } + }, + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province + province) { + return DropdownMenuItem( + value: + province, + child: + FittedBox( + child: Text( + province + .description!), + )); + }).toList(), + decoration: + normalTextFieldStyle( + "Province*", + "Province")), + ), + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: + DropdownButtonFormField< + CityMunicipality>( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + isExpanded: true, + onChanged: + (CityMunicipality? + city) { + if (selectedMunicipality != + city) { + selectedMunicipality = + city; + } + }, + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality + c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), + ), + ), + ), + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( + height: 60, child: DropdownButtonFormField< - CityMunicipality>( + Country>( + isExpanded: true, validator: FormBuilderValidators .required( errorText: "This field is required"), - isExpanded: true, - onChanged: - (CityMunicipality? city) { - if (selectedMunicipality != - city) { - selectedMunicipality = city; - } - }, + items: state.countries.map< + DropdownMenuItem< + Country>>( + (Country country) { + return DropdownMenuItem< + Country>( + value: country, + child: FittedBox( + child: Text( + country.name!))); + }).toList(), + value: selectedCountry!.id == 175 + ? null + : selectedCountry, decoration: normalTextFieldStyle( - "Municipality*", - "Municipality"), - value: selectedMunicipality, - items: citymuns == null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality c) { - return DropdownMenuItem( - value: c, - child: Text( - c.description!)); - }).toList(), + "Country*", "Country"), + onChanged: (Country? value) { + selectedCountry = value; + }, ), ), - ), - ], - ) - //// COUNTRY DROPDOWN - : SizedBox( - height: 60, - child: DropdownButtonFormField( - isExpanded: true, - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - items: state.countries - .map>( - (Country country) { - return DropdownMenuItem( - value: country, - child: FittedBox( - child: Text(country.name!))); - }).toList(), - value: selectedCountry, - decoration: normalTextFieldStyle( - "Country*", "Country"), - onChanged: (Country? value) { - selectedCountry = value; - }, - ), - ), - ), + ), + ], + ); + }), ], - ); - }), - Expanded( - child: SizedBox( - height: blockSizeVertical * 8, - )), + ), + ), SizedBox( width: double.infinity, height: 60, @@ -684,9 +805,12 @@ class _EditVoluntaryWorkScreenState extends State { addressCategory: state.work.address?.addressCategory, addressClass: - state.work.address?.addressClass , + state.work.address?.addressClass, cityMunicipality: selectedMunicipality, - country: selectedCountry); + country: Country( + code: "PH", + id: 175, + name: "PHILIPPINES")); } else { ////if overseas address = Address( diff --git a/lib/screens/profile/components/voluntary_works_screen.dart b/lib/screens/profile/components/voluntary_works_screen.dart index 20becc4..4544fe5 100644 --- a/lib/screens/profile/components/voluntary_works_screen.dart +++ b/lib/screens/profile/components/voluntary_works_screen.dart @@ -18,6 +18,7 @@ import 'package:unit2/widgets/error_state.dart'; import '../../../bloc/profile/voluntary_works/voluntary_work_bloc.dart'; import '../../../utils/alerts.dart'; +import '../../../widgets/Leadings/close_leading.dart'; class VolunataryWorkScreen extends StatelessWidget { const VolunataryWorkScreen({super.key}); @@ -29,13 +30,17 @@ class VolunataryWorkScreen extends StatelessWidget { DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); return Scaffold( appBar: AppBar( - title: const Text(voluntaryScreenTitle), + title:context.watch().state is AddVoluntaryWorkState? const FittedBox(child: Text("Add $voluntaryScreenTitle"),) :context.watch().state is EditVoluntaryWorks? const FittedBox(child: Text("Edit $voluntaryScreenTitle"),): const FittedBox(child: Text(voluntaryScreenTitle)), backgroundColor: primary, - actions: [ - AddLeading(onPressed: () { - context.read().add(ShowAddVoluntaryWorks()); - }) - ], + actions: (context.watch().state is VoluntaryWorkLoadedState)?[ + AddLeading(onPressed: () { + context.read().add(ShowAddVoluntaryWorks()); + }) + ]:(context.watch().state is AddVoluntaryWorkState || context.watch().state is EditVoluntaryWorks)?[ + CloseLeading(onPressed:() { + context.read().add(LoadVoluntaryWorks()); + }) + ]:[] ), body: ProgressHUD( padding: const EdgeInsets.all(24), @@ -112,7 +117,7 @@ class VolunataryWorkScreen extends StatelessWidget { if (state is VoluntaryWorkDeletedState) { if (state.success) { successAlert(context, "Deletion Successfull!", - "Deleted Successfully", () { + "Voluntary Work Has Been Deleted Successfully", () { Navigator.of(context).pop(); context .read() @@ -174,11 +179,7 @@ class VolunataryWorkScreen extends StatelessWidget { position, style: Theme.of(context) .textTheme - .titleMedium! - .copyWith( - fontWeight: - FontWeight - .w500), + .titleMedium!.copyWith(color: primary,fontWeight: FontWeight.w600), ), const SizedBox( height: 5, @@ -187,19 +188,19 @@ class VolunataryWorkScreen extends StatelessWidget { agency, style: Theme.of(context) .textTheme - .titleSmall, + .titleSmall ), - const Divider(), + const SizedBox( - height: 3, + height: 8, ), Text( - "$duration : $from to $to"), + "$duration : $from to $to",style: Theme.of(context).textTheme.labelMedium!.copyWith(fontWeight: FontWeight.w500),), const SizedBox( height: 5, ), Text( - "$numberOfHours : $hours hours"), + "$numberOfHours : $hours hours",style: Theme.of(context).textTheme.labelMedium!.copyWith(fontWeight: FontWeight.w500)), ]), ), AppPopupMenu( @@ -262,17 +263,14 @@ class VolunataryWorkScreen extends StatelessWidget { }, menuItems: [ popMenuItem( - text: "Edit", + text: "Update", value: 1, icon: Icons.edit), popMenuItem( - text: "Delete", - value: 2, - icon: Icons.delete), - popMenuItem( - text: "Attachment", - value: 3, - icon: FontAwesome.attach) + text: "Remove", + value: 2,) + + ], icon: const Icon( Icons.more_vert, @@ -303,7 +301,9 @@ class VolunataryWorkScreen extends StatelessWidget { } if (state is VoluntaryWorkErrorState) { return SomethingWentWrong( - message: state.toString(), onpressed: () {}); + message: state.toString(), onpressed: () { + context.read().add(GetVoluntarWorks(profileId: profileId!, token: token!)); + }); } if (state is EditVoluntaryWorks) { return EditVoluntaryWorkScreen( diff --git a/lib/screens/profile/components/work_history/add_modal.dart b/lib/screens/profile/components/work_history/add_modal.dart index 6db6239..9f16525 100644 --- a/lib/screens/profile/components/work_history/add_modal.dart +++ b/lib/screens/profile/components/work_history/add_modal.dart @@ -6,9 +6,7 @@ import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:fluttericon/font_awesome_icons.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:searchfield/searchfield.dart'; -import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/profile/workHistory/workHistory_bloc.dart'; -import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/work_history.dart'; import 'package:unit2/model/utils/agency.dart'; import 'package:unit2/model/utils/agency_position.dart'; @@ -20,8 +18,8 @@ import 'package:unit2/theme-data.dart/form-style.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/utils/validators.dart'; - import '../../../../model/utils/position.dart'; +import '../../../../utils/formatters.dart'; import '../../shared/add_for_empty_search.dart'; class AddWorkHistoryScreen extends StatefulWidget { @@ -56,6 +54,8 @@ class _AddWorkHistoryScreenState extends State { final positionFocusNode = FocusNode(); final appointmentStatusNode = FocusNode(); final agencyCategoryFocusNode = FocusNode(); + DateTime? from; + DateTime? to; @override void dispose() { addPositionController.dispose(); @@ -75,512 +75,599 @@ class _AddWorkHistoryScreenState extends State { } }, builder: (context, state) { if (state is AddWorkHistoryState) { - return SingleChildScrollView( + return FormBuilder( + key: _formKey, child: SizedBox( height: blockSizeVertical * 90, - child: FormBuilder( - key: _formKey, - child: Padding( - padding: - const EdgeInsets.symmetric(vertical: 25, horizontal: 18), - child: Column( - children: [ - ////POSITIONS - StatefulBuilder(builder: (context, setState) { - return SearchField( - itemHeight: 50, - suggestionsDecoration: box1(), - suggestions: state.agencyPositions - .map((Position position) => SearchFieldListItem( - position.title!, - item: position, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10), - child: ListTile( - title: Text(position.title!), - )))) - .toList(), - focusNode: positionFocusNode, - searchInputDecoration: - normalTextFieldStyle("Position *", "").copyWith( - suffixIcon: const Icon(Icons.arrow_drop_down)), - onSuggestionTap: (position) { - setState(() { - selectedPosition = position.item; - positionFocusNode.unfocus(); - }); - }, - ////EMPTY WIDGET - emptyWidget: EmptyWidget( - title: "Add Position", - controller: addPositionController, - onpressed: () { + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 32, horizontal: 28), + child: Column( + children: [ + Flexible( + child: ListView( + children: [ + ////POSITIONS + StatefulBuilder(builder: (context, setState) { + return SearchField( + inputFormatters: [UpperCaseTextFormatter()], + itemHeight: 100, + suggestionsDecoration: box1(), + suggestions: state.agencyPositions + .map((Position position) => SearchFieldListItem( + position.title!, + item: position, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10), + child: ListTile( + title: Text( + position.title!, + overflow: TextOverflow.visible, + ), + )))) + .toList(), + focusNode: positionFocusNode, + searchInputDecoration: + normalTextFieldStyle("Position *", "").copyWith( + suffixIcon: IconButton( + icon: const Icon(Icons.arrow_drop_down), + onPressed: () { + positionFocusNode.unfocus(); + }, + )), + onSuggestionTap: (position) { setState(() { - Position newAgencyPosition = Position( - id: null, - title: addPositionController.text - .toUpperCase()); - - state.agencyPositions - .insert(0, newAgencyPosition); - selectedPosition = newAgencyPosition; - addPositionController.text = ""; - Navigator.pop(context); + selectedPosition = position.item; + positionFocusNode.unfocus(); }); - }), - validator: (position) { - if (position!.isEmpty) { - return "This field is required"; - } - return null; - }, - ); - }), - const SizedBox( - height: 12, - ), - ////APPOINTMENT STATUS' - SearchField( - suggestions: state.appointmentStatus - .map((AppoinemtStatus status) => - SearchFieldListItem(status.label, item: status)) - .toList(), - focusNode: appointmentStatusNode, - validator: (value) { - if (value!.isEmpty) { - return "This field is required"; - } - return null; - }, - onSuggestionTap: (status) { - selectedStatus = status.item; - appointmentStatusNode.unfocus(); - }, - searchInputDecoration: normalTextFieldStyle( - "Appointment Status", "") - .copyWith( - suffixIcon: const Icon(Icons.arrow_drop_down)), - ), + }, + ////EMPTY WIDGET + emptyWidget: EmptyWidget( + title: "Add Position", + controller: addPositionController, + onpressed: () { + setState(() { + Position newAgencyPosition = Position( + id: null, + title: addPositionController.text + .toUpperCase()); - const SizedBox( - height: 12, - ), - - ////AGENCY - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - SearchField( - itemHeight: 70, - focusNode: agencyFocusNode, - suggestions: state.agencies - .map((Agency agency) => SearchFieldListItem( - agency.name!, - item: agency, - child: ListTile( - title: Text( - agency.name!, - overflow: TextOverflow.ellipsis, - ), - subtitle: - Text(agency.privateEntity == true - ? "Private" - : agency.privateEntity == false - ? "Government" - : ""), - ))) - .toList(), - searchInputDecoration: - normalTextFieldStyle("Agency *", "").copyWith( - suffixIcon: - const Icon(Icons.arrow_drop_down)), - onSuggestionTap: (agency) { - setState(() { - selectedAgency = agency.item; - - if (selectedAgency!.privateEntity == null) { - showIsPrivateRadio = true; - } else { - showIsPrivateRadio = false; - } - if (selectedAgency!.privateEntity == true) { - showSalaryGradeAndSalaryStep = false; - } - if (selectedAgency!.privateEntity == false) { - showSalaryGradeAndSalaryStep = true; - } - agencyFocusNode.unfocus(); - }); + state.agencyPositions + .insert(0, newAgencyPosition); + selectedPosition = newAgencyPosition; + addPositionController.text = ""; + Navigator.pop(context); + }); + }), + validator: (position) { + if (position!.isEmpty) { + return "This field is required"; + } + return null; + }, + ); + }), + const SizedBox( + height: 12, + ), + ////APPOINTMENT STATUS' + SearchField( + inputFormatters: [UpperCaseTextFormatter()], + suggestions: state.appointmentStatus + .map((AppoinemtStatus status) => + SearchFieldListItem(status.label, + item: status, + child: Padding( + padding: const EdgeInsets.all(8), + child: Text(status.label)))) + .toList(), + focusNode: appointmentStatusNode, + validator: (value) { + if (value!.isEmpty) { + return "This field is required"; + } + return null; + }, + onSuggestionTap: (status) { + selectedStatus = status.item; + + appointmentStatusNode.unfocus(); + }, + searchInputDecoration: + normalTextFieldStyle("Appointment Status", "") + .copyWith( + suffixIcon: IconButton( + icon: const Icon(Icons.arrow_drop_down), + onPressed: () { + appointmentStatusNode.unfocus(); }, - validator: (agency) { - if (agency!.isEmpty) { - return "This field is required"; - } - return null; - }, - emptyWidget: EmptyWidget( - controller: addAgencyController, - onpressed: () { + ))), + + const SizedBox( + height: 12, + ), + ////AGENCY + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + SearchField( + inputFormatters: [UpperCaseTextFormatter()], + itemHeight: 100, + focusNode: agencyFocusNode, + suggestions: state.agencies + .map((Agency agency) => + SearchFieldListItem(agency.name!, + item: agency, + child: ListTile( + title: Text( + agency.name!, + overflow: + TextOverflow.visible, + ), + subtitle: Text(agency + .privateEntity == + true + ? "Private" + : agency.privateEntity == + false + ? "Government" + : ""), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle("Agency *", "") + .copyWith( + suffixIcon: IconButton( + icon: const Icon(Icons.arrow_drop_down), + onPressed: () { + agencyFocusNode.unfocus(); + }, + )), + onSuggestionTap: (agency) { setState(() { - Agency newAgency = Agency( - id: null, - name: addAgencyController.text - .toUpperCase(), - category: null, - privateEntity: null); + selectedAgency = agency.item; - state.agencies.insert(0, newAgency); - selectedAgency = newAgency; - addAgencyController.text = ""; - showAgency = true; - - showIsPrivateRadio = true; - - Navigator.pop(context); + if (selectedAgency!.privateEntity == + null) { + showIsPrivateRadio = true; + } else { + showIsPrivateRadio = false; + } + if (selectedAgency!.privateEntity == + true) { + showSalaryGradeAndSalaryStep = false; + } + if (selectedAgency!.privateEntity == + false) { + showSalaryGradeAndSalaryStep = true; + } + agencyFocusNode.unfocus(); }); }, - title: "Add Agency")), - - SizedBox( - height: showAgency ? 12 : 0, - ), - ////SHOW CATEGORY AGENCY - SizedBox( - child: showAgency - ? SearchField( - focusNode: agencyCategoryFocusNode, - itemHeight: 70, - suggestions: state.agencyCategory - .map((Category category) => - SearchFieldListItem(category.name!, - item: category, - child: ListTile( - title: Text(category.name!), - subtitle: Text(category - .industryClass!.name!), - ))) - .toList(), - emptyWidget: Container( - height: 100, - decoration: box1(), - child: const Center( - child: Text("No result found ...")), - ), - onSuggestionTap: (agencyCategory) { - setState(() { - selectedAgencyCategory = - agencyCategory.item; - agencyCategoryFocusNode.unfocus(); - selectedAgency = Agency( - id: null, - name: selectedAgency!.name, - category: selectedAgencyCategory, - privateEntity: null); - }); - }, - searchInputDecoration: - normalTextFieldStyle("Category *", "") - .copyWith( - suffixIcon: const Icon( - Icons.arrow_drop_down)), - validator: (value) { - if (value!.isEmpty) { - return "This field is required"; - } - return null; - }, - ) - : const SizedBox(), - ), - - ////PRVIATE SECTOR - SizedBox( - child: showIsPrivateRadio - ? FormBuilderRadioGroup( - decoration: InputDecoration( - border: InputBorder.none, - label: Row( - children: [ - Text( - "Is this private sector? ", - style: Theme.of(context) - .textTheme - .headlineSmall! - .copyWith(fontSize: 24), - ), - const Icon(FontAwesome.help_circled) - ], - ), - ), - - ////onvhange private sector - onChanged: (value) { + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + emptyWidget: EmptyWidget( + controller: addAgencyController, + onpressed: () { setState(() { - if (value.toString() == "YES") { - isPrivate = true; - showSalaryGradeAndSalaryStep = - false; - } else { - isPrivate = false; - showSalaryGradeAndSalaryStep = true; - } - selectedAgency = Agency( + Agency newAgency = Agency( id: null, - name: selectedAgency!.name, - category: selectedAgencyCategory, - privateEntity: value == "YES" - ? true - : false); - agencyFocusNode.unfocus(); - agencyCategoryFocusNode.unfocus(); + name: addAgencyController.text + .toUpperCase(), + category: null, + privateEntity: null); + + state.agencies.insert(0, newAgency); + selectedAgency = newAgency; + addAgencyController.text = ""; + showAgency = true; + + showIsPrivateRadio = true; + + Navigator.pop(context); }); }, + title: "Add Agency")), - name: 'isPrivate', - validator: - FormBuilderValidators.required(), - options: ["YES", "NO"] - .map((lang) => FormBuilderFieldOption( - value: lang)) - .toList(growable: false), - ) - : const SizedBox()), - ////SALARY GRADE AND SALARY GRADE STEP - SizedBox( - child: showSalaryGradeAndSalaryStep - ? Column( - children: [ - Row( + SizedBox( + height: showAgency ? 12 : 0, + ), + ////SHOW CATEGORY AGENCY + SizedBox( + child: showAgency + ? SearchField( + focusNode: agencyCategoryFocusNode, + itemHeight: 70, + suggestions: state.agencyCategory + .map((Category category) => + SearchFieldListItem( + category.name!, + item: category, + child: ListTile( + title: + Text(category.name!), + subtitle: Text(category + .industryClass! + .name!), + ))) + .toList(), + emptyWidget: Container( + height: 100, + decoration: box1(), + child: const Center( + child: + Text("No result found ...")), + ), + onSuggestionTap: (agencyCategory) { + setState(() { + selectedAgencyCategory = + agencyCategory.item; + agencyCategoryFocusNode.unfocus(); + selectedAgency = Agency( + id: null, + name: selectedAgency!.name, + category: + selectedAgencyCategory, + privateEntity: null); + }); + }, + searchInputDecoration: + normalTextFieldStyle( + "Category *", "") + .copyWith( + suffixIcon: IconButton( + icon: + const Icon(Icons.arrow_drop_down), + onPressed: () { + agencyCategoryFocusNode.unfocus(); + }, + )), + validator: (value) { + if (value!.isEmpty) { + return "This field is required"; + } + return null; + }, + ) + : const SizedBox(), + ), + + ////PRVIATE SECTOR + SizedBox( + child: showIsPrivateRadio + ? FormBuilderRadioGroup( + decoration: InputDecoration( + border: InputBorder.none, + label: Row( + children: [ + Text( + "Is this private sector? ", + style: Theme.of(context) + .textTheme + .headlineSmall! + .copyWith(fontSize: 24), + ), + const Icon( + FontAwesome.help_circled) + ], + ), + ), + + ////onvhange private sector + onChanged: (value) { + setState(() { + if (value.toString() == "YES") { + isPrivate = true; + showSalaryGradeAndSalaryStep = + false; + } else { + isPrivate = false; + showSalaryGradeAndSalaryStep = + true; + } + selectedAgency = Agency( + id: null, + name: selectedAgency!.name, + category: + selectedAgencyCategory, + privateEntity: value == "YES" + ? true + : false); + agencyFocusNode.unfocus(); + agencyCategoryFocusNode.unfocus(); + }); + }, + + name: 'isPrivate', + validator: + FormBuilderValidators.required(), + options: ["YES", "NO"] + .map((lang) => + FormBuilderFieldOption( + value: lang)) + .toList(growable: false), + ) + : const SizedBox()), + SizedBox( + height: showSalaryGradeAndSalaryStep ? 12 : 0, + ), + ////SALARY GRADE AND SALARY GRADE STEP + SizedBox( + child: showSalaryGradeAndSalaryStep + ? Column( children: [ - Flexible( - flex: 1, - child: FormBuilderTextField( - name: 'salary_grade', - keyboardType: - TextInputType.number, - decoration: - normalTextFieldStyle( - "Salary Grade (SG)", - "0"), - onChanged: (value) { - salaryGrade = value; - }, - validator: FormBuilderValidators - .compose([ - FormBuilderValidators.integer( - radix: 10, - errorText: - "Please enter a number"), - FormBuilderValidators.numeric( - errorText: - "Please enter a number") - ]), - autovalidateMode: - AutovalidateMode - .onUserInteraction, - ), - ), - const SizedBox( - width: 12, - ), - Flexible( - flex: 1, - child: FormBuilderTextField( - name: 'salary_step', - keyboardType: - TextInputType.number, - decoration: - normalTextFieldStyle( - "SG Step (SG)", "0"), - validator: FormBuilderValidators - .compose([ - FormBuilderValidators.integer( - radix: 10, - errorText: - "Please enter a number"), - FormBuilderValidators.numeric( - errorText: - "Please enter a number") - ]), - autovalidateMode: - AutovalidateMode - .onUserInteraction, - onChanged: (value) { - setState(() { - salaryGradeStep = value; - }); - }, - ), + Row( + children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + name: 'salary_grade', + keyboardType: + TextInputType.number, + decoration: + normalTextFieldStyle( + "Salary Grade (SG)", + "0"), + onChanged: (value) { + salaryGrade = value; + }, + validator: + FormBuilderValidators + .compose([ + FormBuilderValidators.integer( + radix: 10, + errorText: + "Please enter a number"), + FormBuilderValidators.numeric( + errorText: + "Please enter a number") + ]), + autovalidateMode: + AutovalidateMode + .onUserInteraction, + ), + ), + const SizedBox( + width: 12, + ), + Flexible( + flex: 1, + child: FormBuilderTextField( + name: 'salary_step', + keyboardType: + TextInputType.number, + decoration: + normalTextFieldStyle( + "SG Step (SG)", + "0"), + validator: + FormBuilderValidators + .compose([ + FormBuilderValidators.integer( + radix: 10, + errorText: + "Please enter a number"), + FormBuilderValidators.numeric( + errorText: + "Please enter a number") + ]), + autovalidateMode: + AutovalidateMode + .onUserInteraction, + onChanged: (value) { + setState(() { + salaryGradeStep = value; + }); + }, + ), + ) + ], ) ], ) - ], - ) - : null), - ], - ); - }), - const SizedBox( - height: 12, - ), - ////MONTHLY SALARY - FormBuilderTextField( - onChanged: (value) { - setState(() { - salary = value; - }); - }, - validator: numericRequired, - name: "salary", - decoration: normalTextFieldStyle("Monthly Salary *", "") - .copyWith(prefix: const Text("₱ ")), - ), + : null), + ], + ); + }), + const SizedBox( + height: 12, + ), + ////MONTHLY SALARY + FormBuilderTextField( + onChanged: (value) { + setState(() { + salary = value; + }); + }, + validator: numericRequired, + name: "salary", + decoration: + normalTextFieldStyle("Monthly Salary *", "") + .copyWith(prefix: const Text("₱ ")), + ), - const SizedBox( - height: 12, - ), - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - ////CURRENTLY EMPLOYED - FormBuilderSwitch( - initialValue: currentlyEmployed, - activeColor: second, - onChanged: (value) { - setState(() { - if (value == true) { - currentlyEmployed = true; - toDateController.text = "PRESENT"; - } else { - currentlyEmployed = false; - toDateController.text = ""; - } - }); - }, - decoration: normalTextFieldStyle("", ''), - name: 'overseas', - title: const Text("Currently Employed?"), - ), - const SizedBox( - height: 12, - ), - SizedBox( - width: screenWidth, - child: Row( - children: [ - //// FROM DATE - Flexible( - flex: 1, - child: DateTimePicker( - validator: FormBuilderValidators.required( - errorText: "This field is required"), - use24HourFormat: false, - icon: const Icon(Icons.date_range), - controller: fromDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), - timeHintText: - "Date of Examination/Conferment", - decoration: normalTextFieldStyle( - "From *", "From *") - .copyWith( - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - )), - initialValue: null, - )), - const SizedBox( - width: 12, - ), - //// TO DATE - Flexible( - flex: 1, - child: currentlyEmployed - ? TextFormField( - enabled: false, - initialValue: "PRESENT", - style: const TextStyle( - color: Colors.black45), - decoration: - normalTextFieldStyle("", "") - .copyWith(), - ) - : DateTimePicker( + const SizedBox( + height: 12, + ), + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + ////CURRENTLY EMPLOYED + FormBuilderSwitch( + initialValue: currentlyEmployed, + activeColor: second, + onChanged: (value) { + setState(() { + if (value == true) { + currentlyEmployed = true; + toDateController.text = "PRESENT"; + } else { + currentlyEmployed = false; + toDateController.text = ""; + } + }); + }, + decoration: normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Currently Employed?"), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + //// FROM DATE + Flexible( + flex: 1, + child: DateTimePicker( validator: FormBuilderValidators.required( errorText: "This field is required"), - controller: toDateController, - firstDate: DateTime(1970), + use24HourFormat: false, + icon: const Icon(Icons.date_range), + controller: fromDateController, + firstDate: DateTime(1990), lastDate: DateTime(2100), + selectableDayPredicate: (date) { + if (to != null && + to!.microsecondsSinceEpoch <= + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, + timeHintText: + "Date of Examination/Conferment", decoration: normalTextFieldStyle( - "To *", "To *") + "From *", "From *") .copyWith( prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - ), - prefixText: currentlyEmployed - ? "PRESENT" - : ""), - initialValue: null, - ), + Icons.date_range, + color: Colors.black87, + )), + onChanged: (value) { + setState(() { + from = DateTime.parse(value); + }); + }, + initialDate: to == null + ? DateTime.now() + : to!.subtract( + const Duration(days: 1)), + )), + const SizedBox( + width: 12, + ), + //// TO DATE + Flexible( + flex: 1, + child: currentlyEmployed + ? TextFormField( + enabled: false, + initialValue: "PRESENT", + style: const TextStyle( + color: Colors.black45), + decoration: + normalTextFieldStyle("", "") + .copyWith(), + ) + : DateTimePicker( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + controller: toDateController, + firstDate: DateTime(1990), + lastDate: DateTime(2100), + selectableDayPredicate: (date) { + if (from != null && + from!.microsecondsSinceEpoch >= + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, + onChanged: (value) { + setState(() { + to = + DateTime.parse(value); + }); + }, + initialDate: from == null + ? DateTime.now() + : from!.add( + const Duration(days: 1)), + decoration: normalTextFieldStyle( + "To *", "To *") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + ), + prefixText: + currentlyEmployed + ? "PRESENT" + : ""), + ), + ), + ], ), - ], - ), - ), - ], - ); - }), - const Expanded(child: SizedBox()), - ////SUBMIT BUTTON - SizedBox( - width: double.infinity, - height: 60, - child: ElevatedButton( - style: - mainBtnStyle(primary, Colors.transparent, second), - onPressed: () { - if (_formKey.currentState!.validate()) { - final progress = ProgressHUD.of(context); - progress!.showWithText("Loading..."); - WorkHistory workHistory = WorkHistory( - position: selectedPosition, - id: null, - agency: selectedAgency, - fromDate: fromDateController.text.isEmpty - ? null - : DateTime.parse(fromDateController.text), - toDate: toDateController.text.isEmpty || - toDateController.text.toUpperCase() == - "PRESENT" - ? null - : DateTime.parse(toDateController.text), - salaryGrade: salaryGrade == null - ? null - : int.parse(salaryGrade!), - sgStep: salaryGradeStep == null - ? null - : int.parse(salaryGradeStep!), - monthlySalary: double.parse(salary!), - appointmentStatus: selectedStatus!.value); - context.read().add( - AddWorkHostory( - workHistory: workHistory, - profileId: widget.profileId, - token: widget.token, - isPrivate: isPrivate!)); - } - }, - child: const Text(submit)), + ), + ], + ); + }), + ], ), - const SizedBox( - height: 20, - ), - ], - ), + ), + ////SUBMIT BUTTON + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: + mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + print(selectedStatus?.value); + if (_formKey.currentState!.validate()) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + WorkHistory workHistory = WorkHistory( + position: selectedPosition, + id: null, + agency: selectedAgency, + fromDate: fromDateController.text.isEmpty + ? null + : DateTime.parse(fromDateController.text), + toDate: toDateController.text.isEmpty || + toDateController.text.toUpperCase() == + "PRESENT" + ? null + : DateTime.parse(toDateController.text), + salaryGrade: salaryGrade == null + ? null + : int.parse(salaryGrade!), + sgStep: salaryGradeStep == null + ? null + : int.parse(salaryGradeStep!), + monthlySalary: double.parse(salary!), + appointmentStatus: selectedStatus!.value); + context.read().add(AddWorkHostory( + workHistory: workHistory, + profileId: widget.profileId, + token: widget.token, + isPrivate: selectedAgency!.privateEntity!)); + } + }, + child: const Text(submit)), + ), + ], ), ), ), diff --git a/lib/screens/profile/components/work_history/edit_modal.dart b/lib/screens/profile/components/work_history/edit_modal.dart index 61303b8..4a3a0cd 100644 --- a/lib/screens/profile/components/work_history/edit_modal.dart +++ b/lib/screens/profile/components/work_history/edit_modal.dart @@ -7,9 +7,7 @@ import 'package:fluttericon/font_awesome_icons.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:searchfield/searchfield.dart'; import 'package:unit2/screens/profile/shared/add_for_empty_search.dart'; -import '../../../../bloc/profile/profile_bloc.dart'; import '../../../../bloc/profile/workHistory/workHistory_bloc.dart'; -import '../../../../bloc/user/user_bloc.dart'; import '../../../../model/profile/work_history.dart'; import '../../../../model/utils/agency.dart'; import '../../../../model/utils/agency_position.dart'; @@ -19,6 +17,7 @@ import '../../../../theme-data.dart/box_shadow.dart'; import '../../../../theme-data.dart/btn-style.dart'; import '../../../../theme-data.dart/colors.dart'; import '../../../../theme-data.dart/form-style.dart'; +import '../../../../utils/formatters.dart'; import '../../../../utils/global.dart'; import '../../../../utils/text_container.dart'; import '../../../../utils/validators.dart'; @@ -64,8 +63,8 @@ class _EditWorkHistoryScreenState extends State { final positionFocusNode = FocusNode(); final appointmentStatusNode = FocusNode(); final agencyCategoryFocusNode = FocusNode(); - int? profileId; - String? token; + DateTime? from; + DateTime? to; @override void dispose() { addPositionController.dispose(); @@ -77,571 +76,641 @@ class _EditWorkHistoryScreenState extends State { @override Widget build(BuildContext context) { - return BlocBuilder(builder: (context, state) { - return BlocBuilder( - builder: (context, state) { - if (state is EditWorkHistoryState) { - oldPositionController.text = state.workHistory.position!.title!; - oldAppointmentStatusController.text = - state.workHistory.appointmentStatus!; - oldAgencyController.text = state.workHistory.agency!.name!; - currentlyEmployed = state.workHistory.toDate == null ? true : false; - showSalaryGradeAndSalaryStep = - !state.workHistory.agency!.privateEntity!; - fromDateController.text = state.workHistory.fromDate.toString(); - toDateController.text = state.workHistory.toDate.toString(); - currentlyEmployed = state.workHistory.toDate == null ? true : false; - - return SingleChildScrollView( - child: SizedBox( - height: blockSizeVertical * 90, - child: FormBuilder( - key: _formKey, - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 25, horizontal: 18), - child: Column( - children: [ - ////POSITIONS - StatefulBuilder(builder: (context, setState) { - return SearchField( - controller: oldPositionController, - itemHeight: 50, - suggestionsDecoration: box1(), - suggestions: state.agencyPositions - .map((Position position) => SearchFieldListItem( - position.title!, - item: position, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10), - child: ListTile( - title: Text(position.title!), - )))) - .toList(), - focusNode: positionFocusNode, - searchInputDecoration: - normalTextFieldStyle("Position *", "").copyWith( - suffixIcon: - const Icon(Icons.arrow_drop_down)), - onSuggestionTap: (position) { - setState(() { - selectedPosition = position.item; - positionFocusNode.unfocus(); - }); - }, - emptyWidget: EmptyWidget( - controller: addPositionController, - onpressed: () { - setState(() { - Position newAgencyPosition = Position( - id: null, - title: addPositionController.text - .toUpperCase()); - state.agencyPositions - .insert(0, newAgencyPosition); - selectedPosition = newAgencyPosition; - addPositionController.text = ""; - Navigator.pop(context); - }); - }, - title: "Add Position"), - validator: (position) { - if (position!.isEmpty) { - return "This field is required"; - } - return null; - }, - ); - }), - const SizedBox( - height: 12, - ), - ////APPOINTMENT STATUS' - SearchField( - controller: oldAppointmentStatusController, - suggestions: state.appointmentStatus - .map((AppoinemtStatus status) => - SearchFieldListItem(status.label, - item: status)) - .toList(), - focusNode: appointmentStatusNode, - validator: (value) { - if (value!.isEmpty) { - return "This field is required"; - } - return null; - }, - onSuggestionTap: (status) { - selectedStatus = status.item; - appointmentStatusNode.unfocus(); - }, - searchInputDecoration: - normalTextFieldStyle("Appointment Status", "") - .copyWith( - suffixIcon: - const Icon(Icons.arrow_drop_down)), - ), - - const SizedBox( - height: 12, - ), - - ////AGENCY - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - SearchField( - controller: oldAgencyController, - itemHeight: 70, - focusNode: agencyFocusNode, - suggestions: state.agencies - .map((Agency agency) => - SearchFieldListItem(agency.name!, - item: agency, + return BlocBuilder( + builder: (context, state) { + if (state is EditWorkHistoryState) { + oldPositionController.text = state.workHistory.position!.title!; + oldAppointmentStatusController.text = + state.workHistory.appointmentStatus!; + oldAgencyController.text = state.workHistory.agency!.name!; + currentlyEmployed = state.workHistory.toDate == null ? true : false; + showSalaryGradeAndSalaryStep = + !state.workHistory.agency!.privateEntity!; + fromDateController.text = state.workHistory.fromDate.toString(); + toDateController.text = state.workHistory.toDate.toString(); + currentlyEmployed = state.workHistory.toDate == null ? true : false; + from = state.workHistory.fromDate; + to = state.workHistory.toDate; + return FormBuilder( + key: _formKey, + child: SizedBox( + height: blockSizeVertical * 90, + child: Padding( + padding: + const EdgeInsets.symmetric(vertical: 32, horizontal: 28), + child: Column( + children: [ + Flexible( + child: ListView( + children: [ + ////POSITIONS + StatefulBuilder(builder: (context, setState) { + return SearchField( + inputFormatters: [UpperCaseTextFormatter()], + controller: oldPositionController, + itemHeight: 100, + suggestionsDecoration: box1(), + suggestions: state.agencyPositions + .map((Position position) => + SearchFieldListItem(position.title!, + item: position, + child: Padding( + padding: + const EdgeInsets.symmetric( + horizontal: 10), child: ListTile( title: Text( - agency.name!, + position.title!, overflow: - TextOverflow.ellipsis, + TextOverflow.visible, ), - subtitle: Text( - agency.privateEntity == true - ? "Private" - : "Government"), - ))) - .toList(), - searchInputDecoration: - normalTextFieldStyle("Agency *", "") - .copyWith( - suffixIcon: const Icon( - Icons.arrow_drop_down)), - onSuggestionTap: (agency) { + )))) + .toList(), + focusNode: positionFocusNode, + searchInputDecoration: + normalTextFieldStyle("Position *", "") + .copyWith( + suffixIcon: IconButton( + icon: const Icon(Icons.arrow_drop_down), + onPressed: () { + positionFocusNode.unfocus(); + }, + )), + onSuggestionTap: (position) { + setState(() { + selectedPosition = position.item; + positionFocusNode.unfocus(); + }); + }, + emptyWidget: EmptyWidget( + controller: addPositionController, + onpressed: () { setState(() { - selectedAgency = agency.item; - if (selectedAgency!.privateEntity == - null) { - showIsPrivateRadio = true; - } else { - showIsPrivateRadio = false; - } - if (selectedAgency!.privateEntity == - true) { - showSalaryGradeAndSalaryStep = false; - } - if (selectedAgency!.privateEntity == - false) { - showSalaryGradeAndSalaryStep = true; - } - agencyFocusNode.unfocus(); + Position newAgencyPosition = Position( + id: null, + title: addPositionController.text + .toUpperCase()); + state.agencyPositions + .insert(0, newAgencyPosition); + selectedPosition = newAgencyPosition; + addPositionController.text = ""; + Navigator.pop(context); }); }, - validator: (agency) { - if (agency!.isEmpty) { - return "This field is required"; - } - return null; - }, - emptyWidget: EmptyWidget( - controller: addAgencyController, - onpressed: () { - setState(() { - Agency newAgency = Agency( - id: null, - name: addAgencyController.text - .toUpperCase(), - category: null, - privateEntity: null); - state.agencies.insert(0, newAgency); - selectedAgency = newAgency; - addAgencyController.text = ""; - showAgencyCategory = true; + title: "Add Position"), + validator: (position) { + if (position!.isEmpty) { + return "This field is required"; + } + return null; + }, + ); + }), + const SizedBox( + height: 12, + ), + ////APPOINTMENT STATUS' + SearchField( + inputFormatters: [UpperCaseTextFormatter()], + controller: oldAppointmentStatusController, + suggestions: state.appointmentStatus + .map((AppoinemtStatus status) => + SearchFieldListItem(status.label, + item: status, + child: Padding( + padding: const EdgeInsets.all(8), + child: Text(status.label)))) + .toList(), + focusNode: appointmentStatusNode, + validator: (value) { + if (value!.isEmpty) { + return "This field is required"; + } + return null; + }, + onSuggestionTap: (status) { + selectedStatus = status.item; + appointmentStatusNode.unfocus(); + }, + searchInputDecoration: + normalTextFieldStyle("Appointment Status", "") + .copyWith( + suffixIcon: IconButton( + icon: const Icon(Icons.arrow_drop_down), + onPressed: () { + appointmentStatusNode.unfocus(); + }, + ))), - showIsPrivateRadio = true; + const SizedBox( + height: 12, + ), - Navigator.pop(context); - }); + ////AGENCY + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + SearchField( + inputFormatters: [UpperCaseTextFormatter()], + controller: oldAgencyController, + itemHeight: 100, + focusNode: agencyFocusNode, + suggestions: state.agencies + .map((Agency agency) => + SearchFieldListItem(agency.name!, + item: agency, + child: ListTile( + title: Text( + agency.name!, + overflow: + TextOverflow.visible, + ), + subtitle: Text( + agency.privateEntity == + true + ? "Private" + : "Government"), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle("Agency *", "") + .copyWith( + suffixIcon: IconButton( + icon: const Icon(Icons.arrow_drop_down), + onPressed: () { + agencyFocusNode.unfocus(); }, - title: "Add Agency")), - - SizedBox( - height: showAgencyCategory ? 12 : 0, - ), - ////SHOW AGENCY CATEGORY - SizedBox( - child: showAgencyCategory - ? SearchField( - focusNode: agencyCategoryFocusNode, - itemHeight: 70, - suggestions: state.agencyCategory - .map((Category category) => - SearchFieldListItem( - category.name!, - item: category, - child: ListTile( - title: - Text(category.name!), - subtitle: Text(category - .industryClass! - .name!), - ))) - .toList(), - emptyWidget: Container( - height: 100, - decoration: box1(), - child: const Center( - child: - Text("No result found ...")), - ), - onSuggestionTap: (agencyCategory) { + )), + onSuggestionTap: (agency) { + setState(() { + selectedAgency = agency.item; + if (selectedAgency!.privateEntity == + null) { + showIsPrivateRadio = true; + } else { + showIsPrivateRadio = false; + } + if (selectedAgency!.privateEntity == + true) { + showSalaryGradeAndSalaryStep = false; + } + if (selectedAgency!.privateEntity == + false) { + showSalaryGradeAndSalaryStep = true; + } + agencyFocusNode.unfocus(); + }); + }, + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + emptyWidget: EmptyWidget( + controller: addAgencyController, + onpressed: () { setState(() { - selectedAgencyCategory = - agencyCategory.item; - agencyCategoryFocusNode.unfocus(); - selectedAgency = Agency( + Agency newAgency = Agency( id: null, - name: selectedAgency!.name, - category: - selectedAgencyCategory, + name: addAgencyController.text + .toUpperCase(), + category: null, privateEntity: null); + state.agencies.insert(0, newAgency); + selectedAgency = newAgency; + addAgencyController.text = ""; + showAgencyCategory = true; + + showIsPrivateRadio = true; + + Navigator.pop(context); }); }, - searchInputDecoration: - normalTextFieldStyle( - "Category *", "") - .copyWith( - suffixIcon: const Icon( - Icons.arrow_drop_down)), - validator: (value) { - if (value!.isEmpty) { - return "This field is required"; - } - return null; - }, - ) - : const SizedBox(), - ), + title: "Add Agency")), - ////PRVIATE SECTOR - SizedBox( - child: showIsPrivateRadio - ? FormBuilderRadioGroup( - decoration: InputDecoration( - border: InputBorder.none, - label: Row( - children: [ - Text( - "Is this private sector? ", - style: Theme.of(context) - .textTheme - .headlineSmall! - .copyWith(fontSize: 24), - ), - const Icon( - FontAwesome.help_circled) - ], - ), + SizedBox( + height: showAgencyCategory ? 12 : 0, + ), + ////SHOW AGENCY CATEGORY + SizedBox( + child: showAgencyCategory + ? SearchField( + focusNode: agencyCategoryFocusNode, + itemHeight: 70, + suggestions: state.agencyCategory + .map((Category category) => + SearchFieldListItem( + category.name!, + item: category, + child: ListTile( + title: Text( + category.name!), + subtitle: Text(category + .industryClass! + .name!), + ))) + .toList(), + emptyWidget: Container( + height: 100, + decoration: box1(), + child: const Center( + child: Text( + "No result found ...")), ), - - ////onvhange private sector - onChanged: (value) { + onSuggestionTap: (agencyCategory) { setState(() { - if (value.toString() == "YES") { - isPrivate = true; - showSalaryGradeAndSalaryStep = - false; - } else { - isPrivate = false; - showSalaryGradeAndSalaryStep = - true; - } + selectedAgencyCategory = + agencyCategory.item; + agencyCategoryFocusNode.unfocus(); selectedAgency = Agency( id: null, name: selectedAgency!.name, category: selectedAgencyCategory, - privateEntity: value == "YES" - ? true - : false); - agencyFocusNode.unfocus(); - agencyCategoryFocusNode.unfocus(); + privateEntity: null); }); }, - - name: 'isPrivate', - validator: - FormBuilderValidators.required(), - options: ["YES", "NO"] - .map((lang) => - FormBuilderFieldOption( - value: lang)) - .toList(growable: false), - ) - : const SizedBox()), - SizedBox( - height: showSalaryGradeAndSalaryStep ? 12 : 0, - ), - ////SALARY GRADE AND SALARY GRADE STEP - SizedBox( - child: showSalaryGradeAndSalaryStep - ? Column( - children: [ - Row( - children: [ - ////SALARY GRADE - Flexible( - flex: 1, - child: FormBuilderTextField( - initialValue: state - .workHistory.salaryGrade - ?.toString(), - name: 'salary_grade', - keyboardType: - TextInputType.number, - decoration: - normalTextFieldStyle( - "Salary Grade (SG)", - "0"), - validator: - integerAndNumeric, - autovalidateMode: - AutovalidateMode - .onUserInteraction, - ), - ), - const SizedBox( - width: 12, - ), - //// SALARY STEP - Flexible( - flex: 1, - child: FormBuilderTextField( - initialValue: state - .workHistory.sgStep - ?.toString(), - name: 'salary_step', - keyboardType: - TextInputType.number, - decoration: - normalTextFieldStyle( - "SG Step (SG)", - "0"), - validator: - integerAndNumeric, - autovalidateMode: - AutovalidateMode - .onUserInteraction, - ), - ) - ], - ) - ], - ) - : null), - ], - ); - }), - const SizedBox( - height: 12, - ), - ////MONTHLY SALARY - FormBuilderTextField( - initialValue: - state.workHistory.monthlySalary.toString(), - onChanged: (value) { - setState(() { - salary = value; - }); - }, - validator: numericRequired, - name: "salary", - decoration: - normalTextFieldStyle("Monthly Salary *", "") - .copyWith(prefix: const Text("₱ ")), - ), - - const SizedBox( - height: 12, - ), - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - ////CURRENTLY EMPLOYED - FormBuilderSwitch( - initialValue: currentlyEmployed, - activeColor: second, - onChanged: (value) { - setState(() { - if (value == true) { - currentlyEmployed = true; - toDateController.text = "PRESENT"; - } else { - currentlyEmployed = false; - toDateController.text = ""; - } - }); - }, - decoration: normalTextFieldStyle("", ''), - name: 'overseas', - title: const Text("Currently Employed?"), - ), - const SizedBox( - height: 12, - ), - SizedBox( - width: screenWidth, - child: Row( - children: [ - //// FROM DATE - Flexible( - flex: 1, - child: DateTimePicker( - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - use24HourFormat: false, - icon: const Icon(Icons.date_range), - controller: fromDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), - timeHintText: - "Date of Examination/Conferment", - decoration: normalTextFieldStyle( - "From *", "From *") - .copyWith( - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - )), - initialValue: null, - )), - const SizedBox( - width: 12, - ), - //// TO DATE - Flexible( - flex: 1, - child: currentlyEmployed - ? TextFormField( - enabled: false, - initialValue: "PRESENT", - style: const TextStyle( - color: Colors.black45), - decoration: normalTextFieldStyle( - "", "") + searchInputDecoration: + normalTextFieldStyle( + "Category *", "") .copyWith( - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black45, - )), - ) - : DateTimePicker( + suffixIcon: IconButton( + icon: const Icon( + Icons.arrow_drop_down), + onPressed: () { + agencyCategoryFocusNode.unfocus(); + }, + )), + validator: (value) { + if (value!.isEmpty) { + return "This field is required"; + } + return null; + }, + ) + : const SizedBox(), + ), + + ////PRVIATE SECTOR + SizedBox( + child: showIsPrivateRadio + ? FormBuilderRadioGroup( + decoration: InputDecoration( + border: InputBorder.none, + label: Row( + children: [ + Text( + "Is this private sector? ", + style: Theme.of(context) + .textTheme + .headlineSmall! + .copyWith(fontSize: 24), + ), + const Icon( + FontAwesome.help_circled) + ], + ), + ), + + ////onvhange private sector + onChanged: (value) { + setState(() { + if (value.toString() == "YES") { + isPrivate = true; + showSalaryGradeAndSalaryStep = + false; + } else { + isPrivate = false; + showSalaryGradeAndSalaryStep = + true; + } + selectedAgency = Agency( + id: null, + name: selectedAgency!.name, + category: + selectedAgencyCategory, + privateEntity: + value == "YES" + ? true + : false); + agencyFocusNode.unfocus(); + agencyCategoryFocusNode + .unfocus(); + }); + }, + + name: 'isPrivate', + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + options: ["YES", "NO"] + .map((lang) => + FormBuilderFieldOption( + value: lang)) + .toList(growable: false), + ) + : const SizedBox()), + SizedBox( + height: showSalaryGradeAndSalaryStep ? 12 : 0, + ), + ////SALARY GRADE AND SALARY GRADE STEP + SizedBox( + child: showSalaryGradeAndSalaryStep + ? Column( + children: [ + Row( + children: [ + ////SALARY GRADE + Flexible( + flex: 1, + child: FormBuilderTextField( + initialValue: state + .workHistory + .salaryGrade + ?.toString(), + name: 'salary_grade', + keyboardType: + TextInputType.number, + decoration: + normalTextFieldStyle( + "Salary Grade (SG)", + "0"), + validator: + integerAndNumeric, + autovalidateMode: + AutovalidateMode + .onUserInteraction, + ), + ), + const SizedBox( + width: 12, + ), + //// SALARY STEP + Flexible( + flex: 1, + child: FormBuilderTextField( + initialValue: state + .workHistory.sgStep + ?.toString(), + name: 'salary_step', + keyboardType: + TextInputType.number, + decoration: + normalTextFieldStyle( + "SG Step (SG)", + "0"), + validator: + integerAndNumeric, + autovalidateMode: + AutovalidateMode + .onUserInteraction, + ), + ) + ], + ) + ], + ) + : null), + ], + ); + }), + const SizedBox( + height: 12, + ), + ////MONTHLY SALARY + FormBuilderTextField( + initialValue: + state.workHistory.monthlySalary.toString(), + onChanged: (value) { + setState(() { + salary = value; + }); + }, + validator: numericRequired, + name: "salary", + decoration: + normalTextFieldStyle("Monthly Salary *", "") + .copyWith(prefix: const Text("₱ ")), + ), + + const SizedBox( + height: 12, + ), + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + ////CURRENTLY EMPLOYED + FormBuilderSwitch( + initialValue: currentlyEmployed, + activeColor: second, + onChanged: (value) { + setState(() { + if (value == true) { + currentlyEmployed = true; + toDateController.text = "PRESENT"; + } else { + currentlyEmployed = false; + toDateController.text = ""; + } + }); + }, + decoration: normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Currently Employed?"), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + child: StatefulBuilder( + builder: (context, setState) { + return Row( + children: [ + //// FROM DATE + Flexible( + flex: 1, + child: DateTimePicker( validator: FormBuilderValidators .required( errorText: "This field is required"), - controller: toDateController, - firstDate: DateTime(1970), + use24HourFormat: false, + icon: + const Icon(Icons.date_range), + controller: fromDateController, + firstDate: DateTime(1990), lastDate: DateTime(2100), + timeHintText: + "Date of Examination/Conferment", decoration: normalTextFieldStyle( - "To *", "To *") + "From *", "From *") .copyWith( prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - ), - prefixText: - currentlyEmployed - ? "PRESENT" - : ""), - initialValue: null, - ), - ), - ], + Icons.date_range, + color: Colors.black87, + )), + selectableDayPredicate: (date) { + if (to != null && + to!.microsecondsSinceEpoch <= + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, + onChanged: (value) { + setState(() { + from = DateTime.parse(value); + }); + }, + initialDate: to == null + ? DateTime.now() + : to!.subtract( + const Duration(days: 1)), + )), + const SizedBox( + width: 12, + ), + //// TO DATE + Flexible( + flex: 1, + child: currentlyEmployed + ? TextFormField( + enabled: false, + initialValue: "PRESENT", + style: const TextStyle( + color: Colors.black45), + decoration: + normalTextFieldStyle( + "", "") + .copyWith( + prefixIcon: + const Icon( + Icons.date_range, + color: Colors.black45, + )), + ) + : DateTimePicker( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + controller: toDateController, + firstDate: DateTime(1990), + lastDate: DateTime(2100), + selectableDayPredicate: + (date) { + if (from != null && + from!.microsecondsSinceEpoch >= + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, + onChanged: (value) { + setState(() { + to = + DateTime.parse(value); + }); + }, + initialDate: from == null + ? DateTime.now() + : from!.add( + const Duration( + days: 1)), + decoration: normalTextFieldStyle( + "To *", "To *") + .copyWith( + prefixIcon: + const Icon( + Icons.date_range, + color: + Colors.black87, + ), + prefixText: + currentlyEmployed + ? "PRESENT" + : ""), + initialValue: null, + ), + ), + ], + ); + }), ), - ), - ], - ); - }), - const Expanded(child: SizedBox()), - ////SUBMIT BUTTON - SizedBox( - width: double.infinity, - height: 60, - child: ElevatedButton( - style: mainBtnStyle( - primary, Colors.transparent, second), - onPressed: () { - if (_formKey.currentState!.saveAndValidate()) { - final progress = ProgressHUD.of(context); - progress!.showWithText("Loading..."); - salary = - _formKey.currentState!.value['salary']; - selectedPosition ??= - state.workHistory.position; - salaryGrade = _formKey - .currentState!.value['salary_grade']; - salaryGradeStep = _formKey - .currentState!.value['salary_step']; - selectedAgency ??= state.workHistory.agency; - - selectedStatus ??= AppoinemtStatus( - value: - state.workHistory.appointmentStatus!, - label: - state.workHistory.appointmentStatus!); - WorkHistory newWorkHistory = WorkHistory( - id: state.workHistory.id, - position: selectedPosition, - agency: selectedAgency, - fromDate: fromDateController.text.isEmpty - ? null - : DateTime.parse( - fromDateController.text), - toDate: toDateController.text.isEmpty || - toDateController.text - .toUpperCase() == - "PRESENT" || - toDateController.text - .toLowerCase() == - 'null' - ? null - : DateTime.parse(toDateController.text), - monthlySalary: double.parse(salary!), - appointmentStatus: selectedStatus!.value, - salaryGrade: salaryGrade == null - ? null - : int.parse(salaryGrade!), - sgStep: salaryGradeStep == null - ? null - : int.parse(salaryGradeStep!), - ); - context.read().add( - UpdateWorkHistory( - oldWorkHistory: state.workHistory, - profileId: profileId.toString(), - token: token!, - workHistory: newWorkHistory)); - } - }, - child: const Text(submit)), - ), - const SizedBox( - height: 20, - ), - ], + ], + ); + }), + ], + ), ), - ), + ////SUBMIT BUTTON + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: + mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + if (_formKey.currentState!.saveAndValidate()) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + salary = _formKey.currentState!.value['salary']; + selectedPosition ??= state.workHistory.position; + salaryGrade = + _formKey.currentState!.value['salary_grade']; + salaryGradeStep = + _formKey.currentState!.value['salary_step']; + selectedAgency ??= state.workHistory.agency; + + selectedStatus ??= AppoinemtStatus( + value: state.workHistory.appointmentStatus!, + label: state.workHistory.appointmentStatus!); + WorkHistory newWorkHistory = WorkHistory( + id: state.workHistory.id, + position: selectedPosition, + agency: selectedAgency, + fromDate: fromDateController.text.isEmpty + ? null + : DateTime.parse(fromDateController.text), + toDate: toDateController.text.isEmpty || + toDateController.text.toUpperCase() == + "PRESENT" || + toDateController.text.toLowerCase() == + 'null' + ? null + : DateTime.parse(toDateController.text), + monthlySalary: double.parse(salary!), + appointmentStatus: selectedStatus!.value, + salaryGrade: salaryGrade == null + ? null + : int.parse(salaryGrade!), + sgStep: salaryGradeStep == null + ? null + : int.parse(salaryGradeStep!), + ); + context.read().add( + UpdateWorkHistory( + oldWorkHistory: state.workHistory, + profileId: widget.profileId.toString(), + token: widget.token, + workHistory: newWorkHistory)); + } + }, + child: const Text(submit)), + ), + ], ), ), - ); - } - return const Center( - child: Text("Add Work History"), + ), ); - }, - ); - }); + } + return const Center( + child: Text("Add Work History"), + ); + }, + ); } } diff --git a/lib/screens/profile/components/work_history_screen.dart b/lib/screens/profile/components/work_history_screen.dart index 96de174..5372185 100644 --- a/lib/screens/profile/components/work_history_screen.dart +++ b/lib/screens/profile/components/work_history_screen.dart @@ -33,18 +33,35 @@ class WorkHistoryScreen extends StatelessWidget { int profileId; return Scaffold( appBar: AppBar( - title: const Text(workHistoryScreenTitle), + title: context.watch().state is AddWorkHistoryState + ? const FittedBox(child: Text("Add Work History")) + : context.watch().state is EditWorkHistoryState + ? const FittedBox( + child: Text("Edit Work History"), + ) + : const Text(workHistoryScreenTitle), backgroundColor: primary, centerTitle: true, - actions: context.watch().state is WorkHistoryLoaded?[ - AddLeading(onPressed: () { - context.read().add(ShowAddWorkHistoryForm()); - }) - ]:(context.watch().state is AddWorkHistoryState || context.watch().state is EditWorkHistoryState)?[ - CloseLeading(onPressed: (){ - context.read().add(LoadWorkHistories()); - }) - ]:[], + actions: context.watch().state is WorkHistoryLoaded + ? [ + AddLeading(onPressed: () { + context + .read() + .add(ShowAddWorkHistoryForm()); + }) + ] + : (context.watch().state + is AddWorkHistoryState || + context.watch().state + is EditWorkHistoryState) + ? [ + CloseLeading(onPressed: () { + context + .read() + .add(LoadWorkHistories()); + }) + ] + : [], ), body: //UserBloc @@ -187,9 +204,9 @@ class WorkHistoryScreen extends StatelessWidget { .titleMedium! .copyWith( fontWeight: - FontWeight.w600), + FontWeight.w600, + color: primary), ), - const Divider(), const SizedBox( height: 8, ), @@ -206,10 +223,10 @@ class WorkHistoryScreen extends StatelessWidget { height: 5, ), Text( - "$from $to ", + "$from - $to ", style: Theme.of(context) .textTheme - .bodyMedium, + .labelMedium, ), ], )), @@ -254,20 +271,21 @@ class WorkHistoryScreen extends StatelessWidget { workHistory)); } }, - menuItems: [ - popMenuItem( - text: "Edit", - value: 1, - icon: Icons.edit), - popMenuItem( - text: "Delete", - value: 2, - icon: Icons.delete), - popMenuItem( - text: "Attachment", - value: 3, - icon: FontAwesome.attach) - ], + menuItems: [ + popMenuItem( + text: "Update", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Remove", + value: 2, + icon: Icons.delete), + popMenuItem( + text: "Attach", + value: 2, + icon: Icons.attach_file), + + ], icon: const Icon( Icons.more_vert, color: Colors.grey, @@ -289,15 +307,24 @@ class WorkHistoryScreen extends StatelessWidget { } } if (state is AddWorkHistoryState) { - return AddWorkHistoryScreen(profileId: profileId, token: token!,); + return AddWorkHistoryScreen( + profileId: profileId, + token: token!, + ); } if (state is EditWorkHistoryState) { - return EditWorkHistoryScreen(profileId: profileId,token: token!,); + return EditWorkHistoryScreen( + profileId: profileId, + token: token!, + ); } if (state is WorkHistoryErrorState) { return SomethingWentWrong( - message: state.message, onpressed: () { - context.read().add(GetWorkHistories(profileId: profileId,token: token!)); + message: state.message, + onpressed: () { + context.read().add( + GetWorkHistories( + profileId: profileId, token: token!)); }); } return Container(); diff --git a/lib/screens/profile/profile.dart b/lib/screens/profile/profile.dart index 9788394..a7dc9f9 100644 --- a/lib/screens/profile/profile.dart +++ b/lib/screens/profile/profile.dart @@ -9,6 +9,7 @@ import 'package:fluttericon/entypo_icons.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:fluttericon/modern_pictograms_icons.dart'; import 'package:unit2/bloc/profile/primary_information/address/address_bloc.dart'; +import 'package:unit2/bloc/profile/primary_information/citizenship/citizenship_bloc.dart'; import 'package:unit2/bloc/profile/primary_information/contact/contact_bloc.dart'; import 'package:unit2/bloc/profile/primary_information/identification/identification_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; @@ -116,7 +117,7 @@ class ProfileInfo extends StatelessWidget { return BlocProvider.value(value: ProfileBloc()..add(GetPrimaryBasicInfo(primaryBasicInformation: profile)),child: PrimaryInfo(token: token!,profileId: profileId!,),); })); }), - subMenu(Icons.home, "Home Addresses", () { + subMenu(Icons.home, "Addresses", () { Navigator.push(context, MaterialPageRoute( builder: (BuildContext context) { return BlocProvider( @@ -159,11 +160,17 @@ class ProfileInfo extends StatelessWidget { })); }), subMenu(Icons.flag, "Citizenships", () { - Navigator.push(context, MaterialPageRoute( + Navigator.push(context, MaterialPageRoute( builder: (BuildContext context) { - return CitizenShipScreen( - citizenships: state.profileInformation - .basicInfo.citizenships, + + return BlocProvider( + create: (context) => CitizenshipBloc() + ..add(GetCitizenship( + citizenship: state + .profileInformation + .basicInfo + .citizenships)), + child: CitizenShipScreen(profileId: profileId!,token: token!), ); })); }), diff --git a/lib/screens/profile/shared/add_for_empty_search.dart b/lib/screens/profile/shared/add_for_empty_search.dart index 2685e51..8d90f98 100644 --- a/lib/screens/profile/shared/add_for_empty_search.dart +++ b/lib/screens/profile/shared/add_for_empty_search.dart @@ -4,6 +4,7 @@ import '../../../theme-data.dart/box_shadow.dart'; import '../../../theme-data.dart/btn-style.dart'; import '../../../theme-data.dart/colors.dart'; import '../../../theme-data.dart/form-style.dart'; +import '../../../utils/formatters.dart'; class EmptyWidget extends StatelessWidget { final TextEditingController controller; @@ -41,6 +42,9 @@ class EmptyWidget extends StatelessWidget { child: Column( children: [ TextFormField( + inputFormatters: [ + UpperCaseTextFormatter() + ], controller: controller, decoration: normalTextFieldStyle("", ""), ), diff --git a/lib/screens/unit2/homepage.dart/components/dashboard.dart b/lib/screens/unit2/homepage.dart/components/dashboard.dart index 5a94357..13bec9e 100644 --- a/lib/screens/unit2/homepage.dart/components/dashboard.dart +++ b/lib/screens/unit2/homepage.dart/components/dashboard.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:unit2/screens/docsms/components/request_receipt.dart'; import 'package:unit2/screens/docsms/index.dart'; import 'package:unit2/screens/unit2/homepage.dart/module-screen.dart'; @@ -53,7 +54,7 @@ class DashBoard extends StatelessWidget { vertical: 5, horizontal: 5), children: roles[index].roles.map((role) { //// if role name is qr code && finishRoles not contain security - //// this card will visible if the user has qr code scanner or security role + //// this card will visible if the user has qr code scanner or security roleπurls if (role.role.name!.toLowerCase() == 'qr code scanner' && !finishRoles.contains("security")) { @@ -69,7 +70,9 @@ class DashBoard extends StatelessWidget { .contains("read")) { //// if all conditions are true return card return CardLabel( - ontap: () {}, + ontap: () { + Navigator.pushNamed(context, '/pass-check'); + }, icon: role.icon, title: "Pass Check", ); @@ -154,7 +157,31 @@ class DashBoard extends StatelessWidget { } } return Container(); - } else { + } else if (role.role.name!.toLowerCase() == + 'establishment point person' && + !finishRoles.contains('superadmin')) { + finishRoles.add('establishment point person'); + for (var element in role.role.modules!) { + if (element!.name!.toLowerCase() == 'unit2') { + for (var element in element.objects!) { + if (element!.id == 9 && + element.operations! + .contains("read")) { + return CardLabel( + ontap: () {}, + icon: FontAwesome5.building, + title: "Establishment", + ); + } + } + } + } + return Container(); + //// if role name is field surveyor + } + + + else { return Container(); } }).toList()), diff --git a/lib/screens/unit2/homepage.dart/components/menu-screen.dart b/lib/screens/unit2/homepage.dart/components/menu-screen.dart index 65b3621..b9cedc2 100644 --- a/lib/screens/unit2/homepage.dart/components/menu-screen.dart +++ b/lib/screens/unit2/homepage.dart/components/menu-screen.dart @@ -22,51 +22,56 @@ class _MenuScreenState extends State { final String lastname = widget.userData!.user!.login!.user!.lastName!.toUpperCase(); return Drawer( - child: SingleChildScrollView( - child: SizedBox( - height: blockSizeVertical * 96, - child: Column( - // ignore: prefer_const_literals_to_create_immutables - children: [ - UserAccountsDrawerHeader( - decoration: const BoxDecoration( - color: primary, - image: DecorationImage( - image: AssetImage('assets/pngs/bg.png'), - fit: BoxFit.cover)), - accountName: Text("$firstName $lastname"), - accountEmail: null, - currentAccountPicture: CircleAvatar( - radius: 40, - backgroundColor: fifth, - child: CircleAvatar( - radius: 33, - backgroundColor: third, - child: //Icon(Icons.person, size: 40, color: fifth), - Text( - firstName[0].toUpperCase(), - style: const TextStyle(fontSize: 45.0, color: fifth), + child: SizedBox( + height: screenHeight, + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Flexible( + child: ListView( + // ignore: prefer_const_literals_to_create_immutables + children: [ + UserAccountsDrawerHeader( + decoration: const BoxDecoration( + color: primary, + image: DecorationImage( + image: AssetImage('assets/pngs/bg.png'), + fit: BoxFit.cover)), + accountName: Text("$firstName $lastname"), + accountEmail: null, + currentAccountPicture: CircleAvatar( + radius: 40, + backgroundColor: fifth, + child: CircleAvatar( + radius: 33, + backgroundColor: third, + child: //Icon(Icons.person, size: 40, color: fifth), + Text( + firstName[0].toUpperCase(), + style: const TextStyle(fontSize: 45.0, color: fifth), + ), + ), ), ), - ), + getTile(FontAwesome5.user, "Basic Info", '/basic-info', context, + widget.userData!), + const Divider(), + getTile(FontAwesome5.user_circle, "Profile", + '/profile', context, widget.userData!), + const Divider(), + getTile(FontAwesome5.life_ring, "Request SOS", '/sos', + context, widget.userData!), + + ], ), - getTile(FontAwesome5.user, "Basic Info", '/basic-info', context, - widget.userData!), - const Divider(), - getTile(FontAwesome5.user_circle, "Profile", - '/profile', context, widget.userData!), - const Divider(), - getTile(FontAwesome5.life_ring, "Request SOS", '/sos', - context, widget.userData!), - const Divider(), - Expanded( - child: Align( - alignment: FractionalOffset.bottomLeft, - child: getTile(WebSymbols.logout, "Logout", '/', context, + ), + const Divider(), + Align( + alignment: FractionalOffset.bottomLeft, + child: getTile(WebSymbols.logout, "Logout", '/', context, widget.userData!), - )), - ], - ), + ), + ], ), ), ); diff --git a/lib/screens/unit2/homepage.dart/components/menu.dart b/lib/screens/unit2/homepage.dart/components/menu.dart index d001e80..6288a3f 100644 --- a/lib/screens/unit2/homepage.dart/components/menu.dart +++ b/lib/screens/unit2/homepage.dart/components/menu.dart @@ -24,7 +24,6 @@ Widget getTile( await CREDENTIALS!.clear(); await CREDENTIALS!.deleteAll(['username','password','saved']); Navigator.pushReplacementNamed (context,"/"); - },"Logout","Are You sure you want to logout?"); }if(title.toLowerCase() == 'profile'){ ProfileArguments profileArguments = ProfileArguments(token: userData.user!.login!.token!, userID:userData.user!.login!.user!.profileId!); diff --git a/lib/screens/unit2/login/login.dart b/lib/screens/unit2/login/login.dart index bba33c3..164a5c3 100644 --- a/lib/screens/unit2/login/login.dart +++ b/lib/screens/unit2/login/login.dart @@ -9,6 +9,7 @@ import 'package:fluttertoast/fluttertoast.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:unit2/screens/unit2/login/components/update_required.dart'; +import 'package:unit2/screens/unit2/login/qr_login.dart'; import 'package:unit2/utils/alerts.dart'; import 'package:unit2/utils/internet_time_out.dart'; import 'package:unit2/utils/text_container.dart'; @@ -46,6 +47,7 @@ class _UniT2LoginState extends State { backgroundColor: Colors.black87, indicatorWidget: const SpinKitFadingCircle(color: Colors.white), child: BlocConsumer(listener: (context, state) { + print(state); if (state is UserLoggedIn || state is UuidLoaded) { final progress = ProgressHUD.of(context); progress!.dismiss(); @@ -70,18 +72,23 @@ class _UniT2LoginState extends State { context, '/module-screen'), "Save credentials?", "Do you want to save credentials so you don't have to login again next time?."); - }else{ - Navigator.pushReplacementNamed( - context, '/module-screen'); + } else { + Navigator.pushReplacementNamed(context, '/module-screen'); } } else { final progress = ProgressHUD.of(context); progress!.dismiss(); errorAlert(context, "Error Login", state.message, () { - context.read().add(LoadVersion(username: username,password: password)); + context + .read() + .add(LoadVersion(username: username, password: password)); Navigator.of(context).pop(); }); } + }if(state is UuidLoaded){ + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + return const QRLogin(); + })); } }, builder: (context, state) { if (state is VersionLoaded) { @@ -103,7 +110,7 @@ class _UniT2LoginState extends State { key: _formKey, child: Padding( padding: - const EdgeInsets.symmetric(horizontal: 25), + const EdgeInsets.symmetric(horizontal: 42), child: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, @@ -239,7 +246,6 @@ class _UniT2LoginState extends State { TextStyle(color: Colors.white), ), onPressed: () { - final progress = ProgressHUD.of(context); @@ -247,22 +253,18 @@ class _UniT2LoginState extends State { if (_formKey.currentState! .saveAndValidate()) { - password = _formKey - .currentState! - .value['password']; - username = _formKey - .currentState! - .value['username']; + password = _formKey.currentState! + .value['password']; + username = _formKey.currentState! + .value['username']; progress?.showWithText( 'Logging in...', ); BlocProvider.of(context) .add(UserLogin( - - username:username, - password: password - )); + username: username, + password: password)); } }, ), @@ -319,7 +321,8 @@ class _UniT2LoginState extends State { Colors.transparent, Colors.white38), onPressed: () { - Navigator.pushNamed(context, '/sos'); + Navigator.pushNamed( + context, '/sos'); }, label: const Text( requestSOS, diff --git a/lib/screens/unit2/login/qr_login.dart b/lib/screens/unit2/login/qr_login.dart index 6be5696..344b2bd 100644 --- a/lib/screens/unit2/login/qr_login.dart +++ b/lib/screens/unit2/login/qr_login.dart @@ -3,13 +3,17 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_svg/svg.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; +import 'package:fluttertoast/fluttertoast.dart'; import 'package:unit2/theme-data.dart/btn-style.dart'; +import 'package:unit2/utils/global_context.dart'; import 'package:unit2/widgets/wave.dart'; import '../../../bloc/user/user_bloc.dart'; import '../../../theme-data.dart/colors.dart'; import '../../../theme-data.dart/form-style.dart'; +import '../../../utils/alerts.dart'; import '../../../utils/global.dart'; import '../../../utils/text_container.dart'; import '../../../utils/validators.dart'; @@ -25,11 +29,12 @@ class _QRLoginState extends State { bool showSuffixIcon = false; bool _showPassword = true; final _formKey = GlobalKey(); + String? password; @override Widget build(BuildContext context) { return WillPopScope( onWillPop: ()async{ - context.read().add(LoadVersion()); + Navigator.pushReplacementNamed(context,"/"); return true; }, child: SafeArea( @@ -42,12 +47,44 @@ class _QRLoginState extends State { centerTitle: true, ), body: ProgressHUD( + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), child: BlocConsumer( listener: (context,state){ if (state is UserLoggedIn) { - final progress = ProgressHUD.of(context); - progress!.dismiss(); - Navigator.pushReplacementNamed(context, '/module-screen'); + if (state.success == true) { + if (!state.savedCredentials!) { + confirmAlertWithCancel(context, () async { + await CREDENTIALS?.put('saved', "saved"); + await CREDENTIALS?.put('username', + state.userData!.user!.login!.user!.username!); + await CREDENTIALS?.put('password', password); + Fluttertoast.showToast( + msg: "Credentials Successfully saved", + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.BOTTOM, + backgroundColor: Colors.black, + ); + Navigator.pushReplacementNamed(NavigationService.navigatorKey.currentContext!, '/module-screen'); + }, + () => Navigator.pushReplacementNamed( + context, '/module-screen'), + "Save credentials?", + "Do you want to save credentials so you don't have to login again next time?."); + } else { + Navigator.pushReplacementNamed(context, '/module-screen'); + } + } else { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + errorAlert(context, "Error Login", state.message, () { + context + .read() + .add(LoadUuid()); + Navigator.of(context).pop(); + }); + } + } }, builder: (context, state) { @@ -60,7 +97,7 @@ class _QRLoginState extends State { child: WaveReverse(height: blockSizeVertical * 8)), Container( height: screenHeight * .90, - padding: const EdgeInsets.symmetric(horizontal: 15), + padding: const EdgeInsets.symmetric(horizontal: 32), child: FormBuilder( key: _formKey, child: Column( @@ -161,10 +198,11 @@ class _QRLoginState extends State { height: blockSizeVertical * 6, child: ElevatedButton( style: secondaryBtnStyle( - second, Colors.transparent, primary), + primary, Colors.transparent, primary), onPressed: () { if (_formKey.currentState! .saveAndValidate()) { + password = _formKey.currentState!.value['password']; final progress = ProgressHUD.of(context); progress?.showWithText( @@ -174,7 +212,7 @@ class _QRLoginState extends State { } }, - child: const Text(submit)), + child: const Text("LOGIN")), ) ], ), diff --git a/lib/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart b/lib/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart index 57081ed..3610217 100644 --- a/lib/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart +++ b/lib/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart @@ -33,18 +33,18 @@ class _QRCodeScannerSettingsState extends State { appBar: AppBar( title: const Text(qrScannerTitle), centerTitle: true, - backgroundColor: second, + backgroundColor: primary, ), body: SingleChildScrollView( child: Container( - height: screenHeight * .88, - padding: const EdgeInsets.symmetric(horizontal: 30), + height: screenHeight * .84, + padding: const EdgeInsets.symmetric(horizontal: 30,vertical: 10), child: FormBuilder( key: _formKey, child: Column( children: [ const SizedBox( - height: 32, + height:24, ), SvgPicture.asset( 'assets/svgs/switch.svg', @@ -56,16 +56,16 @@ class _QRCodeScannerSettingsState extends State { setQRScannerSettings, style: Theme.of(context) .textTheme - .headline6! + .titleLarge! .copyWith(color: third), textAlign: TextAlign.center, ), ), Text(includeOtherInputs, - style: Theme.of(context).textTheme.subtitle1), + style: Theme.of(context).textTheme.titleMedium), Text( includeOtherInputsSubTitle, - style: Theme.of(context).textTheme.caption, + style: Theme.of(context).textTheme.bodySmall, ), SizedBox( child: FittedBox( @@ -87,10 +87,10 @@ class _QRCodeScannerSettingsState extends State { ), // Incoming or outgoing Text(incomingORoutgoing, - style: Theme.of(context).textTheme.subtitle1), + style: Theme.of(context).textTheme.titleMedium), Text( incomingORoutgoingSubTitle, - style: Theme.of(context).textTheme.caption, + style: Theme.of(context).textTheme.bodySmall, ), FittedBox( child: CostumToggleSwitch( @@ -174,10 +174,10 @@ class _QRCodeScannerSettingsState extends State { ), SizedBox( width: double.infinity, - height: screenHeight * .06, + height: 60, child: ElevatedButton( - style: secondaryBtnStyle( - second, Colors.transparent, Colors.white54), + style: mainBtnStyle( + primary, Colors.transparent, Colors.white54), child: const Text( submit, style: TextStyle(color: Colors.white), diff --git a/lib/sevices/login_service/auth_service.dart b/lib/sevices/login_service/auth_service.dart index 497e8f4..97720c9 100644 --- a/lib/sevices/login_service/auth_service.dart +++ b/lib/sevices/login_service/auth_service.dart @@ -5,7 +5,6 @@ import 'package:unit2/model/login_data/user_info/user_data.dart'; import 'package:unit2/model/login_data/version_info.dart'; import 'package:http/http.dart' as http; import 'package:unit2/utils/request.dart'; -import '../../utils/text_container.dart'; import '../../utils/urls.dart'; class AuthService { @@ -21,56 +20,59 @@ class AuthService { }; try { String path = Url.instance.latestApk(); - http.Response response = await Request.instance.getRequest(path: path,headers: headers,param: {}); + http.Response response = await Request.instance + .getRequest(path: path, headers: headers, param: {}); if (response.statusCode == 200) { Map data = jsonDecode(response.body); versionInfo = VersionInfo.fromJson(data['data']); } - }catch (e) { + } catch (e) { throw (e.toString()); } return versionInfo; } - Future> webLogin({String? username, String? password})async{ - Map body ={'username':username!,'password':password!}; - Map baseHeaders = { - 'Content-Type': 'application/json' - }; - Map responseStatus = {}; + Future> webLogin( + {String? username, String? password}) async { + Map body = {'username': username!, 'password': password!}; + Map baseHeaders = {'Content-Type': 'application/json'}; + Map responseStatus = {}; String path = Url.instance.authentication(); - UserData? userData; - try{ - http.Response response = await Request.instance.postRequest(path: path,param: {},headers: baseHeaders,body: body); - - Map data = jsonDecode(response.body); - + // try { + http.Response response = await Request.instance + .postRequest(path: path, param: {}, headers: baseHeaders, body: body); + Map data = jsonDecode(response.body); responseStatus = data; - return responseStatus; - }catch(e){ - throw (e.toString()); - } - + // } catch (e) { + // throw (e.toString()); + // } } - Future qrLogin({String? uuid, String? password})async{ - Map body ={'uuid':uuid!,'password':password!}; - Map baseHeaders = { - 'Content-Type': 'application/json' + + Future> qrLogin({String? uuid, String? password}) async { + + Map responseStatus = {}; + Map body = { + 'uuid': uuid!, + 'password': password!, + "login_only": false, + "token": false }; + Map baseHeaders = {'Content-Type': 'application/json'}; String path = Url.instance.authentication(); UserData? userData; - try{ - http.Response response = await Request.instance.postRequest(path: path,param: {},headers: baseHeaders,body: body); - if(response.statusCode == 200){ + try { + http.Response response = await Request.instance + .postRequest(path: path, param: {}, headers: baseHeaders, body: body); + Map data = jsonDecode(response.body); - userData = UserData.fromJson(data['data']); - } - }catch(e){ + responseStatus = data; + + } catch (e) { throw (e.toString()); } - return userData!; + return responseStatus; } } diff --git a/lib/sevices/profile/address_service.dart b/lib/sevices/profile/address_service.dart index 5458c98..5e0478b 100644 --- a/lib/sevices/profile/address_service.dart +++ b/lib/sevices/profile/address_service.dart @@ -19,7 +19,7 @@ class AddressService { required int? lotNumber, required String token, required int profileId}) async { - Map? _response = {}; + Map _response = {}; String authtoken = "Token $token"; String path = '${Url.instance.addressPath()}$profileId/'; Map headers = { @@ -42,12 +42,22 @@ class AddressService { .postRequest(path: path, body: body, headers: headers, param: {}); if (response.statusCode == 201) { Map data = jsonDecode(response.body); - _response = data['response']['data']; - _response!.addAll({'success': true}); + _response = data['response']; + _response.addAll({'data': data['response']['data']}); + _response.addAll( + {'message': data['response']['message']}, + ); + _response.addAll( + {'success': true}, + ); } else { - _response.addAll({'success': false}); + Map data = jsonDecode(response.body); + String message = data['response']['details']; + _response.addAll({'message': message}); + _response.addAll( + {'success': false}, + ); } - return _response; } catch (e) { throw e.toString(); @@ -92,7 +102,7 @@ class AddressService { required int? lotNumber, required String token, required int profileId}) async { - Map? _response = {}; + Map _response = {}; String authtoken = "Token $token"; String path = '${Url.instance.addressPath()}$profileId/'; Map headers = { @@ -126,12 +136,23 @@ class AddressService { "_brgyCode": address.barangay?.code, "_countryId": address.country!.id })); - if (response.statusCode == 200) { + if (response.statusCode == 200) { Map data = jsonDecode(response.body); - _response = data['response']['data']; - _response!.addAll({'success': true}); + _response = data['response']; + _response.addAll({'data': data['response']['data']}); + _response.addAll( + {'message': data['response']['message']}, + ); + _response.addAll( + {'success': true}, + ); } else { - _response.addAll({'success': false}); + Map data = jsonDecode(response.body); + String message = data['response']['details']; + _response.addAll({'message': message}); + _response.addAll( + {'success': false}, + ); } return _response; diff --git a/lib/sevices/profile/citizenship_services.dart b/lib/sevices/profile/citizenship_services.dart new file mode 100644 index 0000000..338bc74 --- /dev/null +++ b/lib/sevices/profile/citizenship_services.dart @@ -0,0 +1,107 @@ +import 'dart:convert'; + +import 'package:unit2/model/profile/basic_information/citizenship.dart'; + +import '../../utils/request.dart'; +import '../../utils/urls.dart'; +import 'package:http/http.dart' as http; + +class CitizenshipServices { + static final CitizenshipServices _instance = CitizenshipServices(); + static CitizenshipServices get instance => _instance; + + Future> add( + {required int profileId, + required String token, + required int countryId, + required bool naturalBorn}) async { + String path = "${Url.instance.citizenship()}$profileId/"; + String authToken = "Token $token"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + Map responseStatus = {}; + Map body = { + "country_id": countryId, + "natural_born": naturalBorn, + }; + try { + http.Response response = await Request.instance + .postRequest(path: path, headers: headers, body: body, param: {}); + if (response.statusCode == 201) { + Map data = jsonDecode(response.body); + responseStatus = data; + } else { + responseStatus.addAll({'success': false}); + } + return responseStatus; + } catch (e) { + throw e.toString(); + } + } + + Future> update( + {required int profileId, + required String token, + required Citizenship citizenship, + required int oldCountry}) async { + String path = "${Url.instance.citizenship()}$profileId/"; + String authToken = "Token $token"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authToken + }; + Map body = { + "country_id": citizenship.country!.id, + "natural_born": citizenship.naturalBorn, + "_oldCountryId": oldCountry + }; + Map responseStatus = {}; + try { + http.Response response = await Request.instance + .putRequest(path: path, headers: headers, body: body, param: {}); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + responseStatus = data; + } else { + Map data = jsonDecode(response.body); + String message = data['message']; + responseStatus.addAll({'success': false}); + responseStatus.addAll({'message': message}); + } + return responseStatus; + } catch (e) { + throw e.toString(); + } + } + + Future delete( + {required int profileId, + required String token, + required countryId, + required bool naturalBorn}) async { + Map params = {"force_mode": "true"}; + String authtoken = "Token $token"; + String path = "${Url.instance.citizenship()}$profileId/"; + bool success = false; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authtoken + }; + Map body = {"country_id": countryId, "natural_born": naturalBorn}; + try { + http.Response response = await Request.instance.deleteRequest( + path: path, headers: headers, body: body, param: params); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + success = data['success']; + } else { + success = false; + } + } catch (e) { + throw (e.toString()); + } + return success; + } +} diff --git a/lib/sevices/profile/family_services.dart b/lib/sevices/profile/family_services.dart index ab47a8d..276607a 100644 --- a/lib/sevices/profile/family_services.dart +++ b/lib/sevices/profile/family_services.dart @@ -140,7 +140,7 @@ class FamilyService { "company_address": family.companyAddress, "company_contact_number": family.companyContactNumber, "first_name": family.relatedPerson!.firstName, - "middle_name": family.relatedPerson!.middleName, + "middle_name": family.relatedPerson!.middleName!.isEmpty?null:family.relatedPerson!.middleName!, "last_name": family.relatedPerson!.lastName, "name_extension": family.relatedPerson?.nameExtension, "birthdate": family.relatedPerson!.birthdate.toString(), @@ -174,7 +174,7 @@ class FamilyService { } return _response; } - +////delete Future delete( {required int personRelatedId, required int profileId, diff --git a/lib/sevices/profile/identification_services.dart b/lib/sevices/profile/identification_services.dart index 64c537b..36650c4 100644 --- a/lib/sevices/profile/identification_services.dart +++ b/lib/sevices/profile/identification_services.dart @@ -105,7 +105,7 @@ class IdentificationServices { "_agencyName": identification.agency!.name, "_agencyCatId": identification.agency!.category!.id, "_privateEntity": identification.agency!.privateEntity, - "_citymunCode": identification.issuedAt?.cityMunicipality?.code, + "_citymunCode":identification.issuedAt!.country!.id == 175? identification.issuedAt?.cityMunicipality?.code:null, "_countryId": identification.issuedAt!.country!.id }; try { diff --git a/lib/sevices/profile/profile_service.dart b/lib/sevices/profile/profile_service.dart index 7ff4b12..1ed39fb 100644 --- a/lib/sevices/profile/profile_service.dart +++ b/lib/sevices/profile/profile_service.dart @@ -1,26 +1,14 @@ import 'dart:convert'; import 'package:http/http.dart' as http; -import 'package:unit2/model/login_data/employee_info/employee_info.dart'; import 'package:unit2/model/profile/basic_info.dart'; import 'package:unit2/model/profile/basic_information/adress.dart'; import 'package:unit2/model/profile/basic_information/citizenship.dart'; import 'package:unit2/model/profile/basic_information/contact_information.dart'; import 'package:unit2/model/profile/basic_information/identification_information.dart'; -import 'package:unit2/model/profile/educational_background.dart'; -import 'package:unit2/model/profile/eligibility.dart'; -import 'package:unit2/model/profile/family_backround.dart'; -import 'package:unit2/model/profile/learning_development.dart'; -import 'package:unit2/model/profile/other_information/non_acedimic_recognition.dart'; import 'package:unit2/model/profile/profileInfomation.dart'; -import 'package:unit2/model/profile/references.dart'; -import 'package:unit2/model/profile/other_information/skills_and_hobbies.dart'; -import 'package:unit2/model/profile/voluntary_works.dart'; -import 'package:unit2/model/profile/work_history.dart'; -import 'package:unit2/utils/request.dart'; import 'package:unit2/utils/urls.dart'; - import '../../model/profile/basic_information/primary-information.dart'; -import '../../model/profile/other_information/organization_memberships.dart'; +import '../../utils/request.dart'; class ProfileService { static final ProfileService _instance = ProfileService(); @@ -73,6 +61,13 @@ class ProfileService { identificationInformation.add(identification); }); } + if(data['data']['basic_information']['citizenships'] != null){ + data['data']['basic_information']['citizenships']! + .forEach((var citizenship) { + Citizenship newCitizenShip = Citizenship.fromJson(citizenship); + citizenships.add(newCitizenShip); + }); + } BasicInfo basicInfo = BasicInfo( contactInformation: contactInformation, @@ -128,7 +123,7 @@ class ProfileService { "title_prefix": profileInfo.titlePrefix, "title_suffix": profileInfo.titleSuffix }; - // try { + try { http.Response response = await Request.instance .postRequest(path: path, headers: headers, body: body, param: {}); if (response.statusCode == 200) { @@ -138,8 +133,8 @@ class ProfileService { statusResponse.addAll({'success': false}); } return statusResponse; - // } catch (e) { - // throw e.toString(); - // } + } catch (e) { + throw e.toString(); + } } } diff --git a/lib/sevices/profile/references_services.dart b/lib/sevices/profile/references_services.dart index ee215ba..e30e827 100644 --- a/lib/sevices/profile/references_services.dart +++ b/lib/sevices/profile/references_services.dart @@ -92,7 +92,7 @@ Future> update({required PersonalReference ref,required St "_brgyCode": overseas?null:ref.address?.barangay?.code, "_countryId": overseas?ref.address!.country!.id:175 }; - // try{ + try{ http.Response response = await Request.instance.putRequest(path: path,body: body,param: {},headers: headers); if(response.statusCode == 200){ Map data = jsonDecode(response.body); @@ -101,9 +101,9 @@ Future> update({required PersonalReference ref,required St responseStatus.addAll({'success':false}); } return responseStatus; - // }catch(e){ - // throw e.toString(); - // } + }catch(e){ + throw e.toString(); + } } Futuredelete({required int profileId, required String token, required int id })async{ diff --git a/lib/sevices/profile/volunatary_services.dart b/lib/sevices/profile/volunatary_services.dart index 0defef6..e655744 100644 --- a/lib/sevices/profile/volunatary_services.dart +++ b/lib/sevices/profile/volunatary_services.dart @@ -19,7 +19,7 @@ class VoluntaryService { 'Authorization': authToken }; - // try { + try { http.Response response = await Request.instance .getRequest(path: path, param: {}, headers: headers); if (response.statusCode == 200) { @@ -31,9 +31,9 @@ class VoluntaryService { }); } } - // } catch (e) { - // throw (e.toString()); - // } + } catch (e) { + throw (e.toString()); + } return voluntaryWorks; } diff --git a/lib/sevices/profile/work_history_services.dart b/lib/sevices/profile/work_history_services.dart index 99d7582..cd85b9e 100644 --- a/lib/sevices/profile/work_history_services.dart +++ b/lib/sevices/profile/work_history_services.dart @@ -1,11 +1,8 @@ import 'dart:convert'; import 'package:unit2/model/profile/work_history.dart'; - import 'package:http/http.dart' as http; -import 'package:unit2/model/utils/agency.dart'; import 'package:unit2/model/utils/agency_position.dart'; -import 'package:unit2/model/utils/category.dart'; import 'package:unit2/model/utils/position.dart'; import 'package:unit2/utils/request.dart'; @@ -15,7 +12,7 @@ class WorkHistoryService { static final WorkHistoryService _instance = WorkHistoryService(); static WorkHistoryService get instance => _instance; -//get all workhistories +////get all workhistories Future> getWorkExperiences( int profileId, String token) async { List workExperiences = []; @@ -198,7 +195,8 @@ class WorkHistoryService { value: "Contact of Service", label: "Contract of Service"), AppoinemtStatus(value: "Coterminous", label: "Coterminous"), AppoinemtStatus(value: "Elected", label: "Elected"), - AppoinemtStatus(value: "Job Order", label: "Permanent"), + AppoinemtStatus(value: "Job Order", label: "Job Order"), + AppoinemtStatus(value: "Permanent", label: "Permanent"), AppoinemtStatus(value: "Elected", label: "Elected"), ]; } diff --git a/lib/utils/app_router.dart b/lib/utils/app_router.dart index 2769d80..efc535b 100644 --- a/lib/utils/app_router.dart +++ b/lib/utils/app_router.dart @@ -1,9 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:unit2/bloc/docsms/docsms_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/sos/sos_bloc.dart'; -import 'package:unit2/screens/docsms/index.dart'; import 'package:unit2/screens/sos/index.dart'; import 'package:unit2/screens/unit2/homepage.dart/components/menu.dart'; import 'package:unit2/screens/unit2/login/login.dart'; @@ -13,6 +11,7 @@ import '../screens/profile/profile.dart'; import '../screens/unit2/basic-info/basic-info.dart'; import '../screens/unit2/homepage.dart/components/drawer-screen.dart'; import '../screens/unit2/login/qr_login.dart'; +import '../screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart'; class AppRouter { Route onGenerateRoute(RouteSettings routeSettings) { @@ -53,7 +52,10 @@ class AppRouter { child: const SosScreen(), ); }); - + case '/pass-check': + return MaterialPageRoute(builder: (BuildContext context){ + return const QRCodeScannerSettings(); + }); default: return MaterialPageRoute(builder: (context) { return Container(); diff --git a/lib/utils/formatters.dart b/lib/utils/formatters.dart index 8b45879..4db4cd8 100644 --- a/lib/utils/formatters.dart +++ b/lib/utils/formatters.dart @@ -1,4 +1,5 @@ - import 'package:mask_text_input_formatter/mask_text_input_formatter.dart'; + import 'package:flutter/services.dart'; +import 'package:mask_text_input_formatter/mask_text_input_formatter.dart'; var mobileFormatter = MaskTextInputFormatter( mask: "+63 (###) ###-####", @@ -10,4 +11,14 @@ var mobileFormatter = MaskTextInputFormatter( mask: "(###) ###-###", filter: {"#": RegExp(r"^[0-9]")}, type: MaskAutoCompletionType.lazy, - initialText: "0"); \ No newline at end of file + initialText: "0"); + + class UpperCaseTextFormatter extends TextInputFormatter { + @override + TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) { + return TextEditingValue( + text: newValue.text.toUpperCase(), + selection: newValue.selection, + ); + } +} \ No newline at end of file diff --git a/lib/utils/request.dart b/lib/utils/request.dart index 3771b9a..329b3c4 100644 --- a/lib/utils/request.dart +++ b/lib/utils/request.dart @@ -10,7 +10,7 @@ import 'package:unit2/utils/urls.dart'; class Request { static final Request _instance = Request(); static Request get instance => _instance; - int requestTimeout = 30; + int requestTimeout = 25; String host = Url.instance.host(); Future getRequest( diff --git a/lib/utils/text_container.dart b/lib/utils/text_container.dart index 2e72c43..79be6de 100644 --- a/lib/utils/text_container.dart +++ b/lib/utils/text_container.dart @@ -110,7 +110,7 @@ const String duration = "Duration"; const String type = "Type"; const String referencesScreenTitle = "Personal References"; const String mobileOrPhone = "phone / mobile number"; -const String voluntaryScreenTitle = "Voluntary Work & Civic Services"; +const String voluntaryScreenTitle = "Voluntary Work & Civic"; const String numberOfHours = "Worked/Involved for"; const String workHistoryScreenTitle = "Work History"; const String present = "present"; diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index 932de80..64c4b21 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -1,12 +1,14 @@ class Url { static final Url _instance = Url(); + static Url get instance => _instance; String host() { - // return '192.168.10.183:3000'; - return 'agusandelnorte.gov.ph'; - // // return "192.168.10.219:3000"; + // return '192.168.10.183:3000'; + // return 'agusandelnorte.gov.ph'; + // return "192.168.10.219:3000"; // return "192.168.10.241"; + return "192.168.10.221:3004"; // return "playweb.agusandelnorte.gov.ph"; // return 'devapi.agusandelnorte.gov.ph:3004'; } @@ -129,6 +131,11 @@ String getNonAcademicRecognition(){ return "/api/jobnet_app/profile/pds/other/non_acad_recognition/"; } +////citizenship +String citizenship(){ + return "/api/jobnet_app/profile/pds/basic/citizenship/"; +} + ////family paths String getFamilies(){ return "/api/jobnet_app/profile/pds/family/"; diff --git a/lib/utils/validators.dart b/lib/utils/validators.dart index b2c05e6..532e915 100644 --- a/lib/utils/validators.dart +++ b/lib/utils/validators.dart @@ -17,18 +17,7 @@ final registerPasswordValidator = FormBuilderValidators.compose([ errorText: "Password must be equal or greater than 6 characters"), ]); -final integerAndNumeric = - FormBuilderValidators - .compose([ - FormBuilderValidators - .integer( - radix: 10, - errorText: - "Please enter a number"), - FormBuilderValidators - .numeric( - errorText: - "Please enter a number") - ]); - - +final integerAndNumeric = FormBuilderValidators.compose([ + FormBuilderValidators.integer(radix: 10, errorText: "Enter a number"), + FormBuilderValidators.numeric(errorText: "Enter a number") +]); diff --git a/pubspec.yaml b/pubspec.yaml index 4bcf5cc..cc3557e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -80,7 +80,7 @@ dependencies: platform_device_id: ^1.0.1 multi_dropdown: ^1.0.9 searchable_paginated_dropdown: ^1.2.0 - + dev_dependencies: flutter_test: sdk: flutter From 83e5c9b4160db05746942c2d11670c5149fa522c Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Wed, 21 Jun 2023 08:22:43 +0800 Subject: [PATCH 72/86] Finish implementing Pass check operations --- android/app/src/main/AndroidManifest.xml | 4 +- .../gradle/wrapper/gradle-wrapper.properties | 1 + assets/fail.mp3 | Bin 0 -> 15885 bytes assets/invalid.mp3 | Bin 0 -> 20301 bytes assets/sounds/ScanFailed.mp3 | Bin 0 -> 4800 bytes assets/sounds/Successful.mp3 | Bin 0 -> 5760 bytes assets/success.mp3 | Bin 0 -> 18333 bytes ios/Podfile.lock | 38 +- ios/Runner.xcodeproj/project.pbxproj | 15 +- ios/Runner/Info.plist | 2 + lib/bloc/profile/family/family_bloc.dart | 3 + lib/bloc/profile/family/family_event.dart | 5 + .../learning_development_bloc.dart | 220 +++-- .../learning_development_state.dart | 7 - .../contact/contact_bloc.dart | 4 +- .../voluntary_works/voluntary_work_bloc.dart | 1 - lib/bloc/role/pass_check/pass_check_bloc.dart | 180 +++++ .../role/pass_check/pass_check_event.dart | 70 ++ .../role/pass_check/pass_check_state.dart | 67 ++ lib/bloc/user/user_bloc.dart | 6 +- lib/bloc/user/user_state.dart | 4 + .../roles/pass_check/agency_area_type.dart | 96 +++ .../pass_check/assign_role_area_type.dart | 52 ++ .../pass_check/barangay_assign_area.dart | 24 + lib/model/roles/pass_check/passer_info.dart | 120 +++ .../roles/pass_check/purok_assign_area.dart | 42 + .../roles/pass_check/station_assign_area.dart | 182 +++++ .../basic_information/address/edit_modal.dart | 33 + .../contact_information_screen.dart | 4 +- .../edit_basic_info_modal.dart | 2 +- .../family/spouse_edit_modal.dart | 3 - .../identification/edit_modal.dart | 33 +- .../identification_information_screen.dart | 2 +- .../primary_information_screen.dart | 9 +- .../components/eligibility/edit_modal.dart | 15 + .../components/family_background_screen.dart | 69 +- .../learning_and_development_screen.dart | 74 +- .../learning_development/add_modal.dart | 1 - .../learning_development/edit_modal.dart | 287 ++++--- .../components/reference/add_modal.dart | 1 - .../components/reference/edit_modal.dart | 441 +++++----- .../profile/components/references_screen.dart | 1 + .../voluntary_works/edit_modal.dart | 14 +- .../components/voluntary_works_screen.dart | 3 +- lib/screens/sos/components/add_mobile.dart | 20 +- lib/screens/sos/components/request_sos.dart | 2 +- lib/screens/sos/index.dart | 2 +- lib/screens/unit2/basic-info/basic-info.dart | 14 +- .../homepage.dart/components/dashboard.dart | 73 +- .../homepage.dart/components/menu-screen.dart | 68 +- .../unit2/homepage.dart/components/menu.dart | 5 +- .../unit2/homepage.dart/module-screen.dart | 3 + lib/screens/unit2/login/login.dart | 12 +- lib/screens/unit2/login/qr_login.dart | 22 +- .../roles/qr_code_scanner.dart/scan.dart | 460 ++++++++--- .../qr_code_scanner.dart/settings_screen.dart | 757 ++++++++++++++---- lib/sevices/profile/address_service.dart | 4 +- lib/sevices/profile/profile_service.dart | 2 +- lib/sevices/roles/pass_check_services.dart | 265 ++++++ lib/utils/app_router.dart | 16 +- lib/utils/global.dart | 2 + lib/utils/urls.dart | 18 +- linux/flutter/generated_plugin_registrant.cc | 4 + linux/flutter/generated_plugins.cmake | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 8 + pubspec.lock | 276 ++++--- pubspec.yaml | 4 + .../flutter/generated_plugin_registrant.cc | 6 + windows/flutter/generated_plugins.cmake | 2 + 69 files changed, 3176 insertions(+), 1005 deletions(-) create mode 100644 assets/fail.mp3 create mode 100644 assets/invalid.mp3 create mode 100644 assets/sounds/ScanFailed.mp3 create mode 100644 assets/sounds/Successful.mp3 create mode 100644 assets/success.mp3 create mode 100644 lib/bloc/role/pass_check/pass_check_bloc.dart create mode 100644 lib/bloc/role/pass_check/pass_check_event.dart create mode 100644 lib/bloc/role/pass_check/pass_check_state.dart create mode 100644 lib/model/roles/pass_check/agency_area_type.dart create mode 100644 lib/model/roles/pass_check/assign_role_area_type.dart create mode 100644 lib/model/roles/pass_check/barangay_assign_area.dart create mode 100644 lib/model/roles/pass_check/passer_info.dart create mode 100644 lib/model/roles/pass_check/purok_assign_area.dart create mode 100644 lib/model/roles/pass_check/station_assign_area.dart create mode 100644 lib/sevices/roles/pass_check_services.dart diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 390ee63..922eb2f 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -15,7 +15,9 @@ android:authorities = "${applicationId}.fileprovider" android:exported = "false" android:grantUriPermissions = "true" -android:name = "androidx.core.content.FileProvider"> +android:name = "androidx.core.content.FileProvider" +android:usesCleartextTraffic="true"> + diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index cb24abd..ee64223 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +android.enableDexingArtifactTransform=false distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip diff --git a/assets/fail.mp3 b/assets/fail.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..6ad924121076544547ab8fb22a85e55445a8840f GIT binary patch literal 15885 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

K>1F-2&crWk$(CU9=xhdx|Xghkw> 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%0D(GgEtH|g+|6f4jEM4wVMQrJ594 z_ST?O&Jho?qU+_pO0%yuH{t^#v8B3-N!+q&(S-GN(S#$(-2_5h9JkU+~xu zSKm_g8AG?gvnnN2`Iyx{OFAEstu=A66O1OK9`8;4er;1oXgimWE`0E?TrwGf|NPjV zNwx)mpYFh{R3UpsL57oUL)(p7gSnsCrSW2yvvaQWtnk3#cKVMrsaI>uYEqi!Ufpd6Um|DU^*k*fg3!Yk;5D zOOR>I4p{gqC~D~Hpx8d4M0LHj?S4Y-=PtjzE_EZ7QKdS06a0JhVnO$908bn3J@&2E z-`pwdyWiY`?St%5VVn2A=QZWElno6H9}>}rsN(;t?ESefdV{H+79Xjua{J%=_}AnA rPjr9Ij{b`e!30bJ02u!Vj6K97|9Xf={%4f}{I~Z1RS)@pT>n1+n+Ra# literal 0 HcmV?d00001 diff --git a/assets/invalid.mp3 b/assets/invalid.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..b7c5d3111d57935b2bbb4f02142c4cc2174ccd21 GIT binary patch literal 20301 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 literal 0 HcmV?d00001 diff --git a/assets/sounds/ScanFailed.mp3 b/assets/sounds/ScanFailed.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..7309226f6a682c576c5613df7c2e8412da30d693 GIT binary patch literal 4800 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 literal 0 HcmV?d00001 diff --git a/assets/sounds/Successful.mp3 b/assets/sounds/Successful.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..5fe691df844af6d35df6c7e4939a663ff0ff275e GIT binary patch literal 5760 zcmbu@c{o&W`vCAWc0w~_U&2iGEF)wsgTW9YOSWXsmPm^=+gP$?UlLi%zDLS#EMFIyNbxz~Xd7mjCLDJ6;8kPR18v^?v)b-H?bUaK z^0Xy+UM*}hyEoG62rG3md2_K;TZ~>81NTJc@tBoJI9;Q5cZS+KWq&5&uBU9+a#~a5 zLXpw3NkrS}qjL(A57i-GjNeOA85Zvj*kM2ZxU{XbO3g;dl_@#*`_{ySFntk*n4%z? zO-~P!g+=qYbLdZ_vB^#s`JLyPE8N9PDj`WG;nc(iA;CLND92tZNpS1Y5Ad%ALOwHxHaZbtFmI>e6J*Jk z8+C3|Mlzo1IfLoeEdDHxyJYSnl&bht5<-V&Tv&^yf%PfCpp!@X2zU~f z*3<#RLF+i~!Vyb9knz)&1cD27P_z`EiOozP2LQ}Fp!dJPucTlee@n;s(dzTbLywBC zS@Pzb5lqdFF4oK`kYp@Q-5s-+;q3i}jYI9qFfIz6F)8LoPzOXZ^#lmfMM~zra2jwn zU`Mq#CY*u+M7Vi)KT52Ml9X9RB*_ zud9L5(Ht@VO67Eq^v#3yx=pD}&*F#ZjO;b7(nu?(E&lWGdA)ghAXsic_oSA;f;ry{u zt1IU}NQCLg&V2iwURs0mUSC={2`kqBp>w4);;p-l*+msDJCzyI*{g^7k4Fn`oATXN z`ED_KR*;WONy+4us7du) zK1I~qHevPTqLoBr3DH;BfchU+27Iw5JpF{)=T0)_ihTy;jlPR1b>O;vf-gqS74>PG zgvy%5*4JxRVYfWRur!b_gtVp$0N^BF`JROC`tF{95cjZ*!w_%mocwQmoFqf8UB2L0 zC~oaJ!IGX?!%>emk^}s-#K8gXc-Wtl{&cp6Tj%kqz)545WKnp!4rAj@ULWRGv#Qu9 z55T*SFTq#BSqLCa+TKI62_D|Ad$e~qE33RThaH#p&bj1QVoSMU{B?G1Vf%je6!M}n zZFs#Gy(>eqj8)2M1|r6usaNhALAxL2=leO)cL&@HqoiP*z=}E+_NP8s*!R)(0D~n| z|NkC)b$4rPK36>oHha{Y+SfGB_O zLw@@mW2r#@u^_*JCoKketn87+XKX}llo*_5l{VGE=QLA~7H4=Rjw{@-7hjI1`lii* zXm||1y4Rj+GSu26g35pm(XhgCD{!4M&l|tRxnssX*C1aIF~Qjk1jM)f=lt8UJ1v`^ z&3vpgi=@F|G7C~Y)0=)iE3{LbKW zL+nP_UM1uoN5~mG0emboW(SuyE+(}S3ywM16{PJ<3Pmh{k!DrfzJszl0fJ4bVXxwYJU8}MG+F)AQ=;X#HTwxJkbfHf1KA8{5}#P`6X75ES-^$= zOayrDr}1`~DW{`+6R-DxYHc9Fnc5h|hbaGsOGUvLQKe#p)PTq%-NiC2?@{W7?b?aa z4&%fn_;FTla2R4Jz9Hh--}?E7-M`)G`pWgQ+#GycB{^3s@DmQL>lWRpX=QUH!a61* zK#bahk)cJD3Xl{=poK3mM{eU7`?bD>NFo)%LkJ;{I#q4U9MtX_F2exMwn522G;h^20ZcR?^2nI%gc{(U;+Zh z-K6k%yHb-)W9RxCeI=}Uf}3a@wmcF@IZ*W_S>?b)wY2B(D(}?(^2c}tHCZ; zt8R8C%*omYJ^hNCpScD3zDQ@@LV&@bLytV=agl3oq@tyY&0Xv!x569~j>b`QnA)gv zFDy18TnxZjMRRPG^0^e${enfLPgqSmISVeUtwqtfoTV+RotU#PA#OONBrudG#>D40Wv$yfE&psr&xYcm$Cr_o4%Du$Oe7-iunQ2Ysql==HW zFv&MS&^J^qj50sJJ-@VWnXxvwySBZHi_lTZWH@ETp$lM-d(6I4Ss;y?8Rn^PqM0(b z8Z)Z9ei_t+o5g1|Tws$Z)0IrOz~!vU4_2Fd<<@=~-h%vWwp6WBV8Fz;EHJsrn)gXk zTsSAu@O?UGH-pU0BKk`$DIWPxZ+b9B-P>nkeff2SCJv!*!b`2S>3jfsG;luS)4l}3kSc_haI zC4?G#0~k*cN8y+4JVLvELf^lC`PKQ=W|;BNF|I7#nK#!E3VXPUA-(}&u6 z{gpOj{p-J>Q-I+8Va{AfV*z9FjCDqO32|A`k7T})@@dYN^DB^_%Bf$O0?helJhFBT z7TvjjRd5DV$FAN2n!O6m19xn`lFgV_imkrEe6FE&NkdB?GQJLlf%m^sT4|`lD8?6_ zKO^mvZ!2x9^s~u2H@BHI`svflL+?Y?8)F>^t`7 zRZVKIDkz&=klxtY54E?$ie+TTo`{g+A1Pk<&O-B3(Gu1^V#f_$%%)g5^okI~4k-MUisqCs}Cnv$zke?uUmNO3MwrV45fK6?hqz;=daQ)J6kP;yH{yq5MuM*# zlV+NICGJ{o_`SMb1)b(YA*qMrdc&_VSuv$)v2S{}L$eE6W}J{p^nt-bv8XWqr2erV z5(7fytI5A96%LI<(uTgF=a5N-h$xgF1oDJvDFE=cU%|dzirdDMNTmC@!*iZHK@9e~x(}La{K!>(bPih^QkEV?qa&Jkq~RQ( zU*&V)bcXy(eLibuTEJj$(9k8(>WwTEzamerZXqyg@4NK!OoQQvT~KN8@Y}NXW2>p} zi{^CT&u%O=@@p$NL)I>cp$RXnKw#=m*5edZ`m{5_pG}qCo~}jRdDqLdyHAQS!>GnP z0iCFJW+s8W&KGDk$VYKCIMoBU?K9fmu}Bs81$XZ1-zY8GF50~}2iy_Srn0)in4ZU=-&4wJ z7UK{39EcwKA;9ca#&?5>6O@#4ZU%LvR`^3*#rG(bgvxt&;D8h(Pr5V5RjPxIBlBC(r(!y z<`>uROQ6wJjUKvDylFN0V)#&SXEjoH73PR1$%lLO<97RjhqUL%T#K9Tcv706iuapi zqM;sl44n#fFbK=+q)~w@*D`11#s17M5@FvABv@r?HGk`H`Bd~wSB2g>kJ{#_pTD*R z__If7-Vl}S*&Xk2!%PVnW6@R1RI;R)^78Utw#bU~2~j=a2nh}~Vg&n2v%8I0oP*Um za~5G5U0EEd1Nj;V9-DID`D|vd>b;H2m0*vnM8AiAr$?UOqWavx0!_=sKdk@+_k*TB z7nc^Pg~*f}n*)2;3T{}qdVv7rG7E>D&8{VEu%HXIJw)en&PaPX_|G1D)w0A` z&Ds-n0i85fmamsX#=kHTrrr&f65{s<@S0eB0|1L-gM4cwNt}$*TeUPHzO8-E!Y~#kLXYZxOAss+sGDJs-OsGRyIb zQl4X6?pk!w`Gz~Cj!=8OLp=le&PeOx5Ma^%&)w-)0y+5inquONf7zKhqZIS>Yo*N3 zTUI@MUPiA?%KmuZar~0En)nCG?apX)NLf4ubSJqf z!HLo}BO_9tvP~P*A|Ib*g*c5}VT`D06cN50GxWWKyyBf)ZP%hRF$DR(sC@wfz+KSJ zRAV)DEm;2eFFNtIXBz8YI3ZydmolDn{Mrd34M=%Xpa zH0T=>Pmo{I+8x|zE{YcV{-S8S8^dGOD|2aNX2dkAue|(+3}plO#@03W+vf2!Q~t`T z@zbvvpYvkQ5X+!U)c2Z^pyVX$p8Rc6x!cGR8kNwE+_1v#|ewyH~w#7$NBNURTa|9ef1 z{dWlW4u}8%-p#t3FKNH?TU!zMdwqZ4FUiGlm!71%EKkC;m^LmIUdKgpya0ECqIs22<{kTr&qh~V$;+g+P#eOk0Oh+6_mgh`|&rNcNO zY+0N!F-VrwS5eXR()2H-i~hU5{EPABTup4206}QK_~IZd8G%uu%AB=yA4P18RTn?M zJ!%0m4aN$SCM9Xt-*?jAzp!E_CKhfcj5+Ig*dRd;zk+WaBAA12N5&B-8NGUB>_7+7 zvSWdU4LT44@hC$_C;+jO%4Gj@>+#>8VPWBepdekbg!j09Gt5LFVEZ))@IWCpH?@uH zl|o{~l%&sR10YcOh>Do)sttoOWh1NHNo1fU#5BMBpJ)8 z_++=^ZO8T`zO)(ovjkdXY?oq}+d7FUthkI6A7zdY!NrI9=Yu8^{yq{68Kf+B1!4XtG1Xl}LZAeAtQ-$l<`C`xHVlWYG?4CUwIE|DSxZA2|%% zDX}3ERF6F;k%sIjjqqwfDi^`H%aq62O&73nD(W-r%*!*z%Ejz zM}WshK10_RmG4>)HQ1o7q+LLEF9rD5AtZk89 zK0sK>tK2YLC5Mak_JAZ@wi+#yHk97O#U5KhA8IItgEIk3ID&!{wfhVuY;i+o#hp>X zt8G_l5BBUd@d8x<2??C}nVg!fQ|6sXIWfCB$OB?Jb3iQb^P0S#?EmseOmJkD@rgsr zkp{0KJ(mU9bWmFYC6QHIk}JBrrBUkb_3ZN*yuLX09ysfN%6U*(;?v;U=APH)9+c}h zA6SJ<8yp-%-}WVbijPEcekD5=C}qp~v%M zRN-(m5f3qno`N#H97szpb1Vui^wT+<&ReUVP^(lpj+f7R%P0J9$vFc)+mcA30N>Y1 z+f6g5@2RWFE-D5{@tc5kx~<}HFf%GJD(kDL++ce9ZZ&KhC&2?Q5rXkTReA$cWewqL#sDxTs5Y zIr)+;e;7^_UoBq*|*|=I2ck$?Al&X^|uQ#=3A{^nS=%w0*=WxLm%b|!D zIX6whMiK8w^A44#ljq`|otvO0%wxXxx}U4v1sI<kRCT)E2yp>G^^M z)yMhw`d<{Bki^-3I=wRlx=G9VXekT`*L(>yo3SFw>t1pTR`cUmTl2eanxK62a?9jL z|82or{U-WXgUdD|UQ>;c`4Vfp@B+?)mQLn7hB0s~O-(!MsRFsv z5S)i!&InKyZ&Q3qEf~&bfKl(ym2X3qPLOy&n&b1TQ?-a%zp})Plh^kWfO3<5b}Evj zRh8R>SHE{RXev8o8?arkVrp^95;J?mGb1=<*^{lAXEv!ewz4l;6=$1z;5lwdueH2C zZqu?6733J9y7pw_j}W^oJm}x}l7T%F!wPmW4Qu*|H%5qIl|{b5;l=NTKduSEykseH z0d(j=PJld8mE^^O`}w2>y(ZhAADs0hiZ$obpP>Om#fhFyKp1rcKCRto__hPcM%QWo znH)k!J>>;o0kJ6Pphg4vhR28}fmu!^Q8L4C`FknVM&_odx}abq$0O>=0iN{6z@s$D zM}qq5FX}lg!dEKX4l`7?={fAk8p0)~8=Nog*B(4FVq4ty7uSx=ls)tAu*eg2_Agv- z@GCoeX79Qp_a|bbebalRa~)!2X6cfUZMy33U1TcQBxtdcIq)m-x2tHw8!lJVi|1PR zc&}yKS?icSVN41?FsNe{$!9UAWG{azhs9zT5kD`>Ia^9C=~7Q3A$eOh5q5?0C#m8& zAdwmbV}d|9a^HcSG;3cW0kT=KD5*Dv_q84?(G_*-lyrOJ2oaDY9%H8l0*&z$Y4P+l zz{A&AW2&(;==N{zUGm&?m#DEI^r5?+$#b+lMHbQb7hQ}BN7Q4_lJVRn=-3S(VX6fh z!t9=V!SW?36lx3|Tvq>b>Dq=TGYHsSYf_B9L(P(`H5`ZTDm9}(m$INQ9;Laf~6^J>*0-M z>Kcq1e+IDTN=XLX6^M|mce?mC40B_=z$A7e2^;2oRXdeoY*Nndu4ZbsQgP;{ihw6t zaM7~xKzV7&rC`X*O|+7Iojk*w!xjdZR}xFFCA663X=`2@@T1pp$&_Fxx8}xJrt3>| z3Ba-sE0@Ih<9QB~z)TCH=&;B${TY|2nM7r}7`7-sQHmP}EX{_^C)w*Z>BV%|CkyX0 z1J{lK=61g4T(_=^&J)M3dZqJtfCTHM6}-{XnuJw__?5}6*@D83I)_5LZ=8u3xB+<8 z+WePd-N|&f-IsaE>jyz+FW&?^I$w=UHyI4rHGWy{&?*s#^L6i&{G~Si?o6s_i@kVxg^KtMZtbCUdH*? zE>JU|zKxdPR(dm$k`byFs^9PMb5i)@>j#VtQ~p&r5dDa#T$dLQ;gbsK3#Dr5ne|Bn ziauhJF_aN8*^O^=KAn%VPAV0?C{o+q6+AKgH2bDgpWzp}Wszrk=@I>>nFJo9HQsk4 znrxII(hFxIN1FV>S|qZs+cJ|t@tHW12RMlDn}dSgvYcu6c#p(HcwHG+wr)O*f_#_tWC03x)}#-L#;TR*^SezZ@~;iy0*uK& zngliB1NjFPnr%8ZVqa~z07JWi!2PBkBK|m8QoMyCE4$&U(ZD&BxuRlo>1eg4LCRbC;vpq0E@3Dgj1%IVO==UyH;g_V25OW@CAQNZ&O}f1s-PECA2;@C3P35z`hvuPDB%~q)dR4QcI@8r*cb?AKIV`SLc?6vnPL59OL5NeA0;^~r1Iwf9A${x*Zx28T~IvYi~-Wv;QL#6mY zxQ9V+c+PDB_Mjxl$97l#UfVDt;>QGlirAwdVZ-+D!@jLu`|txOBMFFD`qPJF`t-Mb zPtWF=Qu^Gbyfc5hThiO*Oy0SzC$Ee{HkRBw%0q1*U2w8(QW`tp`2PZE@F^d>UG&P5 zr52g0bAj)cK*H!Z6nfVU>4oNp(CejpUX~xJb(FH0pNn(ONrw4luRJ2CPTG-R+ThSW zh(_}?ijebTGbtEZ?`-qv=uV2!xR`h->x@V9n1doBx@CpVH9lpAv+|zbI=dHAxqrsm zu!t0k;t`(?I(eDxVjKolTg5=LkKn-4{pe-sL|mWPT;~NL#<8GvSxHc<0L!8S>+Bxcm+gNIo%b4$ADidAdnwxN`b#skJIvHV=)qOIF~)&HR?C&93QkR{EV>6yOlqEoy~1~yU+cy;dcFB*(akCkRk4Nca) zU{>Oy4C7GcY^3rpcJ5?u-|1-kDTb9vqIF*#Uf-V^siv_Qi!K(E3(ZL0bp9yHYjzhn zYGt%QujHkA#hg6%daiGlXuJx`Pg#|^X_rL$h{+6#5dq=YRkz+RVe0Jm<+RhBS&?Tv z?Y=s)PPf=|O8#&#`}fkztcvcQ1K~uJd@Uk59iCtI$*@K#Wip-TMA>VSISoIrlREkF zvuoeh#yzkWyL}u@;?~m?_a|EEQYW+TDxfu=7W3Tgw>Q=&DvHhkQPA!Zxh=6RiL^i=cBg&^zp1`|?U_w#Lgyao$%eHPzA3x9?@_|>4J z*E(j}VU8}0+PQeL9fWnyu}0o}%u(#+?tCUfuDjYXVUs6Nq!x*^b!&5-rw5hB zYB5QQrn2`z)VVdOc&A*dwR5692qtO_b%XchM>9%4qRMZT;4^Kl6+SiKh6MAjbyLj| zj(q~_4h=RMs z5lL1pU(jy-B( zt2}HuoEMZoXbYb`?0J+q_HVc=Zu4)I(B{slCMjT{C1e!}<}9+0wotW;bB;}Z!5KS} zI)6Lv^0o1X*36G8;Aqk1ef^<@^9|>l?Gu0LWB7*s#D>LlBU>)R)ry5?Z_n-5D;5t| zEUBI2e1i^DyIjAOCkJD)<=s@Mls2kTS(Q2-5QtJ`VRhIKP@9lq`Y^iDGZ0ypqk_k~t0s(Xf1GBp%7| ziy{J+n}OLddHRILAC=Nvq0p=x@r)2c)pPu4?#Sj(qQ_^CEdRq91E(7Mun~dsndw2( z%7@VdfZDUEcX98Yxh4sketP+wxn-K*>9=p_=`PdYvjGO1fc?+IAzusb$Lj@EH>uh` zpD_98>mv1_Chh+3f_;4MCL1E(8~Q0m-OB?i6q$G181~@ zBHl3cive6@xOoJZnH)pGBJ_tCI$6%{}|82W%+-~og^B@Unp z8V*$eTLe1Os75hf%~&qIGKn!w?a(IO^M0?PHsBIn5=Wc{JFH+2+j5UU!CwLmU>+SMd@XI9G}mt>*D=2d8Exe3Q5NV$SW6H**4U9reY+^X2SR zFoZgXc?(}kc^{FMB4y&e{9ntJGn5)`F6yYCAFMJkc;@2u0`TlVge;>8kj0BU!A^8Q zn_=ft<=AS8x^qi}^8DzB5ex=KM98n0@)Mnud0TbSU=Ll#+il-=V;Q z$;$QptG~V^#z=s)}tV&&uIyV9Aq~=!pK9pepE?4iWFBaJSIrdVf=D$-LSV{jz zvO5oPU2%^Ayw=fQD;K)PBc|g-rVcUw8?*Jqn$2C5W#=B@H-8Cu>$uxEz>j%N^0XrB zYonwNb6QkwSvN|Aho1$0U9p@8Z1JV+aFd8mb!6eMuPx2bZ$1^Ao0u@IAyw^ZmNepD zj-k1V^L$?sp!8y{A7r}u-$EKh5q>h7jwpEG%aTK;sjFN=_y|9z`RN9`5iyIMqJJPg z20w~8e5n&Z9zpzBc8H+r-*ZagDgw{S)}pxgBIM#PYmw+ z^TL2hs`XFEqjS{hJQr24~81PJ2xKo`}Z8|Vii4)qDf8-`)OH;FG_wk+B!sLUcs}8JBC63!5u*>N$Z`V2K~NW-Qm)(w z6mecUfWMrGH3U`Uw*0~gN2TP%|LUU~=Ll1<=F7#E+O!7S#H6U^-n`S=o}5>^;GXA7 z40r{x!LuxK415j=UrFZBYKI@Um-%Ubn;<^k-e+Y2hDF~L zbDL*@478)*7qBpFI2J@WO9N}$Pt`f?-o2Vv<9E8Iom>*sp>WtXTy@!Njg3`vIjPhx z*)4UdM=j8LIhQzSn(G?8_i9ibkx8v|{z~oFLw~I&m9B5hlRiqBwe?IggQmS-%y}%+ z$~Ug*cN^mwy}*<-_MN}~e+U(*oNiUUgTdLG`hIg~R=7iMgNuL6YfV!Eq=bCF{5{?^ z_&M+RKfIF4627z@k>ZZtk1N=4WIl;3boy8)`cqG3PK(REB^yaMvJ2uY%zkP~o!ml` z;+NU}xZ6-h0U_)DFafDo?P*Gw(y-zPuBsILHR2r>o?K?v%I_jsGcvg$_6iw0#ZP@X z@fJg!S=RZYKZ3G>O)-X2tz)+87x(0*=(2(GG<#~fchPGtQL|@KHP=3$oXZ`ar%9ww z`_vR#EgnD9dj&T%uywpfY}$G5g&O$}bD29G;uy)i3ET&PHk+1S{K+I5p$ zd27sh5^HI!?`ZJ0OMJA2lqw3Oh6T3UL`K@&`x}?AJHgt!=a8FF@(d{|vQ|%RUM~F| zF#^u5%8+l$kq`+_2C{6W&$4jU@?LPpqPbz@=Y=jSiZXUF^+}-J#zCl)GHvGWUii~} z{z?%F?($(>c_j*B8mc@O&vw(N!^|X49rlR}6hF{?EN(sQ`P#(WI6ol}9W1}E_{2?* zAxG?U@aO!KSc)SO#hm%!IVshY8V)X2Vdt6L3L5;dlf`TY&Y+B|oJsXy5UqKI-*W}K z*bI?n#o`@6{fzBBU+;ytq_TX2QCq@^+wZtIgrxuydePU3p&6t_&;`bE8R#i@fb-6hH2zcg%SvWpb+NM1HcEa?fKO;Nghv1#bd)6C?I zF;w!B(A0|~W{5JhEfh~P{f|w@#37}PuEbASZarF5VS+l|$n{4o@4xsx1N%i%Q7Y}` zNLL2?2wr_BX)=LBxql?SR$Km%A(5?UW+dZF;JO#OBXJ0D?EpG%@1P^k{0I5AW zcZH^|czWlu7qGz9Iry+<9!Am6l4IMI=Mr=DG8 z_*_}f)U$lyg?3GCmXtaVjh1coKYzF!dx>OcP5bkSa$u5KQKMyZyQ0RxMd$1F!9I`q zmzfWBNOkQ|2!I7((Ro?s4XX@2LeHhkmZ({S=&{LkI}9HQA&u3&zyH%ODV`TY+0Nw# z`;$p+F>L<%l7KJr^_gj1=fJvoEd_J=6keDhBImToWU1pRqXoaHx3Yke@FAy<_6p5h zjg+&gZtZw1=XZ|WBn6$*@DO2Uy#YJn)ewgtzy1p9adh#hxcz!L-i4H|;B#Z-}_CH(l^5imTWx==BIe*O$ zjGe&X3Y5C0Iz^ghiPt{&cev12VrxoAc{I+ld$<$|YcO2oW&~O_d^;-jg^ypQF}^5V zeVNrX))o0glDg_W>+V)ysjmF+RULfjKqcNZ97N{3T?X9x__awHD>y^8y{$MOxNE{Q zJf9<@PWdCy`XUc;^&K7N2D#eYoHgtW{;|$TuNfKHNKrZdis#z4kD;u9QeHV))%ts^|&OPBzZQZpEYdaZs7#+Zbtk2u=#Y-~T90 zI60roQqYP)mRP*H>Cx6iMdG+H?VZOp|D_JI9vN43uv)j@2#$I2(d=~BJEN>sCL{Wi zy=oCC#Qcc(v2qyQ=fg#qLJ67KkAA{Z*T58g+`9gp#V(`{XCu18RGv&M1Z``x-u4wbLyLt~3zuD4r4oLo@Bq zg$*?I)e@26C*4nBsn{DD%=YDc$N%fCXjP{&$ggyjhluBspcIr?(wO!S%Wvd#2@Be_AR3EAnmmS7nd<2kgx$#c0%q zP)g8UowXI)#T~uuD9FEi{fe2TFNau-xhe$ z@-JjD6K`&ssi3S)Z_ncK8t|?(s=Z40%{-~xyJ`O2M{~LO&f^dL>fPsG7_@6&{xG0H z*DR}ig@;eS#Hn>SWLAbf!?*UweZYZ_Uu)}R%2_NbzbME+$s)u;Wq=FkXe$+_m3i6@ ztdg+qiHN|kq=i#~0g*FIc+>>E1jYbTILOi&rKyO*Qw_D7#@E1)1~HJr34!7P@m>vm z$)}EV_v_c6Jbk4ctP0iu67d`YVh%UTtS!+qP*VG8$TjazzN^d{e0qO|O^?Y`=%1hu z(JWJ|`q8Am?`7I=*q3#QSW!@HU4F6lmUln zC%)HjsO`^xe|OWu)j~$5_;K!H{F4jNn2bX0OW_sog2$8>=w?Ni+=iTwV&$?x#Ee=W zn@9z#?oSQV=~1?Z~Rs zXzKY69nH=GQ;*HuW*?=;{Ku$jmw!n&IdS=(006THgXsuxUGv}-Tt|QZZiGjcluKSx z4LixXA1DYZW)$Id8wtgINDQtb?~Wa-5%$l*LqbzWR3>o4LgQo@-1>;QTDRHPL#3Lg z@ACHmMAgv~+ysKdOZXubHKoc9&V0JR@qjR%B%Q!(2|EG+Q3sxv!$a~NiD=LU>VX7d zo+zCs)iV^K&U#Mh%{_Tb1A)TRemD}J-)Hq1L? zLV$Xm^}h|RK#xyleq$q|8piMWdS@>q<^4T>4&XnM^+B^e#;ZXl`5**J8?SL>BtrRD z(1?PF?Vev%N3%^qOBb+v;MF&g&fH(V&oc~Nk-O~J>u%5kA1Hg5PjTS+-3>@X#cyD~9*FS|mG@}>ho&2GG!c>5@ z(|Y;CydhanAqbJoctRKca8oMTHqAC5qgRj1d$ggKSvFKw9$cer&LOTJr1(-;{4UMA_z}_vWxw;u?Zg5dCvH}r@ULscIHQE9aqNdvxgrudpIk? zhtM)E@07+5yU>N~^m+X3<T13;u+d8mz$?1MUc#Dm_b-?XhcdB?bR+t8;+~bs-EKU;990t@L>KWk5nj<)L?DgFl|&} z48!wXcyhF&1?BWFn<+I;Du74xW0lEseYU0ATU94$3~mJ5b%yy|#^(d&T3Cbh+|9{C zscT@9!J|60Rg}xu`*?gsT~Vrxc7*xvz&4tV>2Nnr>&R-x-jy}qd|5f%8L?!?3ePas zi$WS09z`i>rTr}ABSJFg!Qv#|auND~Nv=`@ucR;ZqlNgKK|11ZEDCFW(j<6eUac7y z$U9ng=T6o9S4!05?Wx!cuC80|Xt4Ik+W%x5=imRMw~ZVK5BWsi!wvT5I$cUjl8aWc zjZ+y_;Ah!=p>F}BNG^Mn6V9RBIBJ)~$vgvz@fkhqdbuGMclXF=>FPzV$`ZmiI7iCi zeS34NbXCWb=sNIap|k^!*N3Ut)trha_RphA(JzBW_%@MVLdFNa*Y?LFChP9d;bUyO zdG^d(#ja_uN=K$XoqFf1=Ojf;v6j_?s-}U^5&L8C3|=((+$$BNXRM{-NiWcRTM?-< z#LAaoi4-g>Xi5^>NDJ|tvd6|~Q9*~ToOwB>A(JNw;M9Bn=kt>-sW;3cYcho)a(^q1 znH^a7MpieXMn&J`zaH6#^1@^iS;aM(B1d{LGf zMlYOwT})Qqd6TyY;|a?6hld@y54Yp^4dr5L*DDxq9j(Ti)nfxzci(b@g23dsp=TAAgQ?@+2Ifo zq3F1%nI4)*@(0S5vJO+0oJk}uUbM$iT9~RvvAb2ijQ8AEtx@HTaR-Duxwl?cE3b?F z)>0&4)6Ei8-hR!;zHU_Sj@JQgtii3QDYh=)II1dAGuUm>)%G_TvZLLW=>(_REBfK zhmq9>*jX@ttA(&TYkDcm-lkXzlN$Tu5k|$xx}X(PA$4w3E*WyL5f5C-S<=~A<@;7| z1ck*UTcKXWE4>v14cD^Yi^mO5qzQWN<#|b@owtwaImyGWoWCE=0XWPy!L>lK7CQ-icM`cRJY#Y6*>*;QQX;iGBr9gA&iha?)$mBUV?$ES zG2FyQ%zx=y#+*x%V~X8govn6vf2ni_q)1wH<3ATc-q&57Kd1HO$9cjL-Ja%-;9(a* zx1f8x2f`Wf*1KR${bmnu3$Wv4FM8t6W}ex09%5@g@hH;(XB->&?Iuo2mH*u2mm=k8|@8y4;y{1 z*!$UdBv&l9A+%Y6aeW~y0k{Ks$r;t?g6j9`@(nPd z?bf(}jZ*n6?6B?(t-MWJwDqNSzo#g*gVr}B52Jq7 zKO6BT{DT`Wcaixeq~i1RQ#S#x{p8jhA9SL^wbu>>0^@L0r(>7ECBJo zTZbuNE~)o-j8oZEUP-L-+&X=mpti3z1%{xuSo_@HzS$^g;Bw|b?r!W*hlfwyReR+9 zMRUeEUT8TpX@m>jCXv32KaQZ~M7iLZ>3sQn`0~&1lV;;cO&xT-If(q(y&1LudoyS$^GdbV+c_BMI@A1OFcTZ1{E zj+={Nq9rcmK^5zwsBdi(%PfG$-bQ|kNr6gSGn)A~OEK~H^6*`f_$dGN;-XTd!e}du zqPbUl{xdr{o;rG%071~3L*%T3)-ZF(+7}DB!zZv18 zBO7qI1z7!GP~Se~lKp*;_v;fpsl?ZtB$#JKe}3nb5CMn}GB5P4KVMBU2!EuXVELCT z!7JuEzH7b6gXW`y0kARl92;)x{A(v0`gkioxiP-C<5r-rfnfa^>+$XP0jUNEM~bhU zs-e^q-5&8WCOZDU%Zya@6D#!O3B$j#W*_MaOn8t)6~+(}aw^1m!F|3=lBW%X{d#D7u`>F3*Oah?0>ZEreewqnaEqdh$oH3fb4l=7R`5^=l| zF-1Csz3!y@f&ZsB=|&Ruaa&sI+$qD92dAWZyDF6*Y(8BIddC=+x4BjTYQXpMza@Jo z70bLy6&YUklh~20*+Ypvsoud&;IUCqm*3^DlP%U-o@#ag&aVK0oUm{=C6Hnv?eJif zawW8s)S3_{Ee>w5uqqHgn5<8wdw2a)ob$4u88|k*7Ij^^zRx*yEjbv0qvpwivK>G67cv_OkCl=^=bn>Ujh4(*j(S+jT(hd=gurL5? zv}rkQhZ&G87s_tdJJcwFU^3bQ)yEwnZX`LJRVR|UG9vZGy4JS5MP zkgj+@iyj1zhdq5_A{sAT*fyb1ktXlS_PE()IIu>2-QVTuG`BPHPN?Cm_bW+(ZRg$l zcy`|&@8ACs1#@_@eGOB!nNZn(|8;OV>cn~%(*3O9R@8%wa_pA>wtt#SScyYIfUjk} z??4?2FfI}K_0nF4N5)VOnkmjzN%volw1d6q!_FpNuN{eqKZkA6r5AAJI5O3WMPAcQ z^EyJr&8LMNT4xoPg{iZ_`G!SZdCl!FE`rw`?su^o_Fp>$`%LxZ`O4#;JbQ}H`PoBR zI&^L6Fu_K&Z^IhMk$_c7aVY@)>8TX%Y@lt=j!t!_{ClI8eE+nLPkggp^6U!TA^#H}0~exzbbi(ExA zd@S*<%bz2Ky;5Vf$5wB4{F7ysE2fAjb}^MP`$PjMl7q^og#ODotEF+)j(E zD_X5Hm-a{IEc&f?1}hiX_-n)?-{14~-EB$A^-Js;wrxlQqxJ|Af^K{qx0d6Z37zR%dtse{ngGC)Yru zvPpN*G}}BULy@C3(EtFT{q8IYKmw5Cxvf4vWC~g8dR7J_8V(3nuq{yAm1n_r$M@N7 zF@Sgm1I7!Fpb2AP;dDsBvFQPz9El+;brkaClsQo`^s2BFpo0<;gwSUhlSj$mQOXVA zSv!!^W`X5I==cd%!-k%P#l)7jDvDFPo3Vsmvt}1`9zIxt(Apn6bOsZ=5{9D5>ElBq zv>`3#tJJ8J=C`V$2z3uyuo|^!K(P)KP%Rl9o$U;<)5^v;eCwVD+B{F%)C??=qV#k8p|w7k_Wr-}7~3zjbd9*IAwzoTOztwvr{88+Hx11gjrP2F>8PMvD6ClT`&m(J&5-q@-Y2$+w~zc&-e7@19I%!_ zm6no#m4417NA?k$P}X?`@9;MIrv^)McT0`NFCmga817gxzX>+Pu_W&HEt`~9%;ADB zNBsMVjlSkeo(wO*kyev?%*f1fW(lvGvph%m_ zQ$>-`gju?DHaC5@0|RgXE4#B`hU8oB-mxHb@98nXFGG0m3NMoEc2j8cQQcw%BkXupu8JCjP~Tc%hjQ zQY<^dCPlnaKoBT5GBGDq1ol=T8fUl|ztegW$9j;5vnt~~xgDDWGv!+1)(dX1H2_iK zltvkb7@P;dD4#_ka?H+)lowcJc%r`HBu870oAI29$aX*^+3S@Cq&F*Pe7N!(%Sm-c$ z{Vh7Z0mxj@2!}UgcNG5Baju`!KoT{CQqE_~6!6Fs8J@uDeyAzi%!O7j9q7x`>wIpu zMl0}!s*@44_EYOOhq*iuhVcPkHbFz9^vGW$fiL50foZPgEMPp#rLj_ah*R3_upYIV zO?+CMl3+PcN=;U2c2!L%z?^yk0%?Eda;i5+iOi4;IGQ?|Hgeh+mogoo`t5yxIKcXQ zo=jAE^WSSM_Q(b$JDW01z0pqv(g)2yI2ntnUg{`hgN(`3jKE{ z?+8A}7UwFJGF-R$z*ViQ5vtxVb#rvS>&mG-F3uzaH^5D_>?9bFs-~)|qC!GmyGvZ1 zNvrAtdlDV#$Iw-a@A*PJGBLVku5>r&7RUGg|GOtBDgW;}vvtQd>kw?N* zMu#2A=mv3-!|gM}tgkJ$Uv%GhGMVAIwV^f)U&(b>>;os@OR+VEWE84#7 z-+;adXpn6)Gj&EZ~NAo82HFn&g zK->DEWOyF8Es2g2Zs=j-hEt!eu|RiCMy)&M_2c-ejJl6#>1~WaTR)^Cr2adeJIVtZCgTGY6oa8rA5~{Jra5A1_S$;ZGmsPXG<|OBytdQ^O?I z`+P(8UhxTAxs8RrQeLMeUmg!;$d>+M3eKQBeLg25yGdK~r#{QyVY^eF-d>Oj6^1^h z_?gg`-q>IA?d8RyRpQnMX-$o2E3@aHf($F)j*I*BZ1>F`&-#3>Y#6vsr(XQe)GYt^ zwEwx$cG7p^pd%nhw@E~_k)3zS6cZ#mv)5dB0RLBBLl2=P*tcT+GeZYm)-S$DRM&fA(9i z8wr>L)GTg$N5e9>Pl?pNJD)nNugZg1&|&{_VRAvEoeiUP;Lw;_<745t70eX#|Cy^& zYTMEtS!V7j(H|LgI-_PKCEI$K?2;Fsr}qE!%^Lx%Z=96-b6ASEto8m`&BW+61b za1~?T#Pmm|e91u)cN%YnY*KUhur%p!{Wa@WKGo>5&P^+Iqe8pR%n_5hF!PFek=z9P zkF%^m^DjT6d90G<=blpVnrN}R?~9Mc+^y2xud2SS4sT!B-TQg6T7u3Z$<53mxeIr` z;G1mmI-y!K*Lgzl$L&QjN3YaADf)4D_SWo+7f-DAamzm}BKoh({d392+56Y*uZa4y z@p}2GUH2@Lrv;X6SKjddvWNceNym#KKb?K*>0c+iy!WceimIhMa$Ji%^qPbe&7NI3 z=+@})%uFYYq3o<$kV)q1Wq)o+d`Wdv zJRXtGVmu-%4s!GHap|#gB{HcpZ8&;aakqF1lbEEZVrpW->p5C1TD}5TVo%8lOyuc| z7G7z|?Kb&U%w*2GYl>5=?ucyU?>^Oa`Yq3-4;@OAQ_Y^fm?tCa>f-_0UvoY)#j0C= z_C*G1J;qJzv`8a6x(9IhnHk`KDbFOEW#DRJym6NjoJ?JL+&;v7yh%>QUQr4HIYm z7V8zk9+D!WK}-Q({TA#xHz6{(BIKEWFkfeg*Qt`#OPFSGHuyMnm?bEDz8aTyD$42a zTEhp-UHY6;DmzwIGKen9+10n@j2VllVC=L>Jgg1-HP6iQ_4P`+cpI3JQ@myh&+@LC z>f2@a?;!7IBkxD&b#!Ef{6$u$@a;NQH0!jRk9MGh=+~;S&=Z?JcI?-^m3>9>)biCQ zPhHkO>$O#O{i)CsS^m6E%TzaqKDsE%m*;C~65VH!INfr$)!|6{+*eQA2^Mud0P5-Zdj|ZMP@lM1m z?#b1jOL6TBW@PpTxOh99YDC6uem;+9 z|98sm@^SQaT?HbHL8rW|BA2;}2H^i^mm?oU727!HB%G= 2.7.5) - SwiftProtobuf (1.20.3) - Toast (4.0.0) DEPENDENCIES: + - assets_audio_player (from `.symlinks/plugins/assets_audio_player/ios`) + - assets_audio_player_web (from `.symlinks/plugins/assets_audio_player_web/ios`) + - audioplayers_darwin (from `.symlinks/plugins/audioplayers_darwin/ios`) - barcode_scan2 (from `.symlinks/plugins/barcode_scan2/ios`) - device_info (from `.symlinks/plugins/device_info/ios`) - easy_app_installer (from `.symlinks/plugins/easy_app_installer/ios`) @@ -49,6 +60,7 @@ DEPENDENCIES: - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - platform_device_id (from `.symlinks/plugins/platform_device_id/ios`) + - rive_common (from `.symlinks/plugins/rive_common/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`) @@ -60,6 +72,12 @@ SPEC REPOS: - Toast EXTERNAL SOURCES: + assets_audio_player: + :path: ".symlinks/plugins/assets_audio_player/ios" + assets_audio_player_web: + :path: ".symlinks/plugins/assets_audio_player_web/ios" + audioplayers_darwin: + :path: ".symlinks/plugins/audioplayers_darwin/ios" barcode_scan2: :path: ".symlinks/plugins/barcode_scan2/ios" device_info: @@ -82,27 +100,33 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/permission_handler_apple/ios" platform_device_id: :path: ".symlinks/plugins/platform_device_id/ios" + rive_common: + :path: ".symlinks/plugins/rive_common/ios" shared_preferences_foundation: :path: ".symlinks/plugins/shared_preferences_foundation/ios" sqflite: :path: ".symlinks/plugins/sqflite/ios" SPEC CHECKSUMS: + assets_audio_player: edee322b9cb625571b830b35872ead1a295fd917 + assets_audio_player_web: 19826380c44375761aa0b9053665c1e3fbc3b86b + audioplayers_darwin: 877d9a4d06331c5c374595e46e16453ac7eafa40 barcode_scan2: 0af2bb63c81b4565aab6cd78278e4c0fa136dbb0 device_info: d7d233b645a32c40dfdc212de5cf646ca482f175 easy_app_installer: 29abe397da7d86721fee853281202f414373f45c Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 - fluttertoast: eb263d302cc92e04176c053d2385237e9f43fad0 + fluttertoast: fafc4fa4d01a6a9e4f772ecd190ffa525e9e2d9c FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a location: 3a2eed4dd2fab25e7b7baf2a9efefe82b512d740 modal_progress_hud_nsn: f6fb744cd060653d66ed8f325360ef3650eb2fde MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e - path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9 - permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce + path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8 + permission_handler_apple: 8f116445eff3c0e7c65ad60f5fef5490aa94b4e4 platform_device_id: 81b3e2993881f87d0c82ef151dc274df4869aef5 - shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472 - sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 + rive_common: 60ae7896ab40f9513974f36f015de33f70d2c5c5 + shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c + sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a SwiftProtobuf: b02b5075dcf60c9f5f403000b3b0c202a11b6ae1 Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index db5f9c0..1372ea9 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -323,6 +323,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -341,7 +342,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -362,7 +363,7 @@ DEVELOPMENT_TEAM = 2WLSMMLG6W; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -404,6 +405,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -428,7 +430,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -459,6 +461,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -477,7 +480,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -500,7 +503,7 @@ DEVELOPMENT_TEAM = 2WLSMMLG6W; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -532,7 +535,7 @@ DEVELOPMENT_TEAM = 2WLSMMLG6W; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 06ce33a..67bed1a 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -2,6 +2,8 @@ +NSCameraUsageDescription + Camera permission is required for barcode scanning. CADisableMinimumFrameDurationOnPhone CFBundleDevelopmentRegion diff --git a/lib/bloc/profile/family/family_bloc.dart b/lib/bloc/profile/family/family_bloc.dart index f948ab6..96b13f8 100644 --- a/lib/bloc/profile/family/family_bloc.dart +++ b/lib/bloc/profile/family/family_bloc.dart @@ -120,5 +120,8 @@ class FamilyBloc extends Bloc { emit(FamilyErrorState(message: e.toString())); } }); + on((event,emit){ + emit(FamilyErrorState(message: state.toString())); + }); } } diff --git a/lib/bloc/profile/family/family_event.dart b/lib/bloc/profile/family/family_event.dart index ba6ff31..a9fb8c5 100644 --- a/lib/bloc/profile/family/family_event.dart +++ b/lib/bloc/profile/family/family_event.dart @@ -64,3 +64,8 @@ class AddEmergencyEvent extends FamilyEvent{ @override List get props => [profileId,token,relatedPersonId]; } + +class CallErrorState extends FamilyEvent{ + final String message; + const CallErrorState({required this.message}); +} diff --git a/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart b/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart index 1857b85..6ef27c0 100644 --- a/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart +++ b/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart @@ -1,14 +1,8 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; -import 'package:flutter/material.dart'; import 'package:unit2/model/location/country.dart'; import 'package:unit2/model/location/region.dart'; -import 'package:unit2/screens/profile/components/other_information/org_membership/add_modal.dart'; -import 'package:unit2/screens/unit2/login/components/update_required.dart'; import 'package:unit2/sevices/profile/learningDevelopment_service.dart'; -import 'package:unit2/test_data.dart'; -import 'package:unit2/utils/text_container.dart'; - import '../../../model/location/barangay.dart'; import '../../../model/location/city.dart'; import '../../../model/location/provinces.dart'; @@ -17,7 +11,6 @@ import '../../../model/utils/agency.dart'; import '../../../model/utils/category.dart'; import '../../../utils/location_utilities.dart'; import '../../../utils/profile_utilities.dart'; - part 'learning_development_event.dart'; part 'learning_development_state.dart'; @@ -40,17 +33,16 @@ class LearningDevelopmentBloc Province? currentProvince; CityMunicipality? currentCity; Barangay? currentBarangay; - - on((event, emit) async { - emit(LearningDevelopmentLoadingState()); - try { - List learnings = await LearningDevelopmentServices - .instance - .getLearningDevelopments(event.profileId, event.token); - learningsAndDevelopments = learnings; - emit(LearningDevelopmentLoadedState( - learningsAndDevelopment: learningsAndDevelopments)); + on((event, emit) async { + emit(LearningDevelopmentLoadingState()); + try { + List learnings = await LearningDevelopmentServices + .instance + .getLearningDevelopments(event.profileId, event.token); + learningsAndDevelopments = learnings; + emit(LearningDevelopmentLoadedState( + learningsAndDevelopment: learningsAndDevelopments)); } catch (e) { emit(LearningDevelopmentErrorState(message: e.toString())); } @@ -93,7 +85,6 @@ class LearningDevelopmentBloc await ProfileUtilities.instance.agencyCategory(); agencyCategory = categoryAgencies; } - emit(LearningDevelopmentAddingState( types: types, topics: topics, @@ -103,102 +94,103 @@ class LearningDevelopmentBloc sponsorAgencies: agencies, agencyCategory: agencyCategory)); } catch (e) { - emit(LeaningDevelopmentErrorState(message: e.toString())); + emit(LearningDevelopmentErrorState(message: e.toString())); } }); ////Show edit form on((event, emit) async { - try{ - if (globalRegions.isEmpty) { - List regions = await LocationUtils.instance.getRegions(); - globalRegions = regions; - } - if (globalCountries.isEmpty) { - List countries = await LocationUtils.instance.getCountries(); - globalCountries = countries; - } - currentCountry = globalCountries.firstWhere((Country country) => - event.learningDevelopment.conductedTraining!.venue!.country!.code == - country.code); - if (!event.isOverseas) { - //// if not overseas - currentRegion = globalRegions.firstWhere((Region region) => - event.learningDevelopment.conductedTraining!.venue! - .cityMunicipality!.province!.region!.code == - region.code); - - globalProvinces = await LocationUtils.instance - .getProvinces(regionCode: currentRegion!.code.toString()); - currentProvince = globalProvinces.firstWhere((Province province) => - event.learningDevelopment.conductedTraining!.venue! - .cityMunicipality!.province!.code == - province.code); - - globalCities = await LocationUtils.instance - .getCities(code: currentProvince!.code.toString()); - - currentCity = globalCities.firstWhere( - (CityMunicipality cityMunicipality) => - event.learningDevelopment.conductedTraining!.venue! - .cityMunicipality!.code == - cityMunicipality.code); - globalBarangay = await LocationUtils.instance - .getBarangay(code: currentCity!.code.toString()); - - if (event.learningDevelopment.conductedTraining?.venue?.barangay?.cityMunicipality != - null) { - currentBarangay = globalBarangay.firstWhere((Barangay barangay) => - event.learningDevelopment.conductedTraining!.venue!.barangay! - .code == - barangay.code); - } else { - currentBarangay = null; + try { + if (globalRegions.isEmpty) { + List regions = await LocationUtils.instance.getRegions(); + globalRegions = regions; } - } - if (topics.isEmpty) { - List newTopics = - await LearningDevelopmentServices.instance.getTrainingTopics(); - topics = newTopics; - } - if (types.isEmpty) { - List newTypes = - await LearningDevelopmentServices.instance - .getLearningDevelopmentType(); - types = newTypes; - } - if (agencies.isEmpty) { - List newAgencies = await ProfileUtilities.instance.getAgecies(); - agencies = newAgencies; - } - if (agencyCategory.isEmpty) { - List categoryAgencies = - await ProfileUtilities.instance.agencyCategory(); - agencyCategory = categoryAgencies; - } - emit(LearningDevelopmentUpdatingState( - cities: globalCities, - currentBarangay: currentBarangay, - barangay: globalBarangay, + if (globalCountries.isEmpty) { + List countries = await LocationUtils.instance.getCountries(); + globalCountries = countries; + } + currentCountry = globalCountries.firstWhere((Country country) => + event.learningDevelopment.conductedTraining!.venue!.country!.code == + country.code); + if (!event.isOverseas) { + //// if not overseas + currentRegion = globalRegions.firstWhere((Region region) => + event.learningDevelopment.conductedTraining!.venue! + .cityMunicipality!.province!.region!.code == + region.code); - provinces: globalProvinces, - types: types, - topics: topics, - training: event.learningDevelopment.conductedTraining!.title!, - learningDevelopement: event.learningDevelopment, - currentConductedBy: - event.learningDevelopment.conductedTraining!.conductedBy!, - currentSponsor: event.learningDevelopment.sponsoredBy, - conductedBy: agencies, - sponsorAgencies: agencies, - agencyCategory: agencyCategory, - countries: globalCountries, - regions: globalRegions, - currentRegion: currentRegion, - currentCountry: currentCountry!, - currentProvince: currentProvince, - currentCity: currentCity, - overseas: event.isOverseas)); - }catch(e){ + globalProvinces = await LocationUtils.instance + .getProvinces(regionCode: currentRegion!.code.toString()); + currentProvince = globalProvinces.firstWhere((Province province) => + event.learningDevelopment.conductedTraining!.venue! + .cityMunicipality!.province!.code == + province.code); + + globalCities = await LocationUtils.instance + .getCities(code: currentProvince!.code.toString()); + + currentCity = globalCities.firstWhere( + (CityMunicipality cityMunicipality) => + event.learningDevelopment.conductedTraining!.venue! + .cityMunicipality!.code == + cityMunicipality.code); + globalBarangay = await LocationUtils.instance + .getBarangay(code: currentCity!.code.toString()); + + if (event.learningDevelopment.conductedTraining?.venue?.barangay + ?.cityMunicipality != + null) { + currentBarangay = globalBarangay.firstWhere((Barangay barangay) => + event.learningDevelopment.conductedTraining!.venue!.barangay! + .code == + barangay.code); + } else { + currentBarangay = null; + } + } + if (topics.isEmpty) { + List newTopics = + await LearningDevelopmentServices.instance.getTrainingTopics(); + topics = newTopics; + } + if (types.isEmpty) { + List newTypes = + await LearningDevelopmentServices.instance + .getLearningDevelopmentType(); + types = newTypes; + } + if (agencies.isEmpty) { + List newAgencies = + await ProfileUtilities.instance.getAgecies(); + agencies = newAgencies; + } + if (agencyCategory.isEmpty) { + List categoryAgencies = + await ProfileUtilities.instance.agencyCategory(); + agencyCategory = categoryAgencies; + } + emit(LearningDevelopmentUpdatingState( + cities: globalCities, + currentBarangay: currentBarangay, + barangay: globalBarangay, + provinces: globalProvinces, + types: types, + topics: topics, + training: event.learningDevelopment.conductedTraining!.title!, + learningDevelopement: event.learningDevelopment, + currentConductedBy: + event.learningDevelopment.conductedTraining!.conductedBy!, + currentSponsor: event.learningDevelopment.sponsoredBy, + conductedBy: agencies, + sponsorAgencies: agencies, + agencyCategory: agencyCategory, + countries: globalCountries, + regions: globalRegions, + currentRegion: currentRegion, + currentCountry: currentCountry!, + currentProvince: currentProvince, + currentCity: currentCity, + overseas: event.isOverseas)); + } catch (e) { emit(LearningDevelopmentErrorState(message: e.toString())); } }); @@ -223,7 +215,7 @@ class LearningDevelopmentBloc emit(LearningDevelopmentErrorState(message: e.toString())); } }); - + ////Update on((event, emit) async { try { @@ -233,9 +225,11 @@ class LearningDevelopmentBloc token: event.token, profileId: event.profileId); if (status['success']) { - learningsAndDevelopments.removeWhere((LearningDevelopement element) => - element.conductedTraining!.id == event.learningDevelopement.conductedTraining!.id && - element.conductedTraining!.totalHours == event.learningDevelopement.conductedTraining!.totalHours); + learningsAndDevelopments.removeWhere((LearningDevelopement element) => + element.conductedTraining!.id == + event.learningDevelopement.conductedTraining!.id && + element.conductedTraining!.totalHours == + event.learningDevelopement.conductedTraining!.totalHours); LearningDevelopement learningDevelopement = LearningDevelopement.fromJson(status['data']); learningsAndDevelopments.add(learningDevelopement); @@ -270,7 +264,7 @@ class LearningDevelopmentBloc }); //// call errror on((event, emit) { - emit(LeaningDevelopmentErrorState(message: event.message)); + emit(LearningDevelopmentErrorState(message: event.message)); }); } } diff --git a/lib/bloc/profile/learningDevelopment/learning_development_state.dart b/lib/bloc/profile/learningDevelopment/learning_development_state.dart index bf0276b..e498dc9 100644 --- a/lib/bloc/profile/learningDevelopment/learning_development_state.dart +++ b/lib/bloc/profile/learningDevelopment/learning_development_state.dart @@ -16,13 +16,6 @@ class LearningDevelopmentLoadedState extends LearningDevelopmentState { List get props => [learningsAndDevelopment]; } -class LeaningDevelopmentErrorState extends LearningDevelopmentState { - final String message; - const LeaningDevelopmentErrorState({required this.message}); - @override - List get props => [message]; -} - class LearningDevelopmentLoadingState extends LearningDevelopmentState {} ////added State diff --git a/lib/bloc/profile/primary_information/contact/contact_bloc.dart b/lib/bloc/profile/primary_information/contact/contact_bloc.dart index 5753c70..e2e1ba2 100644 --- a/lib/bloc/profile/primary_information/contact/contact_bloc.dart +++ b/lib/bloc/profile/primary_information/contact/contact_bloc.dart @@ -107,11 +107,11 @@ class ContactBloc extends Bloc { ContactInfo contactInfo = ContactInfo.fromJson(responseStatus['data']['contact_info']); contactInformations.add(contactInfo); - emit(ContactEditedState( + emit(ContactAddedState( response: responseStatus)); } else { - emit(ContactEditedState( + emit(ContactAddedState( response: responseStatus)); } diff --git a/lib/bloc/profile/voluntary_works/voluntary_work_bloc.dart b/lib/bloc/profile/voluntary_works/voluntary_work_bloc.dart index b9e68e3..9d519eb 100644 --- a/lib/bloc/profile/voluntary_works/voluntary_work_bloc.dart +++ b/lib/bloc/profile/voluntary_works/voluntary_work_bloc.dart @@ -1,6 +1,5 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; -import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:unit2/sevices/profile/volunatary_services.dart'; import 'package:unit2/utils/profile_utilities.dart'; diff --git a/lib/bloc/role/pass_check/pass_check_bloc.dart b/lib/bloc/role/pass_check/pass_check_bloc.dart new file mode 100644 index 0000000..c1d157a --- /dev/null +++ b/lib/bloc/role/pass_check/pass_check_bloc.dart @@ -0,0 +1,180 @@ + +import 'package:barcode_scan2/model/scan_result.dart'; +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:unit2/model/roles/pass_check/passer_info.dart'; +import 'package:unit2/sevices/roles/pass_check_services.dart'; +import '../../../utils/scanner.dart'; +part 'pass_check_event.dart'; +part 'pass_check_state.dart'; + +class PassCheckBloc extends Bloc { + PassCheckBloc() : super(PassCheckInitial()) { + int? roleId; + String? uuid; + bool? otherInputs; + String? io; + dynamic assignedArea; + int? checkerId; + String? token; + int? stationId; + String? cpId; + on((event, emit) async { + try { + emit(PassCheckLoadingState()); + List response = await PassCheckServices.instance + .getPassCheckArea(roleId: event.roleId, userId: event.userId); + roleId = event.roleId; + emit(AssignAreaLoaded(assignedArea: response, roleId: roleId!)); + } catch (e) { + emit(PassCheckErrorState(message: e.toString())); + } + }); + on((event, emit) { + otherInputs = event.includeOtherInputs; + checkerId = event.checkerId; + io = event.entranceExit; + token = event.token; + roleId = event.roleId; + assignedArea = event.assignedArea; + emit(SettingSaved( + assignedArea: assignedArea, + checker: checkerId!, + io: io!, + otherInputs: otherInputs!, + roleId: roleId!, + token: token!)); + }); + on((event, emit) { + emit(SettingSaved( + assignedArea: assignedArea, + checker: checkerId!, + io: io!, + otherInputs: otherInputs!, + roleId: roleId!, + token: token!)); + }); + on((event, emit) { + add(PerformPostLogs( + checkerId: checkerId!, + cpId: cpId, + destination: null, + io: io!.toLowerCase() == "incoming" ? "i" : "o", + otherInputs: otherInputs!, + passerId: uuid!, + roleId: roleId!, + stationId: stationId, + temp: event.temp)); + }); + + on((event, emit) { + add(PerformPostLogs( + checkerId: checkerId!, + cpId: cpId, + destination: event.destination, + io: io!.toLowerCase() == "incoming" ? "i" : "o", + otherInputs: otherInputs!, + passerId: uuid!, + roleId: roleId!, + stationId: stationId, + temp: null)); + }); + on((event, emit) async { + ScanResult result = await QRCodeBarCodeScanner.instance.scanner(); + if (result.rawContent.toString().isNotEmpty) { + uuid = result.rawContent.toString(); + emit(PassCheckLoadingState()); + try { + PasserInfo? passerInfo = await PassCheckServices.instance + .getPasserInfo(uuid: uuid!, token: event.token); + if (roleId == 41 || roleId == 13 || roleId == 17 || roleId == 22) { + stationId = assignedArea.id; + } else if (roleId == 7) { + cpId = assignedArea.brgycode; + } else if (roleId == 10) { + cpId = assignedArea.purokdesc; + } else if (roleId == 16) { + cpId = assignedArea.id; + } + if (passerInfo == null) { + Fluttertoast.showToast( + msg: "QR CODE IS INVALID", + fontSize: 24, + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.CENTER, + backgroundColor: Colors.black, + textColor: Colors.white); + emit(QRInvalid(token: token!)); + } else { + if (otherInputs!) { + if (io?.toLowerCase() == 'incoming') { + emit(IncomingScanState()); + } else { + emit(OutGoingScanState()); + } + } else { + add(PerformPostLogs( + checkerId: checkerId!, + cpId: cpId, + destination: null, + io: io!.toLowerCase() == "incoming" ? "i" : "o", + otherInputs: otherInputs!, + passerId: uuid!, + roleId: roleId!, + stationId: stationId, + temp: null)); + } + } + } catch (e) { + emit(PassCheckErrorState(message: e.toString())); + } + } else { + emit(SettingSaved( + assignedArea: assignedArea, + checker: checkerId!, + io: io!, + otherInputs: otherInputs!, + roleId: roleId!, + token: token!)); + } + }); + on((event, emit) async { + emit(PassCheckLoadingState()); + try { + final bool success = await PassCheckServices.instance.performPostLogs( + passerId: event.passerId, + chekerId: event.checkerId, + temp: event.temp, + destination: event.destination, + io: event.io, + stationId: event.stationId, + cpId: event.cpId, + otherInputs: event.otherInputs, + roleid: event.roleId); + if (success) { + Fluttertoast.showToast( + msg: "SUCCESSFULLY SAVED", + fontSize: 24, + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.CENTER, + backgroundColor: Colors.black, + textColor: Colors.white); + emit(ScanSuccess(token: token!)); + } else { + Fluttertoast.showToast( + msg: "SCAN FAILED", + fontSize: 24, + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.CENTER, + backgroundColor: Colors.black, + textColor: Colors.white); + emit(ScanFailed(token: token!)); + } + } catch (e) { + emit(PassCheckErrorState(message: e.toString())); + } + }); + } +} diff --git a/lib/bloc/role/pass_check/pass_check_event.dart b/lib/bloc/role/pass_check/pass_check_event.dart new file mode 100644 index 0000000..0cfe95b --- /dev/null +++ b/lib/bloc/role/pass_check/pass_check_event.dart @@ -0,0 +1,70 @@ +part of 'pass_check_bloc.dart'; + +abstract class PassCheckEvent extends Equatable { + const PassCheckEvent(); + + @override + List get props => []; +} + +class GetPassCheckAreas extends PassCheckEvent { + final int roleId; + final int userId; + const GetPassCheckAreas({required this.roleId, required this.userId}); +} + +class SetScannerSettings extends PassCheckEvent { + final int roleId; + final String token; + final dynamic assignedArea; + final bool includeOtherInputs; + final String entranceExit; + final int checkerId; + const SetScannerSettings( + {required this.assignedArea, + required this.checkerId, + required this.entranceExit, + required this.includeOtherInputs, + required this.roleId, + required this.token}); +} + +class ScanQr extends PassCheckEvent { + final String token; + + const ScanQr({required this.token}); +} + +class PerformPostLogs extends PassCheckEvent { + final String passerId; + final int checkerId; + final String io; + final bool otherInputs; + final String? cpId; + final int? stationId; + final String? destination; + final double? temp; + final int roleId; + const PerformPostLogs( + {required this.checkerId, + required this.cpId, + required this.destination, + required this.io, + required this.otherInputs, + required this.passerId, + required this.roleId, + required this.stationId, + required this.temp}); +} + +class PerformIncomingPostLog extends PassCheckEvent{ + final double temp; + const PerformIncomingPostLog({required this.temp}); +} +class PerformOutgoingPostLog extends PassCheckEvent{ + final String destination; + const PerformOutgoingPostLog({required this.destination}); +} +class ScanError extends PassCheckEvent{ + +} \ No newline at end of file diff --git a/lib/bloc/role/pass_check/pass_check_state.dart b/lib/bloc/role/pass_check/pass_check_state.dart new file mode 100644 index 0000000..fe93067 --- /dev/null +++ b/lib/bloc/role/pass_check/pass_check_state.dart @@ -0,0 +1,67 @@ +part of 'pass_check_bloc.dart'; + +abstract class PassCheckState extends Equatable { + const PassCheckState(); + + @override + List get props => []; +} + +class PassCheckInitial extends PassCheckState {} + +class PassCheckErrorState extends PassCheckState { + final String message; + const PassCheckErrorState({required this.message}); +} + +class QRInvalid extends PassCheckState{ + final String token; + const QRInvalid({required this.token}); +} + +class PassCheckLoadingState extends PassCheckState {} + +class AssignAreaLoaded extends PassCheckState { + final List assignedArea; + final int roleId; + const AssignAreaLoaded({required this.assignedArea, required this.roleId}); +} + +class SettingSaved extends PassCheckState { + final dynamic assignedArea; + final String token; + final String io; + final int checker; + final bool otherInputs; + final int roleId; + const SettingSaved( + {required this.assignedArea, + required this.checker, + required this.io, + required this.otherInputs, + required this.roleId, + required this.token}); +} + +class IncomingScanState extends PassCheckState { + +} + +class OutGoingScanState extends PassCheckState { + +} + +class ScanSuccess extends PassCheckState{ + final String token; +const ScanSuccess({required this.token}); +} + +class ScanFailed extends PassCheckState{ + final String token; +const ScanFailed({required this.token}); +} + +class QRCodeInvalid extends PassCheckState{ + final String token; + const QRCodeInvalid({required this.token}); +} diff --git a/lib/bloc/user/user_bloc.dart b/lib/bloc/user/user_bloc.dart index 801930b..9fb227e 100644 --- a/lib/bloc/user/user_bloc.dart +++ b/lib/bloc/user/user_bloc.dart @@ -85,6 +85,8 @@ class UserBloc extends Bloc { emit(InternetTimeout(message: timeoutError)); } on SocketException catch (_) { emit(InternetTimeout(message: timeoutError)); + }on Error catch(e){ +emit(LoginErrorState(message: e.toString())); } }); on((event, emit) async { @@ -110,8 +112,8 @@ class UserBloc extends Bloc { emit(InternetTimeout(message: timeoutError)); } on SocketException catch (_) { emit(InternetTimeout(message: timeoutError)); - } on Error catch (_) { - emit(InvalidCredentials(message: "Invalid username or password")); + } on Error catch(e){ +emit(LoginErrorState(message: e.toString())); } }); on((event, emit) { diff --git a/lib/bloc/user/user_state.dart b/lib/bloc/user/user_state.dart index ab21f7f..3bb0873 100644 --- a/lib/bloc/user/user_state.dart +++ b/lib/bloc/user/user_state.dart @@ -66,3 +66,7 @@ class InvalidCredentials extends UserState{ final String message ; InvalidCredentials ({required this.message}); } +class LoginErrorState extends UserState{ + final String message; + LoginErrorState({required this.message}); +} diff --git a/lib/model/roles/pass_check/agency_area_type.dart b/lib/model/roles/pass_check/agency_area_type.dart new file mode 100644 index 0000000..260a7c9 --- /dev/null +++ b/lib/model/roles/pass_check/agency_area_type.dart @@ -0,0 +1,96 @@ + +class AgencyAssignedArea { + final bool? isactive; + final Area? area; + + AgencyAssignedArea({ + required this.isactive, + required this.area, + }); + + factory AgencyAssignedArea.fromJson(Map json) => AgencyAssignedArea( + isactive: json["isactive"], + area: json["area"] == null? null:Area.fromJson(json["area"]), + ); + + Map toJson() => { + "isactive": isactive, + "area": area?.toJson(), + }; +} + +class Area { + final int? id; + final String? name; + final Category? category; + final bool? privateEntity; + final String? contactInfo; + + Area({ + required this.id, + required this.name, + required this.category, + required this.privateEntity, + required this.contactInfo, + }); + + factory Area.fromJson(Map json) => Area( + id: json["id"], + name: json["name"], + category: json["category"]==null?null:Category.fromJson(json["category"]), + privateEntity: json["private_entity"], + contactInfo: json["contact_info"], + ); + + Map toJson() => { + "id": id, + "name": name, + "category": category?.toJson(), + "private_entity": privateEntity, + "contact_info": contactInfo, + }; +} + +class Category { + final int? id; + final IndustryClass? industryClass; + + Category({ + required this.id, + required this.industryClass, + }); + + factory Category.fromJson(Map json) => Category( + id: json["id"], + industryClass: json["industry_class"] == null?null:IndustryClass.fromJson(json["industry_class"]), + ); + + Map toJson() => { + "id": id, + "industry_class": industryClass?.toJson(), + }; +} + +class IndustryClass { + final int? id; + final String? name; + final String? description; + + IndustryClass({ + required this.id, + required this.name, + required this.description, + }); + + factory IndustryClass.fromJson(Map json) => IndustryClass( + id: json["id"], + name: json["name"], + description: json["description"], + ); + + Map toJson() => { + "id": id, + "name": name, + "description": description, + }; +} diff --git a/lib/model/roles/pass_check/assign_role_area_type.dart b/lib/model/roles/pass_check/assign_role_area_type.dart new file mode 100644 index 0000000..3b2df3e --- /dev/null +++ b/lib/model/roles/pass_check/assign_role_area_type.dart @@ -0,0 +1,52 @@ +// To parse this JSON data, do +// +// final assignRoleAreaType = assignRoleAreaTypeFromJson(jsonString); + + +class AssignRoleAreaType { + final String areaTypeName; + final Details details; + + AssignRoleAreaType({ + required this.areaTypeName, + required this.details, + }); + + factory AssignRoleAreaType.fromJson(Map json) => AssignRoleAreaType( + areaTypeName: json["area_type_name"], + details: Details.fromJson(json["details"]), + ); + + Map toJson() => { + "area_type_name": areaTypeName, + "details": details.toJson(), + }; +} + +class Details { + final String? table; + final String? schema; + final String? dataType; + final String? idColumn; + + Details({ + required this.table, + required this.schema, + required this.dataType, + required this.idColumn, + }); + + factory Details.fromJson(Map json) => Details( + table: json["table"], + schema: json["schema"], + dataType: json["data_type"], + idColumn: json["id_column"], + ); + + Map toJson() => { + "table": table, + "schema": schema, + "data_type": dataType, + "id_column": idColumn, + }; +} diff --git a/lib/model/roles/pass_check/barangay_assign_area.dart b/lib/model/roles/pass_check/barangay_assign_area.dart new file mode 100644 index 0000000..f86ddd5 --- /dev/null +++ b/lib/model/roles/pass_check/barangay_assign_area.dart @@ -0,0 +1,24 @@ + +class BaragayAssignArea { + final String? brgycode; + final String? brgydesc; + final String? citymuncode; + + BaragayAssignArea({ + required this.brgycode, + required this.brgydesc, + required this.citymuncode, + }); + + factory BaragayAssignArea.fromJson(Map json) => BaragayAssignArea( + brgycode: json["brgycode"], + brgydesc: json["brgydesc"], + citymuncode: json["citymuncode"], + ); + + Map toJson() => { + "brgycode": brgycode, + "brgydesc": brgydesc, + "citymuncode": citymuncode, + }; +} diff --git a/lib/model/roles/pass_check/passer_info.dart b/lib/model/roles/pass_check/passer_info.dart new file mode 100644 index 0000000..b841783 --- /dev/null +++ b/lib/model/roles/pass_check/passer_info.dart @@ -0,0 +1,120 @@ + +class PasserInfo { + final int personid; + final String familyname; + final String givenname; + final String? middlename; + final String sex; + final DateTime? birthdate; + final String? extension; + final String? civilstatus; + final double? heightM; + final double weightKg; + final String? bloodtype; + final String? photoPath; + final String? uuid; + final String? encryptionKey; + final bool? deceased; + final dynamic encryptedProfile; + final String? addedby; + final DateTime? addedat; + final String? updateby; + final DateTime? updatedat; + final String? deletedby; + final DateTime? deletedat; + final String? encryptSignature; + final String? esigPath; + final String? titlePrefix; + final String? titleSuffix; + final bool? showTitleId; + + PasserInfo({ + required this.personid, + required this.familyname, + required this.givenname, + required this.middlename, + required this.sex, + required this.birthdate, + required this.extension, + required this.civilstatus, + required this.heightM, + required this.weightKg, + required this.bloodtype, + required this.photoPath, + required this.uuid, + required this.encryptionKey, + required this.deceased, + required this.encryptedProfile, + required this.addedby, + required this.addedat, + required this.updateby, + required this.updatedat, + required this.deletedby, + required this.deletedat, + required this.encryptSignature, + required this.esigPath, + required this.titlePrefix, + required this.titleSuffix, + required this.showTitleId, + }); + + factory PasserInfo.fromJson(Map json) => PasserInfo( + personid: json["personid"], + familyname: json["familyname"], + givenname: json["givenname"], + middlename: json["middlename"], + sex: json["sex"], + birthdate: json['birthdate'] == null?null: DateTime.parse(json["birthdate"]), + extension: json["extension"], + civilstatus: json["civilstatus"], + heightM: json["height_m"]?.toDouble(), + weightKg: json["weight_kg"]?.toDouble(), + bloodtype: json["bloodtype"], + photoPath: json["photo_path"], + uuid: json["uuid"], + encryptionKey: json["encryption_key"], + deceased: json["deceased"], + encryptedProfile: json["encrypted_profile"], + addedby: json["addedby"], + addedat: json["addedat"] == null? null:DateTime.tryParse(json["addedat"]), + updateby:json["updateby"], + updatedat:json["updatedat"]==null?null: DateTime.tryParse(json["updatedat"]), + deletedby: json["deletedby"], + deletedat: json['deletedat'] == null?null:DateTime.tryParse(json["deletedat"]), + encryptSignature: json["encrypt_signature"], + esigPath: json["esig_path"], + titlePrefix: json["title_prefix"], + titleSuffix: json["title_suffix"], + showTitleId: json["show_title_id"], + ); + + Map toJson() => { + "personid": personid, + "familyname": familyname, + "givenname": givenname, + "middlename": middlename, + "sex": sex, + "birthdate": "${birthdate?.year.toString().padLeft(4, '0')}-${birthdate?.month.toString().padLeft(2, '0')}-${birthdate?.day.toString().padLeft(2, '0')}", + "extension": extension, + "civilstatus": civilstatus, + "height_m": heightM, + "weight_kg": weightKg, + "bloodtype": bloodtype, + "photo_path": photoPath, + "uuid": uuid, + "encryption_key": encryptionKey, + "deceased": deceased, + "encrypted_profile": encryptedProfile, + "addedby": addedby, + "addedat": addedat?.toIso8601String(), + "updateby": updateby, + "updatedat": updatedat?.toIso8601String(), + "deletedby": deletedby, + "deletedat": deletedat, + "encrypt_signature": encryptSignature, + "esig_path": esigPath, + "title_prefix": titlePrefix, + "title_suffix": titleSuffix, + "show_title_id": showTitleId, + }; +} diff --git a/lib/model/roles/pass_check/purok_assign_area.dart b/lib/model/roles/pass_check/purok_assign_area.dart new file mode 100644 index 0000000..883086f --- /dev/null +++ b/lib/model/roles/pass_check/purok_assign_area.dart @@ -0,0 +1,42 @@ + + +import 'barangay_assign_area.dart'; + +class Purok { + final String? purokid; + final String? purokdesc; + final BaragayAssignArea? brgy; + final dynamic purokLeader; + final bool? writelock; + final dynamic recordsignature; + + Purok({ + required this.purokid, + required this.purokdesc, + required this.brgy, + required this.purokLeader, + required this.writelock, + required this.recordsignature, + }); + + factory Purok.fromJson(Map json) => Purok( + purokid: json["purokid"], + purokdesc: json["purokdesc"], + brgy: json["brgy"]==null?null:BaragayAssignArea.fromJson(json["brgy"]), + purokLeader: json["purok_leader"], + writelock: json["writelock"], + recordsignature: json["recordsignature"], + ); + + Map toJson() => { + "purokid": purokid, + "purokdesc": purokdesc, + "brgy": brgy?.toJson(), + "purok_leader": purokLeader, + "writelock": writelock, + "recordsignature": recordsignature, + }; +} + + + diff --git a/lib/model/roles/pass_check/station_assign_area.dart b/lib/model/roles/pass_check/station_assign_area.dart new file mode 100644 index 0000000..4251403 --- /dev/null +++ b/lib/model/roles/pass_check/station_assign_area.dart @@ -0,0 +1,182 @@ +// To parse this JSON data, do +// +// final assignArea = assignAreaFromJson(jsonString); + +class StationAssignArea { + final bool? isactive; + final Area? area; + + StationAssignArea({ + required this.isactive, + required this.area, + }); + + factory StationAssignArea.fromJson(Map json) => StationAssignArea( + isactive: json["isactive"], + area: json["area"] == null?null: Area.fromJson(json["area"]), + ); + + Map toJson() => { + "isactive": isactive, + "area": area?.toJson(), + }; +} + +class Area { + final int? id; + final String? stationName; + final StationType? stationType; + final int? hierarchyOrderNo; + final String? headPosition; + final GovernmentAgency? governmentAgency; + final String? acronym; + final int? parentStation; + final String? code; + final String? fullcode; + final List? childStationInfo; + final bool? islocationUnderParent; + final int? mainParentStation; + final String? description; + final bool? ishospital; + final bool? isactive; + final bool? sellingStation; + + Area({ + required this.id, + required this.stationName, + required this.stationType, + required this.hierarchyOrderNo, + required this.headPosition, + required this.governmentAgency, + required this.acronym, + required this.parentStation, + required this.code, + required this.fullcode, + required this.childStationInfo, + required this.islocationUnderParent, + required this.mainParentStation, + required this.description, + required this.ishospital, + required this.isactive, + required this.sellingStation, + }); + + factory Area.fromJson(Map json) => Area( + id: json["id"], + stationName: json["station_name"], + stationType:json["station_type"] ==null?null: StationType.fromJson(json["station_type"]), + hierarchyOrderNo: json["hierarchy_order_no"], + headPosition: json["head_position"], + governmentAgency: json["government_agency"] == null?null:GovernmentAgency.fromJson(json["government_agency"]), + acronym: json["acronym"], + parentStation: json["parent_station"], + code: json["code"], + fullcode: json["fullcode"], + childStationInfo: json['child_station_info']==null?[]:List.from(json["child_station_info"].map((x) => ChildStationInfo.fromJson(x))), + islocationUnderParent: json["islocation_under_parent"], + mainParentStation: json["main_parent_station"], + description: json["description"], + ishospital: json["ishospital"], + isactive: json["isactive"], + sellingStation: json["selling_station"], + ); + + Map toJson() => { + "id": id, + "station_name": stationName, + "station_type": stationType?.toJson(), + "hierarchy_order_no": hierarchyOrderNo, + "head_position": headPosition, + "government_agency": governmentAgency?.toJson(), + "acronym": acronym, + "parent_station": parentStation, + "code": code, + "fullcode": fullcode, + "child_station_info": List.from(childStationInfo!.map((x) => x.toJson())), + "islocation_under_parent": islocationUnderParent, + "main_parent_station": mainParentStation, + "description": description, + "ishospital": ishospital, + "isactive": isactive, + "selling_station": sellingStation, + }; +} + +class ChildStationInfo { + final int? id; + final String? stationName; + final String? acroym; + bool? motherStation; + + ChildStationInfo({ + required this.id, + required this.stationName, + required this.acroym, + this.motherStation + }); + + factory ChildStationInfo.fromJson(Map json) => ChildStationInfo( + id: json["id"], + stationName: json["station_name"], + acroym: json["acroym"], + + ); + + Map toJson() => { + "id": id, + "station_name": stationName, + "acroym": acroym, + }; +} + +class GovernmentAgency { + final int? agencyid; + final String? agencyname; + final int? agencycatid; + final bool? privateEntity; + final int? contactinfoid; + + GovernmentAgency({ + required this.agencyid, + required this.agencyname, + required this.agencycatid, + required this.privateEntity, + required this.contactinfoid, + }); + + factory GovernmentAgency.fromJson(Map json) => GovernmentAgency( + agencyid: json["agencyid"], + agencyname: json["agencyname"], + agencycatid: json["agencycatid"], + privateEntity: json["private_entity"], + contactinfoid: json["contactinfoid"], + ); + + Map toJson() => { + "agencyid": agencyid, + "agencyname": agencyname, + "agencycatid": agencycatid, + "private_entity": privateEntity, + "contactinfoid": contactinfoid, + }; +} + +class StationType { + final int? id; + final String? typeName; + + StationType({ + required this.id, + required this.typeName, + }); + + factory StationType.fromJson(Map json) => StationType( + id: json["id"], + typeName: json["type_name"], + ); + + Map toJson() => { + "id": id, + "type_name": typeName, + }; +} diff --git a/lib/screens/profile/components/basic_information/address/edit_modal.dart b/lib/screens/profile/components/basic_information/address/edit_modal.dart index 29b3325..83901b5 100644 --- a/lib/screens/profile/components/basic_information/address/edit_modal.dart +++ b/lib/screens/profile/components/basic_information/address/edit_modal.dart @@ -8,6 +8,7 @@ import 'package:unit2/bloc/profile/primary_information/address/address_bloc.dart import 'package:unit2/model/location/address_category.dart'; import 'package:unit2/model/profile/basic_information/adress.dart'; import 'package:unit2/utils/global.dart'; +import 'package:unit2/utils/global_context.dart'; import '../../../../../model/location/barangay.dart'; import '../../../../../model/location/city.dart'; import '../../../../../model/location/country.dart'; @@ -281,12 +282,16 @@ class _EditAddressScreenState extends State { }); selectedRegion = region; //// GET PROVINCES + try{ provinces = await LocationUtils .instance .getProvinces( regionCode: selectedRegion!.code .toString()); + }catch(e){ + NavigationService.navigatorKey.currentContext?.read().add(CallErrorState()); + } selectedProvince = provinces![0]; setState(() { @@ -294,11 +299,15 @@ class _EditAddressScreenState extends State { cityCall = true; }); //// GET CITIES + try{ citymuns = await LocationUtils .instance .getCities( code: selectedProvince! .code!); + }catch(e){ + NavigationService.navigatorKey.currentContext?.read().add(CallErrorState()); + } selectedMunicipality = citymuns![0]; setState(() { @@ -306,23 +315,31 @@ class _EditAddressScreenState extends State { barangayCall = true; }); //// GET BARANGAY + try{ barangays = await LocationUtils .instance .getBarangay( code: selectedMunicipality! .code!); + }catch(e){ + NavigationService.navigatorKey.currentContext?.read().add(CallErrorState()); + } selectedBarangay = barangays![0]; setState(() { barangayCall = false; }); ////GET CITY MUNICIPALITY + try{ citymuns = await LocationUtils .instance .getCities( code: selectedProvince! .code!); + }catch(e){ + NavigationService.navigatorKey.currentContext?.read().add(CallErrorState()); + } selectedMunicipality = citymuns![0]; setState(() { @@ -330,12 +347,16 @@ class _EditAddressScreenState extends State { barangayCall = true; }); //// GET BARANGAYS + try{ barangays = await LocationUtils .instance .getBarangay( code: selectedMunicipality! .code!); + }catch(e){ + NavigationService.navigatorKey.currentContext?.read().add(CallErrorState()); + } selectedBarangay = barangays![0]; setState(() { @@ -385,12 +406,16 @@ class _EditAddressScreenState extends State { }); //// GET CITIES + try{ citymuns = await LocationUtils .instance .getCities( code: selectedProvince! .code!); + }catch(e){ + NavigationService.navigatorKey.currentContext?.read().add(CallErrorState()); + } selectedMunicipality = citymuns![0]; setState(() { @@ -398,12 +423,16 @@ class _EditAddressScreenState extends State { barangayCall = true; }); //// GET BARANGAY + try{ barangays = await LocationUtils .instance .getBarangay( code: selectedMunicipality! .code!); + }catch(e){ + NavigationService.navigatorKey.currentContext?.read().add(CallErrorState()); + } selectedBarangay = barangays![0]; setState(() { @@ -453,12 +482,16 @@ class _EditAddressScreenState extends State { selectedMunicipality = city; selectedMunicipality = city; //// GET BARANGAYS + try{ barangays = await LocationUtils .instance .getBarangay( code: selectedMunicipality! .code!); + }catch(e){ + NavigationService.navigatorKey.currentContext?.read().add(CallErrorState()); + } selectedBarangay = barangays![0]; setState(() { diff --git a/lib/screens/profile/components/basic_information/contact_information_screen.dart b/lib/screens/profile/components/basic_information/contact_information_screen.dart index 4f4e8bd..3e0f4d2 100644 --- a/lib/screens/profile/components/basic_information/contact_information_screen.dart +++ b/lib/screens/profile/components/basic_information/contact_information_screen.dart @@ -90,7 +90,7 @@ class ContactInformationScreen extends StatelessWidget { .add(LoadContacts()); }); } else { - errorAlert(context, "Update Failed", + errorAlert(context, "Adding Failed", "Something went wrong. Please try again.", () { Navigator.of(context).pop(); @@ -331,7 +331,7 @@ class ContactInformationScreen extends StatelessWidget { ); }); } else { - const EmptyData( + return const EmptyData( message: "You don't have contact information added. Please click + to add"); } diff --git a/lib/screens/profile/components/basic_information/edit_basic_info_modal.dart b/lib/screens/profile/components/basic_information/edit_basic_info_modal.dart index 6d33c67..253ea7d 100644 --- a/lib/screens/profile/components/basic_information/edit_basic_info_modal.dart +++ b/lib/screens/profile/components/basic_information/edit_basic_info_modal.dart @@ -157,7 +157,7 @@ class _EditBasicProfileInfoScreenState UpperCaseTextFormatter() ], name: "middlename", - initialValue: state.primaryInformation.middleName!, + initialValue: state.primaryInformation.middleName??'', decoration: normalTextFieldStyle("Middle name", ""), ), ), diff --git a/lib/screens/profile/components/basic_information/family/spouse_edit_modal.dart b/lib/screens/profile/components/basic_information/family/spouse_edit_modal.dart index 30012e4..df5e3d3 100644 --- a/lib/screens/profile/components/basic_information/family/spouse_edit_modal.dart +++ b/lib/screens/profile/components/basic_information/family/spouse_edit_modal.dart @@ -1,10 +1,8 @@ import 'package:date_time_picker/date_time_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; -import 'package:fluttericon/font_awesome_icons.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:searchfield/searchfield.dart'; - import '../../../../../bloc/profile/family/family_bloc.dart'; import '../../../../../model/profile/family_backround.dart'; import '../../../../../model/utils/agency.dart'; @@ -17,7 +15,6 @@ import '../../../../../theme-data.dart/form-style.dart'; import '../../../../../utils/formatters.dart'; import '../../../../../utils/global.dart'; import '../../../../../utils/text_container.dart'; -import '../../../../../utils/validators.dart'; import '../../../shared/add_for_empty_search.dart'; class SpouseEditAlert extends StatefulWidget { diff --git a/lib/screens/profile/components/basic_information/identification/edit_modal.dart b/lib/screens/profile/components/basic_information/identification/edit_modal.dart index eb6a8c9..626458a 100644 --- a/lib/screens/profile/components/basic_information/identification/edit_modal.dart +++ b/lib/screens/profile/components/basic_information/identification/edit_modal.dart @@ -146,17 +146,17 @@ class _EditIdentificationScreenState extends State { }); }, selectableDayPredicate: (date) { - if (expDate != null && - DateTime.parse(expDate!) + if ((expDate != "null" && expDate != null) && + DateTime.tryParse(expDate!)! .microsecondsSinceEpoch <= date.microsecondsSinceEpoch) { return false; } return true; }, - initialDate: expDate != null + initialDate: expDate == "null" || expDate == null ? DateTime.now() - : DateTime.parse(expDate!).subtract( + : DateTime.tryParse(expDate!)?.subtract( const Duration(days: 1)), )), @@ -187,17 +187,17 @@ class _EditIdentificationScreenState extends State { color: Colors.black87, )), selectableDayPredicate: (date) { - if (issuedDate != null && - DateTime.parse(issuedDate!) + if ((issuedDate != "null" && issuedDate != null) && + DateTime.tryParse(issuedDate!)! .microsecondsSinceEpoch >= date.microsecondsSinceEpoch) { return false; } return true; }, - initialDate: issuedDate != null + initialDate: issuedDate == null && issuedDate == "null" ? DateTime.now() - : DateTime.parse(issuedDate!).add( + : DateTime.tryParse(issuedDate!)?.add( const Duration(days: 1)), )), ], @@ -259,6 +259,14 @@ class _EditIdentificationScreenState extends State { selectedRegion! .code .toString()); + } catch (e) { + context + .read< + IdentificationBloc>() + .add(ShowErrorState( + message: + e.toString())); + } selectedProvince = provinces![0]; setState(() { @@ -287,14 +295,7 @@ class _EditIdentificationScreenState extends State { message: e .toString())); } - } catch (e) { - context - .read< - IdentificationBloc>() - .add(ShowErrorState( - message: - e.toString())); - } + } }, value: selectedRegion, diff --git a/lib/screens/profile/components/basic_information/identification_information_screen.dart b/lib/screens/profile/components/basic_information/identification_information_screen.dart index cd9836e..315189a 100644 --- a/lib/screens/profile/components/basic_information/identification_information_screen.dart +++ b/lib/screens/profile/components/basic_information/identification_information_screen.dart @@ -219,7 +219,7 @@ class IdentificationsScreen extends StatelessWidget { const SizedBox(height: 8,), Badge( backgroundColor: - success2, + government != true?success2:second, label: Text( government == true ? privateText diff --git a/lib/screens/profile/components/basic_information/primary_information_screen.dart b/lib/screens/profile/components/basic_information/primary_information_screen.dart index 875ca64..f574e08 100644 --- a/lib/screens/profile/components/basic_information/primary_information_screen.dart +++ b/lib/screens/profile/components/basic_information/primary_information_screen.dart @@ -1,15 +1,12 @@ -import 'package:date_time_picker/date_time_picker.dart'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; -import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:intl/intl.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; -import 'package:unit2/model/profile/basic_information/primary-information.dart'; import 'package:unit2/screens/profile/components/basic_information/edit_basic_info_modal.dart'; -import 'package:unit2/theme-data.dart/btn-style.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/theme-data.dart/form-style.dart'; import 'package:unit2/utils/global.dart'; @@ -132,8 +129,8 @@ class _PrimaryInfoState extends State { child: FormBuilderTextField( enabled: enabled, name: middlename, - initialValue: - state.primaryBasicInformation.middleName!, + initialValue: state.primaryBasicInformation.middleName??'' + , decoration: normalTextFieldStyle("Middle name", ""), ), diff --git a/lib/screens/profile/components/eligibility/edit_modal.dart b/lib/screens/profile/components/eligibility/edit_modal.dart index ca4dadc..d161084 100644 --- a/lib/screens/profile/components/eligibility/edit_modal.dart +++ b/lib/screens/profile/components/eligibility/edit_modal.dart @@ -9,6 +9,7 @@ import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; import 'package:unit2/model/location/city.dart'; import 'package:unit2/model/profile/eligibility.dart'; import 'package:unit2/model/utils/eligibility.dart'; +import 'package:unit2/utils/global_context.dart'; import 'package:unit2/utils/location_utilities.dart'; import '../../../../bloc/profile/eligibility/eligibility_bloc.dart'; import '../../../../model/location/country.dart'; @@ -315,22 +316,30 @@ class _EditEligibilityScreenState extends State { provinceCall = true; }); selectedRegion = region; + try{ provinces = await LocationUtils .instance .getProvinces( regionCode: selectedRegion!.code .toString()); + }catch(e){ + context.read().add(CallErrorState()); + } selectedProvince = provinces![0]; setState(() { provinceCall = false; cityCall = true; }); + try{ citymuns = await LocationUtils .instance .getCities( code: selectedProvince! .code!); + }catch(e){ + NavigationService.navigatorKey.currentContext?.read().add(CallErrorState()); + } selectedMunicipality = citymuns![0]; setState(() { @@ -376,6 +385,9 @@ class _EditEligibilityScreenState extends State { cityCall = true; }); selectedProvince = province; + try{ + + citymuns = await LocationUtils .instance .getCities( @@ -383,6 +395,9 @@ class _EditEligibilityScreenState extends State { selectedProvince! .code .toString()); + }catch(e){ + context.read().add(CallErrorState()); + } selectedMunicipality = citymuns![0]; setState(() { diff --git a/lib/screens/profile/components/family_background_screen.dart b/lib/screens/profile/components/family_background_screen.dart index c0c6368..e680896 100644 --- a/lib/screens/profile/components/family_background_screen.dart +++ b/lib/screens/profile/components/family_background_screen.dart @@ -328,7 +328,7 @@ class _FamilyBackgroundScreenState extends State { vertical: -4), title: Text( - "${father?.relatedPerson?.firstName} ${father?.relatedPerson?.middleName??''} ${father?.relatedPerson!.lastName} ${father?.relatedPerson?.nameExtension ?? ''}", + "${father?.relatedPerson?.firstName} ${father?.relatedPerson?.middleName ?? ''} ${father?.relatedPerson!.lastName} ${father?.relatedPerson?.nameExtension ?? ''}", style: Theme.of( context) .textTheme @@ -612,7 +612,7 @@ class _FamilyBackgroundScreenState extends State { vertical: -4), title: Text( - "${mother?.relatedPerson?.firstName} ${mother?.relatedPerson?.middleName??''} ${mother?.relatedPerson?.lastName} ${mother?.relatedPerson?.nameExtension ?? ''}", + "${mother?.relatedPerson?.firstName} ${mother?.relatedPerson?.middleName ?? ''} ${mother?.relatedPerson?.lastName} ${mother?.relatedPerson?.nameExtension ?? ''}", style: Theme.of( context) .textTheme @@ -860,25 +860,38 @@ class _FamilyBackgroundScreenState extends State { context); progress!.showWithText( "Loading..."); - if (positions.isEmpty) { - positions = - await ProfileUtilities - .instance - .getAgencyPosition(); - } + try { + if (positions + .isEmpty) { + positions = + await ProfileUtilities + .instance + .getAgencyPosition(); + } - if (agencices.isEmpty) { - agencices = - await ProfileUtilities - .instance - .getAgecies(); - } - if (categories - .isEmpty) { - categories = - await ProfileUtilities - .instance - .agencyCategory(); + if (agencices + .isEmpty) { + agencices = + await ProfileUtilities + .instance + .getAgecies(); + } + if (categories + .isEmpty) { + categories = + await ProfileUtilities + .instance + .agencyCategory(); + } + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read< + FamilyBloc>() + .add(CallErrorState( + message: e + .toString())); } progress.dismiss(); showDialog( @@ -938,7 +951,7 @@ class _FamilyBackgroundScreenState extends State { vertical: -4), title: Text( - "${spouse?.relatedPerson?.firstName} ${spouse?.relatedPerson?.middleName??''} ${spouse?.relatedPerson!.lastName} ${spouse?.relatedPerson?.nameExtension ?? ''}", + "${spouse?.relatedPerson?.firstName} ${spouse?.relatedPerson?.middleName ?? ''} ${spouse?.relatedPerson!.lastName} ${spouse?.relatedPerson?.nameExtension ?? ''}", style: Theme.of( context) .textTheme @@ -1356,7 +1369,7 @@ class _FamilyBackgroundScreenState extends State { horizontal: -4, vertical: -4), title: Text( - "${child.relatedPerson?.firstName} ${child.relatedPerson?.middleName??''} ${child.relatedPerson?.lastName} ${child.relatedPerson?.nameExtension ?? ''}", + "${child.relatedPerson?.firstName} ${child.relatedPerson?.middleName ?? ''} ${child.relatedPerson?.lastName} ${child.relatedPerson?.nameExtension ?? ''}", style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500)), subtitle: const Text("fullname"), @@ -1859,9 +1872,15 @@ class _FamilyBackgroundScreenState extends State { ), ), ]); - }if(state is FamilyErrorState){ - return SomethingWentWrong(message: state.message, onpressed: (){context.read().add(GetFamilies(profileId: profileId!, token: token!));}); - } + } + if (state is FamilyErrorState) { + return SomethingWentWrong( + message: state.message, + onpressed: () { + context.read().add(GetFamilies( + profileId: profileId!, token: token!)); + }); + } return Container(); }, ); diff --git a/lib/screens/profile/components/learning_and_development_screen.dart b/lib/screens/profile/components/learning_and_development_screen.dart index 1ca5d34..fc8df3b 100644 --- a/lib/screens/profile/components/learning_and_development_screen.dart +++ b/lib/screens/profile/components/learning_and_development_screen.dart @@ -35,19 +35,39 @@ class LearningAndDevelopmentScreen extends StatelessWidget { DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); return Scaffold( appBar: AppBar( - title: context.watch().state is LearningDevelopmentAddingState? const FittedBox(child: Text("Add $learningAndDevelopmentScreenTitle")):context.watch().state is LearningDevelopmentUpdatingState?const FittedBox(child: Text("Edit $learningAndDevelopmentScreenTitle")):const FittedBox(child: Text(learningAndDevelopmentScreenTitle)), - centerTitle: true, - backgroundColor: primary, - actions: (context.watch().state is LearningDevelopmentLoadedState)?[ - AddLeading(onPressed: () { - context.read().add(ShowAddLearningDevelopmentForm()); - }) - ]:(context.watch().state is LearningDevelopmentAddingState || context.watch().state is LearningDevelopmentUpdatingState)?[ - CloseLeading(onPressed:() { - context.read().add(LoadLearniningDevelopment()); - }) - ]:[] - ), + title: context.watch().state + is LearningDevelopmentAddingState + ? const FittedBox( + child: Text("Add $learningAndDevelopmentScreenTitle")) + : context.watch().state + is LearningDevelopmentUpdatingState + ? const FittedBox( + child: Text("Edit $learningAndDevelopmentScreenTitle")) + : const FittedBox( + child: Text(learningAndDevelopmentScreenTitle)), + centerTitle: true, + backgroundColor: primary, + actions: (context.watch().state + is LearningDevelopmentLoadedState) + ? [ + AddLeading(onPressed: () { + context + .read() + .add(ShowAddLearningDevelopmentForm()); + }) + ] + : (context.watch().state + is LearningDevelopmentAddingState || + context.watch().state + is LearningDevelopmentUpdatingState) + ? [ + CloseLeading(onPressed: () { + context + .read() + .add(LoadLearniningDevelopment()); + }) + ] + : []), body: ProgressHUD( padding: const EdgeInsets.all(24), indicatorWidget: const SpinKitFadingCircle( @@ -56,11 +76,13 @@ class LearningAndDevelopmentScreen extends StatelessWidget { backgroundColor: Colors.black87, child: BlocBuilder( builder: (context, state) { + if (state is UserLoggedIn) { token = state.userData!.user!.login!.token!; profileId = state.userData!.user!.login!.user!.profileId!; return BlocBuilder( builder: (context, state) { + if (state is ProfileLoaded) { return BlocConsumer( @@ -73,7 +95,8 @@ class LearningAndDevelopmentScreen extends StatelessWidget { state is LearningDevelopmentErrorState || state is LearningDevelopmentAddingState || state is LearningDevelopmentAddedState || - state is LearningDevelopmentUpdatingState) { + state is LearningDevelopmentUpdatingState || + state is LearningDevelopmentUpdatedState) { final progress = ProgressHUD.of(context); progress!.dismiss(); } @@ -99,7 +122,7 @@ class LearningAndDevelopmentScreen extends StatelessWidget { } } ////Updated State - if (state is LearningDevelopmentUpdatedState) { + if (state is LearningDevelopmentUpdatedState) { if (state.response['success']) { successAlert(context, "Update Successfull!", state.response['message'], () { @@ -123,7 +146,8 @@ class LearningAndDevelopmentScreen extends StatelessWidget { if (state is DeleteLearningDevelopmentState) { if (state.success) { successAlert(context, "Deletion Successfull!", - "Learning Development Has Been Deleted Successfully", () { + "Learning Development Has Been Deleted Successfully", + () { Navigator.of(context).pop(); context .read() @@ -143,7 +167,9 @@ class LearningAndDevelopmentScreen extends StatelessWidget { // TODO: implement listener }, builder: (context, state) { + print(state); if (state is LearningDevelopmentLoadedState) { + if (state.learningsAndDevelopment.isNotEmpty) { return ListView.builder( padding: const EdgeInsets.symmetric( @@ -200,9 +226,9 @@ class LearningAndDevelopmentScreen extends StatelessWidget { .copyWith( fontWeight: FontWeight - .w600,color: primary), + .w600, + color: primary), ), - const SizedBox( height: 8, ), @@ -296,7 +322,7 @@ class LearningAndDevelopmentScreen extends StatelessWidget { isOverseas)); } }, - menuItems: [ + menuItems: [ popMenuItem( text: "Update", value: 1, @@ -305,11 +331,10 @@ class LearningAndDevelopmentScreen extends StatelessWidget { text: "Remove", value: 2, icon: Icons.delete), - popMenuItem( + popMenuItem( text: "Attach", value: 2, icon: Icons.attach_file), - ], icon: const Icon( Icons.more_vert, @@ -334,7 +359,12 @@ class LearningAndDevelopmentScreen extends StatelessWidget { } if (state is LearningDevelopmentErrorState) { return (SomethingWentWrong( - message: state.message, onpressed: () {context.read().add(GetLearningDevelopments(profileId: profileId, token: token));})); + message: state.message, + onpressed: () { + context.read().add( + GetLearningDevelopments( + profileId: profileId, token: token)); + })); } if (state is LearningDevelopmentAddingState) { return AddLearningAndDevelopmentScreen( diff --git a/lib/screens/profile/components/learning_development/add_modal.dart b/lib/screens/profile/components/learning_development/add_modal.dart index 14a945d..5a0e4f7 100644 --- a/lib/screens/profile/components/learning_development/add_modal.dart +++ b/lib/screens/profile/components/learning_development/add_modal.dart @@ -1,6 +1,5 @@ import 'package:date_time_picker/date_time_picker.dart'; import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; diff --git a/lib/screens/profile/components/learning_development/edit_modal.dart b/lib/screens/profile/components/learning_development/edit_modal.dart index 7951297..ea5e04e 100644 --- a/lib/screens/profile/components/learning_development/edit_modal.dart +++ b/lib/screens/profile/components/learning_development/edit_modal.dart @@ -1,6 +1,5 @@ import 'package:date_time_picker/date_time_picker.dart'; import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; @@ -11,6 +10,7 @@ import 'package:searchfield/searchfield.dart'; import 'package:unit2/bloc/profile/learningDevelopment/learning_development_bloc.dart'; import 'package:unit2/model/profile/learning_development.dart'; import 'package:unit2/theme-data.dart/btn-style.dart'; +import 'package:unit2/utils/global_context.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/utils/validators.dart'; import '../../../../model/location/barangay.dart'; @@ -379,7 +379,7 @@ class _EditLearningAndDevelopmentScreenState selectableDayPredicate: (date) { if (to != null && - to!.microsecondsSinceEpoch <= + to!.microsecondsSinceEpoch < date.microsecondsSinceEpoch) { return false; } @@ -388,14 +388,10 @@ class _EditLearningAndDevelopmentScreenState onChanged: (value) { setState(() { from = - DateTime.parse(value); + DateTime.tryParse(value); }); }, - initialDate: to == null - ? DateTime.now() - : to!.subtract( - const Duration( - days: 1)), + initialDate: to??=DateTime.now(), timeHintText: "Date of Examination/Conferment", decoration: @@ -442,7 +438,7 @@ class _EditLearningAndDevelopmentScreenState ), selectableDayPredicate: (date) { if (from != null && - from!.microsecondsSinceEpoch >= + from!.microsecondsSinceEpoch > date.microsecondsSinceEpoch) { return false; } @@ -450,13 +446,10 @@ class _EditLearningAndDevelopmentScreenState }, onChanged: (value) { setState(() { - to = DateTime.parse(value); + to = DateTime.tryParse(value); }); }, - initialDate: from == null - ? DateTime.now() - : from!.add(const Duration( - days: 1)), + initialDate: from??=DateTime.now() ), ), ], @@ -545,12 +538,21 @@ class _EditLearningAndDevelopmentScreenState selectedRegion = region; //// GET PROVINCES - provinces = await LocationUtils - .instance - .getProvinces( - regionCode: selectedRegion! - .code - .toString()); + try { + provinces = await LocationUtils + .instance + .getProvinces( + regionCode: selectedRegion! + .code + .toString()); + } catch (e) { + context + .read< + LearningDevelopmentBloc>() + .add(CallErrorState( + message: + e.toString())); + } selectedProvince = provinces![0]; setState(() { @@ -559,11 +561,22 @@ class _EditLearningAndDevelopmentScreenState cityCall = true; }); //// GET CITIES - citymuns = await LocationUtils - .instance - .getCities( - code: selectedProvince! - .code!); + try { + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code!); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read< + LearningDevelopmentBloc>() + .add(CallErrorState( + message: + e.toString())); + } selectedMunicipality = citymuns![0]; setState(() { @@ -573,11 +586,22 @@ class _EditLearningAndDevelopmentScreenState true; }); //// GET BARANGAY - barangays = await LocationUtils - .instance - .getBarangay( - code: selectedMunicipality! - .code!); + try { + barangays = await LocationUtils + .instance + .getBarangay( + code: selectedMunicipality! + .code!); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read< + LearningDevelopmentBloc>() + .add(CallErrorState( + message: + e.toString())); + } selectedBarangay = barangays![0]; setState(() { @@ -585,11 +609,22 @@ class _EditLearningAndDevelopmentScreenState false; }); ////GET CITY MUNICIPALITY - citymuns = await LocationUtils - .instance - .getCities( - code: selectedProvince! - .code!); + try { + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code!); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read< + LearningDevelopmentBloc>() + .add(CallErrorState( + message: + e.toString())); + } selectedMunicipality = citymuns![0]; setState(() { @@ -599,11 +634,22 @@ class _EditLearningAndDevelopmentScreenState true; }); //// GET BARANGAYS - barangays = await LocationUtils - .instance - .getBarangay( - code: selectedMunicipality! - .code!); + try { + barangays = await LocationUtils + .instance + .getBarangay( + code: selectedMunicipality! + .code!); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read< + LearningDevelopmentBloc>() + .add(CallErrorState( + message: + e.toString())); + } selectedBarangay = barangays![0]; setState(() { @@ -645,70 +691,79 @@ class _EditLearningAndDevelopmentScreenState .transparent, inAsyncCall: provinceCall, - child: DropdownButtonFormField< - Province?>( - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: (value) => - value == null + child: + DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => value == null ? 'required' : null, - isExpanded: - true, - value: - selectedProvince, - onChanged: - (Province? - province) async { - if (selectedProvince != - province) { - selectedProvince = - province; - setState( - () { - cityCall = - true; - }); + isExpanded: + true, + value: + selectedProvince, + onChanged: + (Province? + province) async { + if (selectedProvince != + province) { + selectedProvince = + province; + setState( + () { + cityCall = + true; + }); - //// GET CITIES - citymuns = await LocationUtils - .instance - .getCities( - code: - selectedProvince!.code!); - selectedMunicipality = - citymuns![ - 0]; - setState( - () { - cityCall = - false; - barangayCall = - true; - }); - //// GET BARANGAY - barangays = await LocationUtils - .instance - .getBarangay( - code: - selectedMunicipality!.code!); - selectedBarangay = - barangays![ - 0]; - setState( - () { - barangayCall = - false; - }); - } - }, - items: - provinces == + //// GET CITIES + try { + citymuns = await LocationUtils + .instance + .getCities(code: selectedProvince!.code!); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read() + .add(CallErrorState(message: e.toString())); + } + selectedMunicipality = + citymuns![0]; + setState( + () { + cityCall = + false; + barangayCall = + true; + }); + //// GET BARANGAY + try { + barangays = await LocationUtils + .instance + .getBarangay(code: selectedMunicipality!.code!); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read() + .add(CallErrorState(message: e.toString())); + } + selectedBarangay = + barangays![0]; + setState( + () { + barangayCall = + false; + }); + } + }, + items: provinces == null ? [] - : provinces! - .map>((Province province) { + : provinces!.map>((Province + province) { return DropdownMenuItem( enabled: !enabled ? overseas : true, value: province, @@ -716,7 +771,13 @@ class _EditLearningAndDevelopmentScreenState child: Text(province.description!), )); }).toList(), - decoration: normalTextFieldStyle("Province*", "Province").copyWith(filled: !enabled ? !overseas : false, fillColor: Colors.grey.shade300)), + decoration: normalTextFieldStyle("Province*", "Province").copyWith( + filled: !enabled + ? !overseas + : false, + fillColor: Colors + .grey + .shade300)), ), ), ////CITY MUNICIPALITY @@ -729,7 +790,6 @@ class _EditLearningAndDevelopmentScreenState cityCall, child: DropdownButtonFormField< CityMunicipality>( - validator: FormBuilderValidators .required( errorText: @@ -746,13 +806,24 @@ class _EditLearningAndDevelopmentScreenState }); selectedMunicipality = city; - + //// GET BARANGAYS - barangays = await LocationUtils - .instance - .getBarangay( - code: - selectedMunicipality!.code!); + try { + barangays = await LocationUtils + .instance + .getBarangay( + code: + selectedMunicipality!.code!); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read< + LearningDevelopmentBloc>() + .add(CallErrorState( + message: + e.toString())); + } selectedBarangay = barangays![ 0]; @@ -805,7 +876,7 @@ class _EditLearningAndDevelopmentScreenState child: DropdownButtonFormField< Barangay>( - isExpanded: true, + isExpanded: true, onChanged: (Barangay? baragay) { diff --git a/lib/screens/profile/components/reference/add_modal.dart b/lib/screens/profile/components/reference/add_modal.dart index 5883898..cfd8ca3 100644 --- a/lib/screens/profile/components/reference/add_modal.dart +++ b/lib/screens/profile/components/reference/add_modal.dart @@ -110,7 +110,6 @@ class _AddReferenceScreenState extends State { inputFormatters: [ UpperCaseTextFormatter() ], - decoration: normalTextFieldStyle( "Middle name ", ""), name: "middlename", diff --git a/lib/screens/profile/components/reference/edit_modal.dart b/lib/screens/profile/components/reference/edit_modal.dart index 803d7cb..479bed8 100644 --- a/lib/screens/profile/components/reference/edit_modal.dart +++ b/lib/screens/profile/components/reference/edit_modal.dart @@ -4,6 +4,7 @@ import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart'; +import 'package:unit2/utils/global_context.dart'; import '../../../../bloc/profile/references/references_bloc.dart'; import '../../../../model/location/address_category.dart'; import '../../../../model/location/barangay.dart'; @@ -72,7 +73,7 @@ class _EditReferenceScreenState extends State { return FormBuilder( key: formKey, child: SizedBox( - height: screenHeight * .90, + height: screenHeight * .90, child: Padding( padding: const EdgeInsets.all(28), child: Column( @@ -87,9 +88,7 @@ class _EditReferenceScreenState extends State { Flexible( flex: 1, child: FormBuilderTextField( - inputFormatters: [ - UpperCaseTextFormatter() - ], + inputFormatters: [UpperCaseTextFormatter()], initialValue: state.ref.lastName, decoration: normalTextFieldStyle( "Last name *", "Last name *"), @@ -105,10 +104,7 @@ class _EditReferenceScreenState extends State { Flexible( flex: 1, child: FormBuilderTextField( - inputFormatters: [ - UpperCaseTextFormatter() - ], - + inputFormatters: [UpperCaseTextFormatter()], initialValue: state.ref.firstName, decoration: normalTextFieldStyle( "First name *", "First name *"), @@ -127,15 +123,11 @@ class _EditReferenceScreenState extends State { Flexible( flex: 1, child: FormBuilderTextField( - inputFormatters: [ - UpperCaseTextFormatter() - ], - + inputFormatters: [UpperCaseTextFormatter()], initialValue: state.ref.middleName, decoration: normalTextFieldStyle( "Middle name *", "Midlle name *"), name: "middlename", - ), ), const SizedBox( @@ -160,8 +152,8 @@ class _EditReferenceScreenState extends State { ), FormBuilderDropdown( name: 'category', - validator: - FormBuilderValidators.required(errorText: "This field is required"), + validator: FormBuilderValidators.required( + errorText: "This field is required"), decoration: normalTextFieldStyle( "Address Category", "Address Category"), items: state.categories @@ -209,62 +201,95 @@ class _EditReferenceScreenState extends State { ////REGION DROPDOWN DropdownButtonFormField( isExpanded: true, - autovalidateMode: AutovalidateMode - .onUserInteraction, - validator: - FormBuilderValidators.required( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: FormBuilderValidators + .required( errorText: "This field is required"), - onChanged: (Region? region) async { + onChanged: + (Region? region) async { setState(() { provinceCall = true; - + selectedRegion = region; }); //// GET PROVINCES - provinces = await LocationUtils - .instance - .getProvinces( - regionCode: selectedRegion! - .code - .toString()); - selectedProvince = provinces![0]; + try { + provinces = await LocationUtils + .instance + .getProvinces( + regionCode: + selectedRegion! + .code + .toString()); + } catch (e) { + context + .read() + .add(CallErrorState()); + } + selectedProvince = + provinces![0]; setState(() { provinceCall = false; cityCall = true; }); ////GET CITY MUNICIPALITY - citymuns = await LocationUtils - .instance - .getCities( - code: selectedProvince! - .code!); - selectedMunicipality = citymuns![0]; + try { + citymuns = await LocationUtils + .instance + .getCities( + code: + selectedProvince! + .code!); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read() + .add(CallErrorState()); + } + selectedMunicipality = + citymuns![0]; setState(() { cityCall = false; barangayCall = true; }); //// GET BARANGAYS - barangays = await LocationUtils - .instance - .getBarangay( - code: selectedMunicipality! - .code!); - selectedBarangay = barangays![0]; + try { + barangays = await LocationUtils + .instance + .getBarangay( + code: + selectedMunicipality! + .code!); + selectedBarangay = + barangays![0]; + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read() + .add(CallErrorState()); + } setState(() { barangayCall = false; }); }, value: selectedRegion, - decoration: normalTextFieldStyle( - "Region*", "Region"), - items: state.regions - .map>( - (Region region) { - return DropdownMenuItem( + decoration: + normalTextFieldStyle( + "Region*", "Region"), + items: state.regions.map< + DropdownMenuItem< + Region>>( + (Region region) { + return DropdownMenuItem< + Region>( value: region, - child: - Text(region.description!)); + child: Text( + region.description!)); }).toList(), ), const SizedBox( @@ -286,19 +311,30 @@ class _EditReferenceScreenState extends State { ? 'required' : null, isExpanded: true, - onChanged: - (Province? province) async { - selectedProvince = province; + onChanged: (Province? + province) async { + selectedProvince = + province; setState(() { cityCall = true; }); //// GET CITIES - citymuns = await LocationUtils - .instance - .getCities( - code: - selectedProvince! - .code!); + try { + citymuns = + await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code!); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read< + ReferencesBloc>() + .add( + CallErrorState()); + } selectedMunicipality = citymuns![0]; setState(() { @@ -306,12 +342,22 @@ class _EditReferenceScreenState extends State { barangayCall = true; }); //// GET BARANGAY - barangays = await LocationUtils - .instance - .getBarangay( - code: - selectedMunicipality! - .code!); + try { + barangays = + await LocationUtils + .instance + .getBarangay( + code: selectedMunicipality! + .code!); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read< + ReferencesBloc>() + .add( + CallErrorState()); + } selectedBarangay = barangays![0]; setState(() { @@ -324,12 +370,16 @@ class _EditReferenceScreenState extends State { : provinces!.map< DropdownMenuItem< Province>>( - (Province province) { + (Province + province) { return DropdownMenuItem( - value: province, - child: FittedBox( - child: Text(province - .description!), + value: + province, + child: + FittedBox( + child: Text( + province + .description!), )); }).toList(), decoration: @@ -344,46 +394,61 @@ class _EditReferenceScreenState extends State { child: ModalProgressHUD( color: Colors.white, inAsyncCall: cityCall, - child: DropdownButtonFormField< - CityMunicipality>( + child: + DropdownButtonFormField< + CityMunicipality>( validator: FormBuilderValidators .required( errorText: "This field is required"), isExpanded: true, - onChanged: (CityMunicipality? - city) async { + onChanged: + (CityMunicipality? + city) async { setState(() { barangayCall = true; }); - selectedMunicipality = city; + selectedMunicipality = + city; //// GET BARANGAYS - barangays = await LocationUtils - .instance - .getBarangay( - code: - selectedMunicipality! - .code!); + try { + barangays = + await LocationUtils + .instance + .getBarangay( + code: selectedMunicipality! + .code!); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read< + ReferencesBloc>() + .add( + CallErrorState()); + } selectedBarangay = barangays![0]; setState(() { barangayCall = false; }); }, - decoration: normalTextFieldStyle( - "Municipality*", - "Municipality"), + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), value: selectedMunicipality, items: citymuns == null ? [] : citymuns!.map< DropdownMenuItem< CityMunicipality>>( - (CityMunicipality c) { + (CityMunicipality + c) { return DropdownMenuItem( value: c, - child: Text( - c.description!)); + child: Text(c + .description!)); }).toList(), ), ), @@ -394,25 +459,31 @@ class _EditReferenceScreenState extends State { child: ModalProgressHUD( color: Colors.white, inAsyncCall: barangayCall, - child: DropdownButtonFormField< - Barangay>( + child: + DropdownButtonFormField< + Barangay>( validator: FormBuilderValidators .required( errorText: "This field is required"), isExpanded: true, - onChanged: (Barangay? baragay) { - selectedBarangay = baragay; + onChanged: + (Barangay? baragay) { + selectedBarangay = + baragay; }, - decoration: normalTextFieldStyle( - "Barangay*", "Barangay"), + decoration: + normalTextFieldStyle( + "Barangay*", + "Barangay"), value: selectedBarangay, items: barangays == null ? [] : barangays!.map< DropdownMenuItem< Barangay>>( - (Barangay barangay) { + (Barangay + barangay) { return DropdownMenuItem( value: barangay, child: Text(barangay @@ -426,22 +497,28 @@ class _EditReferenceScreenState extends State { //// COUNTRY DROPDOWN : SizedBox( height: 60, - child: DropdownButtonFormField( + child: DropdownButtonFormField< + Country>( isExpanded: true, - value: selectedCountry?.id==175?null:selectedCountry, - validator: - FormBuilderValidators.required( + value: selectedCountry?.id == 175 + ? null + : selectedCountry, + validator: FormBuilderValidators + .required( errorText: "This field is required"), - items: state.countries - .map>( - (Country country) { - return DropdownMenuItem( + items: state.countries.map< + DropdownMenuItem< + Country>>( + (Country country) { + return DropdownMenuItem< + Country>( value: country, child: FittedBox( - child: Text(country.name!))); + child: Text( + country.name!))); }).toList(), - + decoration: normalTextFieldStyle( "Country*", "Country"), //// country dropdown @@ -454,93 +531,91 @@ class _EditReferenceScreenState extends State { ], ); }), - - - + const SizedBox( height: 20, ), ], ), ), - SizedBox( - width: double.infinity, - height: 60, - child: ElevatedButton( - style: - mainBtnStyle(primary, Colors.transparent, second), - child: const Text(submit), - onPressed: () { - PersonalReference? personalReference; - if (formKey.currentState!.saveAndValidate()) { - final progress = ProgressHUD.of(context); - progress!.showWithText("Please wait..."); - String lastname = - formKey.currentState!.value['lastname']; - String firstname = - formKey.currentState!.value['firstname']; - String? middlename = - formKey.currentState?.value['middlename']; - String mobile = - formKey.currentState!.value['mobile']; - - Region? region = selectedRegion; - Province? province = Province( - code: selectedProvince?.code, - description: selectedProvince?.description, - region: region, - psgcCode: selectedProvince?.psgcCode, - shortname: selectedProvince?.shortname); - CityMunicipality? city = CityMunicipality( - code: selectedMunicipality?.code, - description: - selectedMunicipality?.description, - province: province, - psgcCode: selectedMunicipality?.psgcCode, - zipcode: selectedMunicipality?.zipcode); - - ////IF IS OVERSEAS - if (overseas) { - personalReference = PersonalReference( - id: state.ref.id, - address: Address( - id: state.ref.address!.id, - addressCategory: selectedCategory, - country: selectedCountry, - barangay: null, - cityMunicipality: null, - addressClass: null), - lastName: lastname, - contactNo: mobile, - firstName: firstname, - middleName: middlename); - } else { - //// IF NOT OVERSEAS - personalReference = PersonalReference( - id: state.ref.id, - address: Address( - id: state.ref.address!.id, - addressCategory: selectedCategory, - country: Country( - id: 175, code: null, name: null), - barangay: selectedBarangay, - cityMunicipality: city, - addressClass: - state.ref.address?.addressClass), - lastName: lastname, - contactNo: mobile, - firstName: firstname, - middleName: middlename); - } - - context.read().add(EditReference( - profileId: widget.profileId, - reference: personalReference, - token: widget.token)); - } - }, - ), - ), + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: + mainBtnStyle(primary, Colors.transparent, second), + child: const Text(submit), + onPressed: () { + PersonalReference? personalReference; + if (formKey.currentState!.saveAndValidate()) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + String lastname = + formKey.currentState!.value['lastname']; + String firstname = + formKey.currentState!.value['firstname']; + String? middlename = + formKey.currentState?.value['middlename']; + String mobile = + formKey.currentState!.value['mobile']; + + Region? region = selectedRegion; + Province? province = Province( + code: selectedProvince?.code, + description: selectedProvince?.description, + region: region, + psgcCode: selectedProvince?.psgcCode, + shortname: selectedProvince?.shortname); + CityMunicipality? city = CityMunicipality( + code: selectedMunicipality?.code, + description: + selectedMunicipality?.description, + province: province, + psgcCode: selectedMunicipality?.psgcCode, + zipcode: selectedMunicipality?.zipcode); + + ////IF IS OVERSEAS + if (overseas) { + personalReference = PersonalReference( + id: state.ref.id, + address: Address( + id: state.ref.address!.id, + addressCategory: selectedCategory, + country: selectedCountry, + barangay: null, + cityMunicipality: null, + addressClass: null), + lastName: lastname, + contactNo: mobile, + firstName: firstname, + middleName: middlename); + } else { + //// IF NOT OVERSEAS + personalReference = PersonalReference( + id: state.ref.id, + address: Address( + id: state.ref.address!.id, + addressCategory: selectedCategory, + country: Country( + id: 175, code: null, name: null), + barangay: selectedBarangay, + cityMunicipality: city, + addressClass: + state.ref.address?.addressClass), + lastName: lastname, + contactNo: mobile, + firstName: firstname, + middleName: middlename); + } + + context.read().add(EditReference( + profileId: widget.profileId, + reference: personalReference, + token: widget.token)); + } + }, + ), + ), ], ), ), diff --git a/lib/screens/profile/components/references_screen.dart b/lib/screens/profile/components/references_screen.dart index 1f53c27..a9731fe 100644 --- a/lib/screens/profile/components/references_screen.dart +++ b/lib/screens/profile/components/references_screen.dart @@ -71,6 +71,7 @@ class ReferencesScreen extends StatelessWidget { return BlocConsumer( //listener listener: (context, state) { + print(state); if (state is ReferencesLoadingState) { final progress = ProgressHUD.of(context); progress!.showWithText("Please wait..."); diff --git a/lib/screens/profile/components/voluntary_works/edit_modal.dart b/lib/screens/profile/components/voluntary_works/edit_modal.dart index 4ea1a6a..b841bda 100644 --- a/lib/screens/profile/components/voluntary_works/edit_modal.dart +++ b/lib/screens/profile/components/voluntary_works/edit_modal.dart @@ -578,6 +578,11 @@ class _EditVoluntaryWorkScreenState extends State { selectedRegion! .code .toString()); + }catch(e){ + context + .read() + .add(ShowErrorState(message: e.toString())); + } selectedProvince = provinces![0]; setState(() { @@ -606,14 +611,7 @@ class _EditVoluntaryWorkScreenState extends State { message: e .toString())); } - } catch (e) { - context - .read< - VoluntaryWorkBloc>() - .add(ShowErrorState( - message: e - .toString())); - } + } }, value: selectedRegion, diff --git a/lib/screens/profile/components/voluntary_works_screen.dart b/lib/screens/profile/components/voluntary_works_screen.dart index 4544fe5..c52dece 100644 --- a/lib/screens/profile/components/voluntary_works_screen.dart +++ b/lib/screens/profile/components/voluntary_works_screen.dart @@ -268,7 +268,8 @@ class VolunataryWorkScreen extends StatelessWidget { icon: Icons.edit), popMenuItem( text: "Remove", - value: 2,) + value: 2, + icon: Icons.delete) ], diff --git a/lib/screens/sos/components/add_mobile.dart b/lib/screens/sos/components/add_mobile.dart index 3025e92..cb2ec9b 100644 --- a/lib/screens/sos/components/add_mobile.dart +++ b/lib/screens/sos/components/add_mobile.dart @@ -36,16 +36,13 @@ class AddMobile extends StatelessWidget { Container( height: screenHeight, padding: isMobile() - ? const EdgeInsets.symmetric(horizontal: 24) + ? const EdgeInsets.symmetric(horizontal: 60) : const EdgeInsets.symmetric(horizontal: 60), width: double.infinity, child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ - SizedBox( - height: isMobile() - ? screenHeight * .12 - : screenHeight * .20), + const SizedBox(height: 100,), SvgPicture.asset( 'assets/svgs/add_mobile.svg', height: isMobile() @@ -56,13 +53,13 @@ class AddMobile extends StatelessWidget { const SizedBox( height: 24, ), - Text(addMobile, style: titleTextStyle()), + Text(addMobile,textAlign: TextAlign.center, style: titleTextStyle()), const SizedBox( height: 8, ), Text(addMobileCaption, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.caption), + style: Theme.of(context).textTheme.bodySmall), const SizedBox( height: 24, ), @@ -78,9 +75,7 @@ class AddMobile extends StatelessWidget { maxLength: 11, decoration: normalTextFieldStyle(mobile1, "09")), - const SizedBox( - height: 12, - ), + //// Mobile number 2 FormBuilderTextField( autovalidateMode: AutovalidateMode.onUserInteraction, @@ -89,10 +84,7 @@ class AddMobile extends StatelessWidget { decoration: normalTextFieldStyle(mobile2, "09")), - SizedBox( - height: isMobile() - ? blockSizeVertical * 3 - : blockSizeHorizontal * 5), + const SizedBox(height: 30,), SizedBox( width: double.infinity, height: screenHeight * .06, diff --git a/lib/screens/sos/components/request_sos.dart b/lib/screens/sos/components/request_sos.dart index 990de3e..a7510a6 100644 --- a/lib/screens/sos/components/request_sos.dart +++ b/lib/screens/sos/components/request_sos.dart @@ -40,7 +40,7 @@ class _RequestSOSState extends State { return SingleChildScrollView( child: Container( height: screenHeight * .82, - padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 10), + padding: const EdgeInsets.symmetric(horizontal: 42, vertical: 10), child: FormBuilder( key: formKey, child: Column( diff --git a/lib/screens/sos/index.dart b/lib/screens/sos/index.dart index f3bdb90..cb9dbcc 100644 --- a/lib/screens/sos/index.dart +++ b/lib/screens/sos/index.dart @@ -47,7 +47,7 @@ class _SosScreenState extends State { Widget build(BuildContext context) { return SafeArea( child: Scaffold( - appBar: AppBar(backgroundColor: primary), + appBar: AppBar(title:const Text("SOS"),backgroundColor: primary,centerTitle: true,), resizeToAvoidBottomInset: true, body: ProgressHUD( padding: const EdgeInsets.all(32), diff --git a/lib/screens/unit2/basic-info/basic-info.dart b/lib/screens/unit2/basic-info/basic-info.dart index c16824a..2194b21 100644 --- a/lib/screens/unit2/basic-info/basic-info.dart +++ b/lib/screens/unit2/basic-info/basic-info.dart @@ -42,7 +42,7 @@ class BasicInfo extends StatelessWidget { children: [ const CoverImage(), Positioned( - top: blockSizeVertical * 17.5, + top: blockSizeVertical * 15.5, child: Stack( alignment: Alignment.center, children: [ @@ -115,8 +115,8 @@ class BuildInformation extends StatelessWidget { final String firstName = userData.user!.login!.user!.firstName!.toUpperCase(); final String lastname = userData.user!.login!.user!.lastName!.toUpperCase(); - final String middlename = - userData.employeeInfo!.profile!.middleName!.toUpperCase(); + final String? middlename = userData.employeeInfo == null?'': + userData.employeeInfo!.profile?.middleName?.toUpperCase(); final String sex = userData.employeeInfo!.profile!.sex!.toUpperCase(); final DateTime? bday = userData.employeeInfo!.profile!.birthdate; final uuid = userData.employeeInfo!.uuid; @@ -129,11 +129,11 @@ class BuildInformation extends StatelessWidget { height: 25, ), Text( - "$firstName $middlename $lastname", + "$firstName ${middlename??''} $lastname", textAlign: TextAlign.center, style: Theme.of(context) .textTheme - .headline5! + .headlineSmall! .copyWith(fontWeight: FontWeight.bold), ), const SizedBox( @@ -141,7 +141,7 @@ class BuildInformation extends StatelessWidget { ), Text( "${dteFormat2.format(bday!)} | $sex", - style: Theme.of(context).textTheme.caption!.copyWith(fontSize: 18), + style: Theme.of(context).textTheme.bodySmall!.copyWith(fontSize: 18), ), const SizedBox( height: 15, @@ -162,7 +162,7 @@ class BuildInformation extends StatelessWidget { mainBtnStyle(third, Colors.transparent, Colors.white54), onPressed: () { Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ - return SignaturePad(); + return const SignaturePad(); })); }, icon: const Icon( diff --git a/lib/screens/unit2/homepage.dart/components/dashboard.dart b/lib/screens/unit2/homepage.dart/components/dashboard.dart index 13bec9e..e1056ac 100644 --- a/lib/screens/unit2/homepage.dart/components/dashboard.dart +++ b/lib/screens/unit2/homepage.dart/components/dashboard.dart @@ -1,20 +1,18 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; -import 'package:unit2/screens/docsms/components/request_receipt.dart'; import 'package:unit2/screens/docsms/index.dart'; import 'package:unit2/screens/unit2/homepage.dart/module-screen.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/global_context.dart'; import 'package:unit2/utils/qr_scanner.dart'; -import 'package:unit2/utils/text_container.dart'; - import '../../../../bloc/docsms/docsms_bloc.dart'; class DashBoard extends StatelessWidget { final List roles; - const DashBoard({super.key, required this.roles}); + final int userId; + const DashBoard({super.key, required this.roles,required this.userId}); @override Widget build(BuildContext context) { List finishRoles = []; @@ -27,6 +25,7 @@ class DashBoard extends StatelessWidget { shrinkWrap: true, itemCount: roles.length, itemBuilder: (BuildContext context, int index) { + //// gridview.count return roles[index].roles.isNotEmpty ? SizedBox( @@ -53,25 +52,21 @@ class DashBoard extends StatelessWidget { padding: const EdgeInsets.symmetric( vertical: 5, horizontal: 5), children: roles[index].roles.map((role) { - //// if role name is qr code && finishRoles not contain security - //// this card will visible if the user has qr code scanner or security roleπurls if (role.role.name!.toLowerCase() == 'qr code scanner' && !finishRoles.contains("security")) { + print("1 true"); finishRoles.add('scanner'); - //// loop thru module to find unit module exist for (var element in role.role.modules!) { if (element!.name!.toLowerCase() == 'unit2') { for (var element in element.objects!) { - //// loop thru objects to find pass check exist - if (element!.id == 9 && - //// check if operations contains read and write + if (element!.id == 9 && element.operations! .contains("read")) { - //// if all conditions are true return card return CardLabel( ontap: () { - Navigator.pushNamed(context, '/pass-check'); + PassCheckArguments passCheckArguments = PassCheckArguments(roleId: role.role.id!, userId: userId); + Navigator.pushNamed(context, '/pass-check',arguments: passCheckArguments); }, icon: role.icon, title: "Pass Check", @@ -81,11 +76,10 @@ class DashBoard extends StatelessWidget { } } return Container(); - //// if role name is security - //// this card will visible if the user has qr code scanner or security role } else if (role.role.name!.toLowerCase() == 'security guard' && !finishRoles.contains('scanner')) { + print("2 true"); finishRoles.add('security'); for (var element in role.role.modules!) { if (element!.name!.toLowerCase() == 'unit2') { @@ -102,10 +96,10 @@ class DashBoard extends StatelessWidget { } } } - return Container(); - //// if role name is field surveyor + return Container(color: Colors.red,); } else if (role.role.name!.toLowerCase() == 'field surveyor') { + print("3 true"); for (var element in role.role.modules!) { if (element!.name!.toLowerCase() == 'rpass') { for (var element in element.objects!) { @@ -122,9 +116,9 @@ class DashBoard extends StatelessWidget { } } return Container(); - //// if role name is process server } else if (role.role.name!.toLowerCase() == 'process server') { + print("4 true"); for (var element in role.role.modules!) { if (element!.name!.toLowerCase() == 'document management') { @@ -158,15 +152,16 @@ class DashBoard extends StatelessWidget { } return Container(); } else if (role.role.name!.toLowerCase() == - 'establishment point person' && + 'establishment point-person' && !finishRoles.contains('superadmin')) { - finishRoles.add('establishment point person'); + finishRoles.add('establishment point-person'); for (var element in role.role.modules!) { + print("5 true"); if (element!.name!.toLowerCase() == 'unit2') { for (var element in element.objects!) { - if (element!.id == 9 && + if (element!.id == 7 && element.operations! - .contains("read")) { + .contains("upload")) { return CardLabel( ontap: () {}, icon: FontAwesome5.building, @@ -177,13 +172,33 @@ class DashBoard extends StatelessWidget { } } return Container(); - //// if role name is field surveyor - } - - - else { + + } else if (role.role.name!.toLowerCase() == + 'establishment point-person' && + !finishRoles.contains('superadmin')) { + finishRoles.add('establishment point-person'); + for (var element in role.role.modules!) { + if (element!.name!.toLowerCase() == 'unit2') { + for (var element in element.objects!) { + if (element!.id == 7 && + element.operations! + .contains("upload")) { + return CardLabel( + ontap: () {}, + icon: FontAwesome5.building, + title: "Establishment", + ); + } + } + } + } return Container(); + + } else{ + return Wrap(); } + + }).toList()), const SizedBox( height: 8, @@ -197,6 +212,12 @@ class DashBoard extends StatelessWidget { } } +class PassCheckArguments{ + final int roleId; + final int userId; + const PassCheckArguments({required this.roleId, required this.userId}); +} + // ignore: must_be_immutable class CardLabel extends StatelessWidget { final String title; diff --git a/lib/screens/unit2/homepage.dart/components/menu-screen.dart b/lib/screens/unit2/homepage.dart/components/menu-screen.dart index b9cedc2..0b712e3 100644 --- a/lib/screens/unit2/homepage.dart/components/menu-screen.dart +++ b/lib/screens/unit2/homepage.dart/components/menu-screen.dart @@ -27,50 +27,50 @@ class _MenuScreenState extends State { child: Column( mainAxisSize: MainAxisSize.max, children: [ - Flexible( - child: ListView( - // ignore: prefer_const_literals_to_create_immutables - children: [ - UserAccountsDrawerHeader( - decoration: const BoxDecoration( - color: primary, - image: DecorationImage( - image: AssetImage('assets/pngs/bg.png'), - fit: BoxFit.cover)), - accountName: Text("$firstName $lastname"), - accountEmail: null, - currentAccountPicture: CircleAvatar( - radius: 40, - backgroundColor: fifth, - child: CircleAvatar( - radius: 33, - backgroundColor: third, - child: //Icon(Icons.person, size: 40, color: fifth), - Text( - firstName[0].toUpperCase(), - style: const TextStyle(fontSize: 45.0, color: fifth), - ), + Column( + // ignore: prefer_const_literals_to_create_immutables + children: [ + UserAccountsDrawerHeader( + decoration: const BoxDecoration( + color: primary, + image: DecorationImage( + image: AssetImage('assets/pngs/bg.png'), + fit: BoxFit.cover)), + accountName: Text("$firstName $lastname"), + accountEmail: null, + currentAccountPicture: CircleAvatar( + radius: 40, + backgroundColor: fifth, + child: CircleAvatar( + radius: 33, + backgroundColor: third, + child: //Icon(Icons.person, size: 40, color: fifth), + Text( + firstName[0].toUpperCase(), + style: const TextStyle(fontSize: 45.0, color: fifth), ), ), ), - getTile(FontAwesome5.user, "Basic Info", '/basic-info', context, - widget.userData!), - const Divider(), - getTile(FontAwesome5.user_circle, "Profile", - '/profile', context, widget.userData!), - const Divider(), - getTile(FontAwesome5.life_ring, "Request SOS", '/sos', - context, widget.userData!), - - ], - ), + ), + getTile(FontAwesome5.user, "Basic Info", '/basic-info', context, + widget.userData!), + const Divider(), + getTile(FontAwesome5.user_circle, "Profile", + '/profile', context, widget.userData!), + const Divider(), + getTile(FontAwesome5.life_ring, "Request SOS", '/sos', + context, widget.userData!), + + ], ), + const Expanded(child: SizedBox()), const Divider(), Align( alignment: FractionalOffset.bottomLeft, child: getTile(WebSymbols.logout, "Logout", '/', context, widget.userData!), ), + const SizedBox(height: 10,), ], ), ), diff --git a/lib/screens/unit2/homepage.dart/components/menu.dart b/lib/screens/unit2/homepage.dart/components/menu.dart index 6288a3f..abb12bc 100644 --- a/lib/screens/unit2/homepage.dart/components/menu.dart +++ b/lib/screens/unit2/homepage.dart/components/menu.dart @@ -1,8 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:unit2/model/login_data/employee_info/employee_info.dart'; import 'package:unit2/model/login_data/user_info/user_data.dart'; import 'package:unit2/utils/alerts.dart'; -import '../../../../model/profile/basic_information/primary-information.dart'; +import 'package:unit2/utils/global_context.dart'; import '../../../../theme-data.dart/colors.dart'; import '../../../../utils/global.dart'; @@ -23,7 +22,7 @@ Widget getTile( confirmAlert(context, () async{ await CREDENTIALS!.clear(); await CREDENTIALS!.deleteAll(['username','password','saved']); - Navigator.pushReplacementNamed (context,"/"); + Navigator.pushReplacementNamed (NavigationService.navigatorKey.currentContext!,"/"); },"Logout","Are You sure you want to logout?"); }if(title.toLowerCase() == 'profile'){ ProfileArguments profileArguments = ProfileArguments(token: userData.user!.login!.token!, userID:userData.user!.login!.user!.profileId!); diff --git a/lib/screens/unit2/homepage.dart/module-screen.dart b/lib/screens/unit2/homepage.dart/module-screen.dart index aef990f..e8a9585 100644 --- a/lib/screens/unit2/homepage.dart/module-screen.dart +++ b/lib/screens/unit2/homepage.dart/module-screen.dart @@ -25,6 +25,7 @@ class _MainScreenState extends State { Module(name: 'DocSms module operations', roles: []), Module(name: "RPAss module operations",roles:[] ) ]; + int? userId; @override Widget build(BuildContext context) { return WillPopScope( @@ -33,6 +34,7 @@ class _MainScreenState extends State { }, child: BlocBuilder(builder: (context, state) { if (state is UserLoggedIn) { + userId = state.userData!.user!.login!.user!.id; for (var element in roles) { element.roles.clear(); } @@ -78,6 +80,7 @@ class _MainScreenState extends State { ), body: state.userData!.user!.login!.user!.roles!.isNotEmpty ? DashBoard( + userId: userId!, roles: roles, ) : const NoModule(), diff --git a/lib/screens/unit2/login/login.dart b/lib/screens/unit2/login/login.dart index 164a5c3..c0a0c03 100644 --- a/lib/screens/unit2/login/login.dart +++ b/lib/screens/unit2/login/login.dart @@ -273,7 +273,7 @@ class _UniT2LoginState extends State { SizedBox( height: blockSizeVertical * 1.5, ), - +//// Login via Scan QR SizedBox( height: blockSizeVertical * 7, child: SizedBox( @@ -367,6 +367,16 @@ class _UniT2LoginState extends State { if (state is SplashScreen) { return const UniTSplashScreen(); } + if(state is LoginErrorState){ + return SomethingWentWrong(message: state.message, onpressed: () { + BlocProvider.of( + NavigationService.navigatorKey.currentContext!) + .add(GetApkVersion()); + return MaterialPageRoute(builder: (_) { + return const UniT2Login(); + }); + },); + } return Container(); }), ), diff --git a/lib/screens/unit2/login/qr_login.dart b/lib/screens/unit2/login/qr_login.dart index 344b2bd..9fd52c5 100644 --- a/lib/screens/unit2/login/qr_login.dart +++ b/lib/screens/unit2/login/qr_login.dart @@ -15,8 +15,10 @@ import '../../../theme-data.dart/colors.dart'; import '../../../theme-data.dart/form-style.dart'; import '../../../utils/alerts.dart'; import '../../../utils/global.dart'; +import '../../../utils/internet_time_out.dart'; import '../../../utils/text_container.dart'; import '../../../utils/validators.dart'; +import '../../../widgets/error_state.dart'; class QRLogin extends StatefulWidget { const QRLogin({super.key}); @@ -96,7 +98,7 @@ class _QRLoginState extends State { bottom: 0, child: WaveReverse(height: blockSizeVertical * 8)), Container( - height: screenHeight * .90, + height: screenHeight * .87, padding: const EdgeInsets.symmetric(horizontal: 32), child: FormBuilder( key: _formKey, @@ -221,7 +223,23 @@ class _QRLoginState extends State { ], ), ); - } + } if (state is InternetTimeout) { + return SomethingWentWrong(message: state.message, onpressed: () { + context + .read() + .add(LoadUuid()); + Navigator.of(context).pop(); + },); + } + + if(state is LoginErrorState){ + return SomethingWentWrong(message: state.message, onpressed: () { + context + .read() + .add(LoadUuid()); + Navigator.of(context).pop(); + },); + } return Container(); }, ), diff --git a/lib/screens/unit2/roles/qr_code_scanner.dart/scan.dart b/lib/screens/unit2/roles/qr_code_scanner.dart/scan.dart index 8a26cb8..5e25ab4 100644 --- a/lib/screens/unit2/roles/qr_code_scanner.dart/scan.dart +++ b/lib/screens/unit2/roles/qr_code_scanner.dart/scan.dart @@ -1,148 +1,360 @@ +import 'package:assets_audio_player/assets_audio_player.dart'; +import 'package:audioplayers/audioplayers.dart'; +import 'package:cool_alert/cool_alert.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:fluttericon/entypo_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:unit2/bloc/role/pass_check/pass_check_bloc.dart'; +import 'package:unit2/theme-data.dart/btn-style.dart'; +import 'package:unit2/theme-data.dart/form-style.dart'; import 'package:unit2/utils/text_container.dart'; - +import 'package:unit2/utils/validators.dart'; +import 'package:unit2/widgets/error_state.dart'; import '../../../../theme-data.dart/colors.dart'; import '../../../../utils/global.dart'; import 'components/save_settings.dart'; -class QRCodeScanner extends StatelessWidget { +class QRCodeScanner extends StatefulWidget { const QRCodeScanner({super.key}); + @override + State createState() => _QRCodeScannerState(); +} + +final GlobalKey scaffoldKey = GlobalKey(); +final formKey = GlobalKey(); +AudioPlayer? player; + +class _QRCodeScannerState extends State { + @override + void initState() { + player = AudioPlayer(); + super.initState(); + } + @override + void dispose() { + player?.dispose(); + super.dispose(); + } + + @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: const Text(qrScannerTitle), - centerTitle: true, - backgroundColor: primary, + key: scaffoldKey, + appBar: AppBar( + title: const Text(qrScannerTitle), + centerTitle: true, + backgroundColor: primary, + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + indicatorWidget: const SpinKitFadingCircle( + color: Colors.white, ), - body: SizedBox( - height: screenHeight * 100, - width: double.infinity, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const SizedBox( - height: 40, - ), - GestureDetector( - onTap: () {}, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox( - height: 160, - child: Image.asset('assets/pngs/qr-scan.png')), - const SizedBox( - height: 8, - ), - Text( - tapToScanQR, - textAlign: TextAlign.center, - style: Theme.of(context) - .textTheme - .displayMedium! - .copyWith( - fontSize: 22, - color: primary, - fontWeight: FontWeight.bold), - ), - const SizedBox( - height: 8, - ), - //TODO add API data - "INCOMING" == "INCOMING" - ? SizedBox( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - incoming, - textAlign: TextAlign.center, - style: Theme.of(context) - .textTheme - .displayMedium! - .copyWith( - fontSize: 20, - color: success2, - fontWeight: FontWeight.bold), - ), - const Icon( - Entypo.down_bold, - color: success2, - ) - ], + backgroundColor: Colors.black87, + child: BlocConsumer( + listener: (context, state) { + if (state is PassCheckLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is SettingSaved || + state is IncomingScanState || + state is OutGoingScanState || + state is ScanSuccess || + state is ScanFailed || + state is PassCheckErrorState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + if (state is ScanSuccess) { + Future.delayed(const Duration(seconds: 1), () async{ + await player?.play(AssetSource("success.mp3")); + }); + context.read().add(ScanQr(token: state.token)); + } + if (state is QRInvalid) { + Future.delayed(const Duration(seconds: 1), ()async { + await player?.play(AssetSource("invalid.mp3")); + }); + context.read().add(ScanQr(token: state.token)); + } + if (state is ScanFailed) { + Future.delayed(const Duration(seconds: 1), ()async { + await player?.play(AssetSource("fail.mp3")); + }); + + context.read().add(ScanQr(token: state.token)); + } + if (state is IncomingScanState) { + CoolAlert.show( + barrierDismissible: false, + context: context, + type: CoolAlertType.loading, + text: "Enter Temperature", + widget: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: FormBuilder( + key: formKey, + child: Column( + children: [ + const SizedBox( + height: 24, ), - ) - : SizedBox( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - //TODO add API data - "INCOMING", - textAlign: TextAlign.center, - style: Theme.of(context) - .textTheme - .displayMedium! - .copyWith( - fontSize: 20, - color: second, - fontWeight: FontWeight.bold), - ), - const Icon( - Entypo.up_bold, - color: second, - ) - ], + FormBuilderTextField( + keyboardType: TextInputType.number, + name: "temp", + decoration: + normalTextFieldStyle("Temperature", ""), + validator: numericRequired), + const SizedBox( + height: 12, ), - ) - ], - ), - ), - Container( - padding: const EdgeInsets.only(top: 12), - decoration: const BoxDecoration( - boxShadow: [ - BoxShadow( - color: Colors.black38, - blurRadius: 30, - offset: Offset(-5, 0), - ), - ], - color: Colors.white, - borderRadius: BorderRadius.only( - topLeft: Radius.circular(15), - topRight: Radius.circular(15))), + SizedBox( + height: 50, + width: double.infinity, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + child: const Text(submit), + onPressed: () { + if (formKey.currentState!.saveAndValidate()) { + double temperature = double.parse( + formKey.currentState!.value['temp']); + context.read().add( + PerformIncomingPostLog( + temp: temperature)); + Navigator.of(context).pop(); + } + }, + ), + ) + ], + )), + )); + } + if (state is OutGoingScanState) { + CoolAlert.show( + barrierDismissible: false, + context: context, + type: CoolAlertType.loading, + text: "Enter Destination", + widget: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: FormBuilder( + key: formKey, + child: Column( + children: [ + const SizedBox( + height: 24, + ), + FormBuilderTextField( + textCapitalization: + TextCapitalization.sentences, + name: "destination", + decoration: + normalTextFieldStyle("Destination", ""), + validator: FormBuilderValidators.required( + errorText: "This field is required")), + const SizedBox( + height: 12, + ), + SizedBox( + height: 50, + width: double.infinity, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + child: const Text(submit), + onPressed: () { + if (formKey.currentState!.saveAndValidate()) { + String destination = formKey + .currentState!.value['destination']; + context.read().add( + PerformOutgoingPostLog( + destination: destination)); + Navigator.of(context).pop(); + } + }, + ), + ) + ], + )), + )); + } + }, + builder: (context, state) { + if (state is SettingSaved) { + return SizedBox( + height: screenHeight * 100, width: double.infinity, child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: const [ - SelectedState( - //TODO add api data - - title: "Provincial Government of ADN", - subtitle: establishment, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const SizedBox( + height: 40, ), - Center( - child: SelectedState( - //TODO add api data - title: "Agusan Up", - subtitle: checkpointArea, + GestureDetector( + onTap: () {}, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + height: 160, + child: GestureDetector( + child: Image.asset('assets/pngs/qr-scan.png'), + onTap: () { + context + .read() + .add(ScanQr(token: state.token)); + })), + const SizedBox( + height: 8, + ), + Text( + tapToScanQR, + textAlign: TextAlign.center, + style: Theme.of(context) + .textTheme + .displayMedium! + .copyWith( + fontSize: 22, + color: primary, + fontWeight: FontWeight.bold), + ), + const SizedBox( + height: 8, + ), + //TODO add API data + state.io == "INCOMING" + ? SizedBox( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + incoming, + textAlign: TextAlign.center, + style: Theme.of(context) + .textTheme + .displayMedium! + .copyWith( + fontSize: 20, + color: success2, + fontWeight: FontWeight.bold), + ), + const Icon( + Entypo.down_bold, + color: success2, + ) + ], + ), + ) + : SizedBox( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "OUTGOING", + textAlign: TextAlign.center, + style: Theme.of(context) + .textTheme + .displayMedium! + .copyWith( + fontSize: 20, + color: second, + fontWeight: FontWeight.bold), + ), + const Icon( + Entypo.up_bold, + color: second, + ) + ], + ), + ) + ], ), ), - SelectedState( - //TODO add api data - title: "INCOMING", - subtitle: scanMode, - ), + Container( + padding: const EdgeInsets.only(top: 12), + decoration: const BoxDecoration( + boxShadow: [ + BoxShadow( + color: Colors.black38, + blurRadius: 30, + offset: Offset(-5, 0), + ), + ], + color: Colors.white, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(15), + topRight: Radius.circular(15))), + width: double.infinity, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SelectedState( + //TODO add api data + title: state.roleId == 41 || + state.roleId == 13 || + state.roleId == 17 || + state.roleId == 22 + ? state.assignedArea.stationName + : state.roleId == 7 + ? state.assignedArea.brgydesc + : state.roleId == 10 + ? state.assignedArea.purokdesc + : state.roleId == 16 + ? "Agency" + : "", + subtitle: state.roleId == 41 || + state.roleId == 13 || + state.roleId == 17 || + state.roleId == 22 + ? "Station" + : state.roleId == 7 + ? "Barangay" + : state.roleId == 10 + ? "Purok" + : state.roleId == 16 + ? "Agency" + : "", + ), + SelectedState( + //TODO add api data + + title: state.otherInputs ? "YES" : "NO", + subtitle: "Include other inputs", + ), + const SizedBox( + height: 54, + ), + ], + ), + ), + ) ], ), - ) - ], - ), - )); + ); + } + if (state is PassCheckErrorState) { + return SomethingWentWrong( + message: state.message, + onpressed: () { + context.read().add(ScanError()); + }); + } + return Container(); + }, + ), + ), + ); } } diff --git a/lib/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart b/lib/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart index 3610217..19df2b5 100644 --- a/lib/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart +++ b/lib/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart @@ -1,19 +1,28 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_svg/svg.dart'; import 'package:fluttericon/entypo_icons.dart'; import 'package:fluttericon/modern_pictograms_icons.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:unit2/bloc/role/pass_check/pass_check_bloc.dart'; +import 'package:unit2/bloc/user/user_bloc.dart'; +import 'package:unit2/screens/unit2/homepage.dart/components/dashboard.dart'; import 'package:unit2/screens/unit2/roles/qr_code_scanner.dart/components/custom_switch.dart'; -import 'package:unit2/test_data.dart'; +import 'package:unit2/screens/unit2/roles/qr_code_scanner.dart/scan.dart'; import 'package:unit2/utils/text_container.dart'; +import 'package:unit2/widgets/error_state.dart'; import '../../../../theme-data.dart/btn-style.dart'; import '../../../../theme-data.dart/colors.dart'; import '../../../../theme-data.dart/form-style.dart'; import '../../../../utils/global.dart'; class QRCodeScannerSettings extends StatefulWidget { - const QRCodeScannerSettings({super.key}); + final int roleId; + final int userId; + const QRCodeScannerSettings({super.key, required this.roleId, required this.userId}); @override State createState() => _QRCodeScannerSettingsState(); @@ -25,6 +34,9 @@ class _QRCodeScannerSettingsState extends State { String selectedLevel = ''; String selectedEstablishment = ''; String selectedArea = ''; + dynamic assignedArea; + int? checkerId; + String? token; final _formKey = GlobalKey(); @override Widget build(BuildContext context) { @@ -35,171 +47,584 @@ class _QRCodeScannerSettingsState extends State { centerTitle: true, backgroundColor: primary, ), - body: SingleChildScrollView( - child: Container( - height: screenHeight * .84, - padding: const EdgeInsets.symmetric(horizontal: 30,vertical: 10), - child: FormBuilder( - key: _formKey, - child: Column( - children: [ - const SizedBox( - height:24, - ), - SvgPicture.asset( - 'assets/svgs/switch.svg', - height: blockSizeVertical * 14, - allowDrawingOutsideViewBox: true, - ), - ListTile( - title: Text( - setQRScannerSettings, - style: Theme.of(context) - .textTheme - .titleLarge! - .copyWith(color: third), - textAlign: TextAlign.center, - ), - ), - Text(includeOtherInputs, - style: Theme.of(context).textTheme.titleMedium), - Text( - includeOtherInputsSubTitle, - style: Theme.of(context).textTheme.bodySmall, - ), - SizedBox( - child: FittedBox( - child: CostumToggleSwitch( - activeBGColors: [ - Colors.green[800]!, - Colors.red[800]! - ], - initialLabelIndex: _includeOtherInputs ? 0 : 1, - icons: const [Entypo.check, ModernPictograms.cancel], - labels: const ['YES', 'NO'], - onToggle: (value) { - value == 0 - ? _includeOtherInputs = true - : _includeOtherInputs = false; - }, - ), - ), - ), - // Incoming or outgoing - Text(incomingORoutgoing, - style: Theme.of(context).textTheme.titleMedium), - Text( - incomingORoutgoingSubTitle, - style: Theme.of(context).textTheme.bodySmall, - ), - FittedBox( - child: CostumToggleSwitch( - activeBGColors: [ Colors.red[800]!,Colors.green[800]!], - initialLabelIndex: scanMode == 'INCOMING' ? 0 : 1, - icons: const [ - Entypo.down_bold, - Entypo.up_bold, - ], - labels: const ['INCOMING', 'OUTGOING'], - onToggle: (value) { - value == 0 - ? scanMode = 'INCOMING' - : scanMode = 'OUTGOING'; - }, - ), - ), - const SizedBox( - height: 10, - ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + indicatorWidget: const SpinKitFadingCircle( + color: Colors.white, + ), + backgroundColor: Colors.black87, + child: BlocBuilder( + builder: (context, state) { + if (state is UserLoggedIn) { + token = state.userData!.user!.login!.token; + checkerId = state.userData!.user!.login!.user!.id; + return BlocConsumer( + listener: (context, state) { + if (state is PassCheckLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is AssignAreaLoaded || + state is PassCheckErrorState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + }, + builder: (context, state) { + if (state is AssignAreaLoaded) { + return Container( + height: screenHeight * .90, + padding: const EdgeInsets.symmetric( + horizontal: 42, vertical: 10), + child: FormBuilder( + key: _formKey, + child: Column( + children: [ + Flexible( + child: ListView( + children: [ + const SizedBox( + height: 32, + ), + SvgPicture.asset( + 'assets/svgs/switch.svg', + height: blockSizeVertical * 14, + allowDrawingOutsideViewBox: true, + ), + ListTile( + title: Text( + setQRScannerSettings, + style: Theme.of(context) + .textTheme + .titleLarge! + .copyWith(color: third), + textAlign: TextAlign.center, + ), + ), + Text(includeOtherInputs, + style: Theme.of(context) + .textTheme + .titleMedium), + Text( + includeOtherInputsSubTitle, + style: Theme.of(context) + .textTheme + .bodySmall, + ), + SizedBox( + child: FittedBox( + child: CostumToggleSwitch( + activeBGColors: [ + Colors.green[800]!, + Colors.red[800]! + ], + initialLabelIndex: + _includeOtherInputs ? 0 : 1, + icons: const [ + Entypo.check, + ModernPictograms.cancel + ], + labels: const ['YES', 'NO'], + onToggle: (value) { + value == 0 + ? _includeOtherInputs = true + : _includeOtherInputs = false; + }, + ), + ), + ), + // Incoming or outgoing + Text(incomingORoutgoing, + style: Theme.of(context) + .textTheme + .titleMedium), + Text( + incomingORoutgoingSubTitle, + style: Theme.of(context) + .textTheme + .bodySmall, + ), + FittedBox( + child: CostumToggleSwitch( + activeBGColors: [ + Colors.red[800]!, + Colors.green[800]! + ], + initialLabelIndex: + scanMode == 'INCOMING' ? 0 : 1, + icons: const [ + Entypo.down_bold, + Entypo.up_bold, + ], + labels: const [ + 'INCOMING', + 'OUTGOING' + ], + onToggle: (value) { + value == 0 + ? scanMode = 'INCOMING' + : scanMode = 'OUTGOING'; + }, + ), + ), + const SizedBox( + height: 24, + ), - //SELECT LEVEL - - FormBuilderDropdown( - name: 'level', - validator: FormBuilderValidators.required( - errorText: fieldIsRequired), - decoration: normalTextFieldStyle(selectLevel, "level"), - items: levels - .map((level) => DropdownMenuItem( - value: level, - child: Text(level), - )) - .toList(), - // value: selectedLevel, - onChanged: (value) async { - selectedLevel = value!; - }, - ), - - const SizedBox( - height: 8, - ), - - FormBuilderDropdown( - name: 'establishment', - decoration: normalTextFieldStyle( - selectedEstablishment, "establishments"), - isExpanded: true, - validator: FormBuilderValidators.required( - errorText: fieldIsRequired), - items: establishments - .map((est) => DropdownMenuItem( - value: est, - child: Text(est), - )) - .toList(), - // value: selectedArea, - onChanged: (value) async { - selectedArea = value!; - }), - const SizedBox( - height: 8, - ), - DropdownButtonFormField( - decoration: normalTextFieldStyle( - selectEstablishment, "establishments"), - isExpanded: true, - items: establishments - .map((est) => DropdownMenuItem( - value: est, - child: Text(est), - )) - .toList(), - // value: selectedArea, - onChanged: (value) async { - selectedArea = value!; - }), - const Expanded( - child: SizedBox(), - ), - SizedBox( - width: double.infinity, - height: 60, - child: ElevatedButton( - style: mainBtnStyle( - primary, Colors.transparent, Colors.white54), - child: const Text( - submit, - style: TextStyle(color: Colors.white), - ), - onPressed: () { - // if (_formKey.currentState.validate()) { - // _formKey.currentState.save(); - // BlocProvider.of(context) - // .add(UserWebLogin( - // password: password, - // username: username)); - // } - }, - ), - ), - - const SizedBox( - height: 8, - ), - ], - ), - ), + ////STATION + Container( + child: state.roleId == 41 + ? DropdownButtonFormField( + isExpanded: true, + validator: FormBuilderValidators + .required( + errorText: + fieldIsRequired), + decoration: + normalTextFieldStyle( + "station", "station"), + items: state.assignedArea + .map((station) { + if (station.motherStation) { + return DropdownMenuItem< + dynamic>( + enabled: false, + value: station, + child: Text( + station.stationName + .toUpperCase(), + style: + const TextStyle( + color: Colors + .grey), + ), + ); + } else { + return DropdownMenuItem< + dynamic>( + value: station, + child: Padding( + padding: + const EdgeInsets + .only( + left: 10), + child: Text(station + .stationName), + ), + ); + } + }).toList(), + // value: selectedLevel, + onChanged: (value) async { + assignedArea = value; + }, + ////BARANGAY + ) + : state.roleId == 7 + ? Column( + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + Text( + "Select Barangay", + textAlign: + TextAlign.start, + style: + Theme.of(context) + .textTheme + .titleMedium, + ), + const SizedBox( + height: 12, + ), + DropdownButtonFormField( + isExpanded: true, + decoration: + normalTextFieldStyle( + "Barangay", + "Barangay"), + items: state + .assignedArea + .map((barangay) { + return DropdownMenuItem< + dynamic>( + value: barangay, + child: Padding( + padding: + const EdgeInsets + .only( + left: + 5), + child: Text( + barangay + .brgydesc), + ), + ); + }).toList(), + onChanged: (value) { + assignedArea = + value; + }, + ), + ], + ) + : + ////PUROK + state.roleId == 10 + ? Column( + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + Text( + "Select Purok", + textAlign: + TextAlign + .start, + style: Theme.of( + context) + .textTheme + .titleMedium, + ), + const SizedBox( + height: 12, + ), + DropdownButtonFormField( + isExpanded: true, + decoration: + normalTextFieldStyle( + "Purok", + "Purok"), + items: state + .assignedArea + .map((purok) { + return DropdownMenuItem< + dynamic>( + value: purok, + child: + Padding( + padding: const EdgeInsets + .only( + left: + 5), + child: Text( + purok + .purokdesc), + ), + ); + }).toList(), + onChanged: + (value) { + assignedArea = + value; + }, + ), + ], + ) + : + ////Registration InCharge + state.roleId == 22 + ? Column( + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + Text( + "Select Station", + textAlign: + TextAlign + .start, + style: Theme.of( + context) + .textTheme + .titleMedium, + ), + const SizedBox( + height: 12, + ), + DropdownButtonFormField( + isExpanded: + true, + validator: FormBuilderValidators + .required( + errorText: + fieldIsRequired), + decoration: normalTextFieldStyle( + "station", + "station"), + items: state + .assignedArea + .map( + (station) { + if (station + .motherStation) { + return DropdownMenuItem< + dynamic>( + enabled: + false, + value: + station, + child: + Text( + station + .stationName + .toUpperCase(), + style: + const TextStyle(color: Colors.grey), + ), + ); + } else { + return DropdownMenuItem< + dynamic>( + value: + station, + child: + Padding( + padding: + const EdgeInsets.only(left: 5), + child: + Text(station.stationName), + ), + ); + } + }).toList(), + // value: selectedLevel, + onChanged: + (value) async { + assignedArea = + value; + }, + ), + ], + ) + : ////QR Code Scanner + state.roleId == 13 + ? Column( + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + Text( + "Select Station", + textAlign: + TextAlign + .start, + style: Theme.of( + context) + .textTheme + .titleMedium, + ), + const SizedBox( + height: + 12, + ), + DropdownButtonFormField( + isExpanded: + true, + validator: + FormBuilderValidators.required( + errorText: fieldIsRequired), + decoration: normalTextFieldStyle( + "station", + "station"), + items: state + .assignedArea + .map( + (station) { + if (station + .motherStation) { + return DropdownMenuItem< + dynamic>( + enabled: + false, + value: + station, + child: + Text( + station.stationName.toUpperCase(), + style: const TextStyle(color: Colors.grey), + ), + ); + } else { + return DropdownMenuItem< + dynamic>( + value: + station, + child: + Padding( + padding: const EdgeInsets.only(left: 5), + child: Text(station.stationName), + ), + ); + } + }).toList(), + // value: selectedLevel, + onChanged: + (value) async { + assignedArea = + value; + }, + ), + ], + ) + : + ////Establishment Point-Person + state.roleId == 16 + ? Column( + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + Text( + "Select Agency", + textAlign: + TextAlign.start, + style: Theme.of(context) + .textTheme + .titleMedium, + ), + const SizedBox( + height: + 12, + ), + DropdownButtonFormField( + isExpanded: + true, + validator: + FormBuilderValidators.required(errorText: fieldIsRequired), + decoration: normalTextFieldStyle( + "Agency", + "Agency"), + items: state + .assignedArea + .map((agency) { + return DropdownMenuItem( + value: agency, + child: Padding( + padding: const EdgeInsets.only(left: 5), + child: Text(agency.area.name), + ), + ); + }).toList(), + // value: selectedLevel, + onChanged: + (value) async { + assignedArea = + value; + }, + ), + ], + ) + : ////Office Branch Chief + state.roleId == + 17 + ? Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + "Select Station", + textAlign: TextAlign.start, + style: Theme.of(context).textTheme.titleMedium, + ), + const SizedBox( + height: 12, + ), + DropdownButtonFormField( + isExpanded: true, + validator: FormBuilderValidators.required(errorText: fieldIsRequired), + decoration: normalTextFieldStyle("station", "station"), + items: state.assignedArea.map((station) { + if (station.motherStation) { + return DropdownMenuItem( + enabled: false, + value: station, + child: Text( + station.stationName.toUpperCase(), + style: const TextStyle(color: Colors.grey), + ), + ); + } else { + return DropdownMenuItem( + value: station, + child: Padding( + padding: const EdgeInsets.only(left: 5), + child: Text(station.stationName), + ), + ); + } + }).toList(), + // value: selectedLevel, + onChanged: (value) async { + assignedArea = value; + }, + ), + ], + ) + : Container()) + ], + ), + ), + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: mainBtnStyle(primary, + Colors.transparent, Colors.white54), + child: const Text( + submit, + style: TextStyle(color: Colors.white), + ), + onPressed: () { + if (_formKey.currentState! + .saveAndValidate()) { + print(scanMode); + print(_includeOtherInputs); + print(checkerId); + print(assignedArea); + Navigator.push(context, + MaterialPageRoute(builder: + (BuildContext context) { + return BlocProvider< + PassCheckBloc>.value( + value: PassCheckBloc() + ..add(SetScannerSettings( + token: token!, + assignedArea: assignedArea, + checkerId: checkerId!, + entranceExit: scanMode, + includeOtherInputs: + _includeOtherInputs, + roleId: state.roleId)), + child: const QRCodeScanner(), + ); + })); + } + }, + ), + ), + const SizedBox( + height: 52, + ), + ], + ), + ), + ); + } + if (state is PassCheckErrorState) { + return SomethingWentWrong( + message: state.message, onpressed: () { + + context.read().add(GetPassCheckAreas(roleId: widget.roleId, userId: widget.userId)); + }); + } + return Container(); + }, + ); + } + return Container(); + }, ), )), ); diff --git a/lib/sevices/profile/address_service.dart b/lib/sevices/profile/address_service.dart index 5e0478b..cded751 100644 --- a/lib/sevices/profile/address_service.dart +++ b/lib/sevices/profile/address_service.dart @@ -1,7 +1,5 @@ import 'dart:convert'; -import 'package:unit2/theme-data.dart/colors.dart'; - import '../../model/profile/basic_information/adress.dart'; import '../../utils/request.dart'; import '../../utils/urls.dart'; @@ -123,7 +121,7 @@ class AddressService { try { http.Response response = await http.patch( Uri.parse( - 'http://${Url.instance.host()}${Url.instance.addressPath()}$profileId/'), + 'https://${Url.instance.host()}${Url.instance.addressPath()}$profileId/'), headers: headers, body: jsonEncode({ "id": address.id, diff --git a/lib/sevices/profile/profile_service.dart b/lib/sevices/profile/profile_service.dart index 1ed39fb..af057b1 100644 --- a/lib/sevices/profile/profile_service.dart +++ b/lib/sevices/profile/profile_service.dart @@ -106,7 +106,7 @@ class ProfileService { Map body = { "profile_id": profileId, "first_name": profileInfo.firstName, - "middle_name": profileInfo.middleName, + "middle_name": profileInfo.middleName!.isEmpty?null:profileInfo.middleName, "last_name": profileInfo.lastName, "name_extension": profileInfo.nameExtension, "birthdate": profileInfo.birthdate.toString(), diff --git a/lib/sevices/roles/pass_check_services.dart b/lib/sevices/roles/pass_check_services.dart new file mode 100644 index 0000000..3b076a6 --- /dev/null +++ b/lib/sevices/roles/pass_check_services.dart @@ -0,0 +1,265 @@ +import 'dart:convert'; +import 'dart:io'; +import 'package:audioplayers/audioplayers.dart'; +import 'package:flutter/services.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:unit2/model/location/barangay.dart'; +import 'package:unit2/model/roles/pass_check/agency_area_type.dart'; +import 'package:unit2/model/roles/pass_check/assign_role_area_type.dart'; +import 'package:unit2/model/roles/pass_check/barangay_assign_area.dart'; +import 'package:unit2/model/roles/pass_check/passer_info.dart'; +import 'package:unit2/utils/global.dart'; +import 'package:http/http.dart' as http; +import 'package:unit2/utils/request.dart'; +import '../../model/roles/pass_check/purok_assign_area.dart'; +import '../../model/roles/pass_check/station_assign_area.dart'; +import '../../utils/urls.dart'; + +class PassCheckServices { + static final PassCheckServices _instance = PassCheckServices(); + static PassCheckServices get instance => _instance; + + Future> getPassCheckArea( + {required int roleId, required int userId}) async { + String path = Url.instance.getAssignAreas(); + Map params = { + "assigned_role__role__id": roleId.toString(), + "assigned_role__user__id": userId.toString() + }; + List? statusResponse; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientSecret + }; + try { + http.Response response = await Request.instance + .getRequest(param: params, headers: headers, path: path); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + AssignRoleAreaType assignRoleAreaType = AssignRoleAreaType.fromJson( + data['data'][0]['assigned_role_area_type']); + ////station + if (assignRoleAreaType.areaTypeName.toLowerCase() == "station") { + List assignedArea = []; + data['data'][0]['assigned_area'].forEach((element) { + StationAssignArea stationAssignArea = + StationAssignArea.fromJson(element); + ChildStationInfo headStation = ChildStationInfo( + id: stationAssignArea.area!.id, + stationName: stationAssignArea.area!.stationName, + acroym: stationAssignArea.area!.acronym, + motherStation: true); + ChildStationInfo childStationInfo = ChildStationInfo( + id: stationAssignArea.area!.id, + stationName: stationAssignArea.area!.stationName, + acroym: stationAssignArea.area!.acronym, + motherStation: false); + assignedArea.add(headStation); + assignedArea.add(childStationInfo); + if (stationAssignArea.area?.childStationInfo != null) { + for (var element in stationAssignArea.area!.childStationInfo!) { + ChildStationInfo newStationInfo = ChildStationInfo( + id: element.id, + stationName: element.stationName, + acroym: element.acroym, + motherStation: false); + assignedArea.add(newStationInfo); + } + } + }); + + statusResponse = assignedArea; + } + ////registration in-chage + if (assignRoleAreaType.areaTypeName.toLowerCase() == + "registration in-charge") { + List assignedArea = []; + data['data'][0]['assigned_area'].forEach((element) { + StationAssignArea stationAssignArea = + StationAssignArea.fromJson(element); + ChildStationInfo headStation = ChildStationInfo( + id: stationAssignArea.area!.id, + stationName: stationAssignArea.area!.stationName, + acroym: stationAssignArea.area!.acronym, + motherStation: true); + ChildStationInfo childStationInfo = ChildStationInfo( + id: stationAssignArea.area!.id, + stationName: stationAssignArea.area!.stationName, + acroym: stationAssignArea.area!.acronym, + motherStation: false); + assignedArea.add(headStation); + assignedArea.add(childStationInfo); + if (stationAssignArea.area?.childStationInfo != null) { + for (var element in stationAssignArea.area!.childStationInfo!) { + ChildStationInfo newStationInfo = ChildStationInfo( + id: element.id, + stationName: element.stationName, + acroym: element.acroym, + motherStation: false); + assignedArea.add(newStationInfo); + } + } + }); + statusResponse = assignedArea; + } + ////Barangay + if (assignRoleAreaType.areaTypeName.toLowerCase() == "baranggay") { + List assignedArea = []; + data['data'][0]['assigned_area'].forEach((var element) { + BaragayAssignArea baragayAssignArea = + BaragayAssignArea.fromJson(element['area']); + assignedArea.add(baragayAssignArea); + }); + statusResponse = assignedArea; + } + ////PUROK + if (assignRoleAreaType.areaTypeName.toLowerCase() == 'purok') { + List assignedArea = []; + data['data'][0]['assigned_area'].forEach((var element) { + Purok purok = Purok.fromJson(element['area']); + assignedArea.add(purok); + }); + statusResponse = assignedArea; + } + ////AGENCY + if (assignRoleAreaType.areaTypeName.toLowerCase() == 'agency') { + List assignedArea = []; + data['data'][0]['assigned_area'].forEach((var element) { + AgencyAssignedArea agencyAssignedArea = + AgencyAssignedArea.fromJson(element); + assignedArea.add(agencyAssignedArea); + }); + statusResponse = assignedArea; + } + } + } catch (e) { + throw e.toString(); + } + return statusResponse!; + } + + Future getPasserInfo( + {required String uuid, required String token}) async { + PasserInfo? passerInfo; + String path = Url.instance.getPasserInfo(); + String authtoken = "Token $token"; + Map params = {"uuid": uuid}; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authtoken + }; + try { + http.Response response = await Request.instance + .getRequest(param: params, path: path, headers: headers); + if (response.statusCode == 200) { + Map body = jsonDecode(response.body); + passerInfo = PasserInfo.fromJson(body['data'][0]); + } + } catch (e) { + throw (e.toString()); + } + return passerInfo; + } + + Future performPostLogs( + {required String passerId, + required int chekerId, + required String io, + required bool otherInputs, + String? destination, + double? temp, + int? stationId, + String? cpId, + required int roleid}) async { + String path = Url.instance.postLogs(); + bool success; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientSecret + }; + Map body; + if (otherInputs) { + if (roleid == 41 || roleid == 13 || roleid == 17 || roleid == 22) { + if (io == "i") { + print("1"); + body = { + "station_id": stationId, + "temperature": temp, + "passer": passerId, + "checkedby_user_id": chekerId, + "io": io + }; + } else { + print("2"); + body = { + "station_id": stationId, + "destination": destination, + "passer": passerId, + "checkedby_user_id": chekerId, + "io": io + }; + } + } else { + print("3"); + if (io == "i") { + body = { + "cp_id": cpId, + "temperature": temp, + "passer": passerId, + "checkedby_user_id": chekerId, + "io": io + }; + } else { + print("4"); + body = { + "cp_id": cpId, + "destination": destination, + "passer": passerId, + "checkedby_user_id": chekerId, + "io": io + }; + } + } + } else { + print("5"); + if (roleid == 41 || roleid == 13 || roleid == 17 || roleid == 22) { + body = { + "station_id": stationId, + "passer": passerId, + "checkedby_user_id": chekerId, + "io": io + }; + } else { + print("6"); + body = { + "cp_id": cpId, + "temperature": temp, + "passer": passerId, + "checkedby_user_id": chekerId, + "io": io + }; + } + } + try { + http.Response response = await Request.instance + .postRequest(path: path, headers: headers, body: body, param: {}); + if (response.statusCode == 201) { + success = true; + } else { + success = false; + } + } catch (e) { + throw e.toString(); + } + return success; + } +} + +Future setFailedAudio(AudioPlayer audioPlayer) async { + AudioCache player = AudioCache(); + + final url = await player.load("ScanFailed.mp3"); + audioPlayer.play(AssetSource(url.path)); +} diff --git a/lib/utils/app_router.dart b/lib/utils/app_router.dart index efc535b..3bf9c9b 100644 --- a/lib/utils/app_router.dart +++ b/lib/utils/app_router.dart @@ -1,8 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/role/pass_check/pass_check_bloc.dart'; import 'package:unit2/bloc/sos/sos_bloc.dart'; import 'package:unit2/screens/sos/index.dart'; +import 'package:unit2/screens/unit2/homepage.dart/components/dashboard.dart'; import 'package:unit2/screens/unit2/homepage.dart/components/menu.dart'; import 'package:unit2/screens/unit2/login/login.dart'; import 'package:unit2/utils/global_context.dart'; @@ -47,15 +49,19 @@ class AppRouter { }); case '/sos': return MaterialPageRoute(builder: (BuildContext context) { - return BlocProvider( + return BlocProvider( create: (_) => SosBloc()..add(LoadUserLocation()), child: const SosScreen(), ); }); - case '/pass-check': - return MaterialPageRoute(builder: (BuildContext context){ - return const QRCodeScannerSettings(); - }); + case '/pass-check': + PassCheckArguments arguments = routeSettings.arguments as PassCheckArguments; + return MaterialPageRoute(builder: (BuildContext context) { + return BlocProvider( + create: (context) => PassCheckBloc()..add(GetPassCheckAreas(roleId: arguments.roleId, userId: arguments.userId)), + child: QRCodeScannerSettings(roleId: arguments.roleId, userId: arguments.userId,), + ); + }); default: return MaterialPageRoute(builder: (context) { return Container(); diff --git a/lib/utils/global.dart b/lib/utils/global.dart index a58ef4a..0b427f7 100644 --- a/lib/utils/global.dart +++ b/lib/utils/global.dart @@ -9,6 +9,8 @@ double safeAreaVertical = 0; double safeBlockHorizontal = 0; double safeBlockVertical = 0; +const xClientKey = "unitK3CQaXiWlPReDsBzmmwBZPd9Re1z"; +const xClientSecret = "unitcYqAN7GGalyz"; diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index 64c4b21..5dea2f3 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -5,10 +5,10 @@ class Url { String host() { // return '192.168.10.183:3000'; - // return 'agusandelnorte.gov.ph'; + return 'agusandelnorte.gov.ph'; // return "192.168.10.219:3000"; // return "192.168.10.241"; - return "192.168.10.221:3004"; + // return "192.168.10.221:3004"; // return "playweb.agusandelnorte.gov.ph"; // return 'devapi.agusandelnorte.gov.ph:3004'; } @@ -190,6 +190,20 @@ String getGenders(){ return "/api/profile_app/gender/"; } +/////ROLES +// pass check +String getAssignAreas(){ + return "/api/account/auth/assigned_role_area/"; +} + +String getPasserInfo(){ + return "/api/profile_app/person_basicinfo/"; +} + +String postLogs(){ + return "/api/unit2_app/monitoring/pass_check/"; +} + //// location utils path String getCounties(){ diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 4b37fe9..41d8796 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,10 +6,14 @@ #include "generated_plugin_registrant.h" +#include #include #include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin"); + audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar); g_autoptr(FlPluginRegistrar) modal_progress_hud_nsn_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "ModalProgressHudNsnPlugin"); modal_progress_hud_nsn_plugin_register_with_registrar(modal_progress_hud_nsn_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index f64d1cc..64b5f1f 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + audioplayers_linux modal_progress_hud_nsn platform_device_id_linux ) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 7a1cdf6..fa11536 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,22 +5,30 @@ import FlutterMacOS import Foundation +import assets_audio_player +import assets_audio_player_web +import audioplayers_darwin import location import modal_progress_hud_nsn import package_info_plus import path_provider_foundation import platform_device_id import platform_device_id_macos +import rive_common import shared_preferences_foundation import sqflite func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + AssetsAudioPlayerPlugin.register(with: registry.registrar(forPlugin: "AssetsAudioPlayerPlugin")) + AssetsAudioPlayerWebPlugin.register(with: registry.registrar(forPlugin: "AssetsAudioPlayerWebPlugin")) + AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin")) LocationPlugin.register(with: registry.registrar(forPlugin: "LocationPlugin")) ModalProgressHudNsnPlugin.register(with: registry.registrar(forPlugin: "ModalProgressHudNsnPlugin")) FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PlatformDeviceIdMacosPlugin.register(with: registry.registrar(forPlugin: "PlatformDeviceIdMacosPlugin")) PlatformDeviceIdMacosPlugin.register(with: registry.registrar(forPlugin: "PlatformDeviceIdMacosPlugin")) + RivePlugin.register(with: registry.registrar(forPlugin: "RivePlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) } diff --git a/pubspec.lock b/pubspec.lock index b586442..43d40ba 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -45,10 +45,26 @@ packages: dependency: transitive description: name: args - sha256: "4cab82a83ffef80b262ddedf47a0a8e56ee6fbf7fe21e6e768b02792034dd440" + sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.4.2" + assets_audio_player: + dependency: "direct main" + description: + name: assets_audio_player + sha256: dcea8cd9c11cd9c34586f2446bfcdf099362159c56f97517ba941ac151974ea9 + url: "https://pub.dev" + source: hosted + version: "3.0.6" + assets_audio_player_web: + dependency: transitive + description: + name: assets_audio_player_web + sha256: "4575ec40033d818ff022d48f7d46e24c01bc632bd1cd36a4f8b58b38e9aa4a81" + url: "https://pub.dev" + source: hosted + version: "3.0.6" async: dependency: transitive description: @@ -57,6 +73,62 @@ packages: url: "https://pub.dev" source: hosted version: "2.10.0" + audioplayers: + dependency: "direct main" + description: + name: audioplayers + sha256: "61583554386721772f9309f509e17712865b38565a903c761f96b1115a979282" + url: "https://pub.dev" + source: hosted + version: "4.1.0" + audioplayers_android: + dependency: transitive + description: + name: audioplayers_android + sha256: dbdc9b7f2aa2440314c638aa55aadd45c7705e8340d5eddf2e3fb8da32d4ae2c + url: "https://pub.dev" + source: hosted + version: "3.0.2" + audioplayers_darwin: + dependency: transitive + description: + name: audioplayers_darwin + sha256: "6aea96df1d12f7ad5a71d88c6d1b22a216211a9564219920124c16768e456e9d" + url: "https://pub.dev" + source: hosted + version: "4.1.0" + audioplayers_linux: + dependency: transitive + description: + name: audioplayers_linux + sha256: "396b62ac62c92dd26c3bc5106583747f57a8b325ebd2b41e5576f840cfc61338" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + audioplayers_platform_interface: + dependency: transitive + description: + name: audioplayers_platform_interface + sha256: f7daaed4659143094151ecf6bacd927d29ab8acffba98c110c59f0b81ae51143 + url: "https://pub.dev" + source: hosted + version: "5.0.1" + audioplayers_web: + dependency: transitive + description: + name: audioplayers_web + sha256: ec84fd46eed1577148ed4113f5998a36a18da4fce7170c37ce3e21b631393339 + url: "https://pub.dev" + source: hosted + version: "3.1.0" + audioplayers_windows: + dependency: transitive + description: + name: audioplayers_windows + sha256: "1d3aaac98a192b8488167711ba1e67d8b96333e8d0572ede4e2912e5bbce69a3" + url: "https://pub.dev" + source: hosted + version: "2.0.2" auto_size_text: dependency: "direct main" description: @@ -69,10 +141,10 @@ packages: dependency: "direct main" description: name: awesome_dialog - sha256: ac08268b991f228fc6b8880b68ec030d2941bcc8df8b6a6551fb79f2bd36b7da + sha256: "7da175ea284fa5da0a4d0cbdfe835c5b71d30c7b38c1770c0f27f48272ff5a08" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.1.0" azlistview: dependency: "direct main" description: @@ -85,10 +157,10 @@ packages: dependency: "direct main" description: name: badges - sha256: d33d4e07197d6e61ddc940640f2b4dc303d3c80887011344940e24d7522f8d26 + sha256: "6e7f3ec561ec08f47f912cfe349d4a1707afdc8dda271e17b046aa6d42c89e77" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.1.1" barcode_scan2: dependency: "direct main" description: @@ -101,10 +173,10 @@ packages: dependency: transitive description: name: bloc - sha256: "658a5ae59edcf1e58aac98b000a71c762ad8f46f1394c34a52050cafb3e11a80" + sha256: "3820f15f502372d979121de1f6b97bfcf1630ebff8fe1d52fb2b0bfa49be5b49" url: "https://pub.dev" source: hosted - version: "8.1.1" + version: "8.1.2" boolean_selector: dependency: transitive description: @@ -157,10 +229,10 @@ packages: dependency: transitive description: name: build_runner_core - sha256: "14febe0f5bac5ae474117a36099b4de6f1dbc52df6c5e55534b3da9591bf4292" + sha256: "0671ad4162ed510b70d0eb4ad6354c249f8429cab4ae7a4cec86bbc2886eb76e" url: "https://pub.dev" source: hosted - version: "7.2.7" + version: "7.2.7+1" built_collection: dependency: transitive description: @@ -173,10 +245,10 @@ packages: dependency: transitive description: name: built_value - sha256: "31b7c748fd4b9adf8d25d72a4c4a59ef119f12876cf414f94f8af5131d5fa2b0" + sha256: "598a2a682e2a7a90f08ba39c0aaa9374c5112340f0a2e275f61b59389543d166" url: "https://pub.dev" source: hosted - version: "8.4.4" + version: "8.6.1" cached_network_image: dependency: "direct main" description: @@ -213,10 +285,10 @@ packages: dependency: transitive description: name: checked_yaml - sha256: "3d1505d91afa809d177efd4eed5bb0eb65805097a1463abdd2add076effae311" + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "2.0.3" clock: dependency: transitive description: @@ -229,10 +301,10 @@ packages: dependency: transitive description: name: code_builder - sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe" + sha256: "4ad01d6e56db961d29661561effde45e519939fdaeb46c351275b182eac70189" url: "https://pub.dev" source: hosted - version: "4.4.0" + version: "4.5.0" collection: dependency: transitive description: @@ -269,10 +341,10 @@ packages: dependency: transitive description: name: crypto - sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.3" dart_style: dependency: transitive description: @@ -365,10 +437,10 @@ packages: dependency: transitive description: name: ffi - sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 + sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99 url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" file: dependency: transitive description: @@ -418,10 +490,10 @@ packages: dependency: "direct main" description: name: flutter_bloc - sha256: "434951eea948dbe87f737b674281465f610b8259c16c097b8163ce138749a775" + sha256: e74efb89ee6945bcbce74a5b3a5a3376b088e5f21f55c263fc38cbdc6237faae url: "https://pub.dev" source: hosted - version: "8.1.2" + version: "8.1.3" flutter_blurhash: dependency: transitive description: @@ -471,10 +543,10 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: c224ac897bed083dabf11f238dd11a239809b446740be0c2044608c50029ffdf + sha256: "950e77c2bbe1692bc0874fc7fb491b96a4dc340457f4ea1641443d0a6c1ea360" url: "https://pub.dev" source: hosted - version: "2.0.9" + version: "2.0.15" flutter_progress_hud: dependency: "direct main" description: @@ -487,10 +559,10 @@ packages: dependency: "direct main" description: name: flutter_spinkit - sha256: "77a2117c0517ff909221f3160b8eb20052ab5216107581168af574ac1f05dff8" + sha256: b39c753e909d4796906c5696a14daf33639a76e017136c8d82bf3e620ce5bb8e url: "https://pub.dev" source: hosted - version: "5.1.0" + version: "5.2.0" flutter_svg: dependency: "direct main" description: @@ -529,18 +601,18 @@ packages: dependency: "direct main" description: name: fluttertoast - sha256: "2f9c4d3f4836421f7067a28f8939814597b27614e021da9d63e5d3fb6e212d25" + sha256: "474f7d506230897a3cd28c965ec21c5328ae5605fc9c400cd330e9e9d6ac175c" url: "https://pub.dev" source: hosted - version: "8.2.1" + version: "8.2.2" form_builder_validators: dependency: "direct main" description: name: form_builder_validators - sha256: d0a940d77231723fcb203ad6f5319ff30cd0c4412a6e74d29d826957b1f9afe0 + sha256: f2d90439c56345c23ad8d0c2912e4002cd02563d816f4387c9495051c10d3321 url: "https://pub.dev" source: hosted - version: "8.5.0" + version: "8.6.1" freezed_annotation: dependency: transitive description: @@ -561,10 +633,10 @@ packages: dependency: transitive description: name: glob - sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c" + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" globbing: dependency: transitive description: @@ -577,10 +649,10 @@ packages: dependency: transitive description: name: graphs - sha256: f9e130f3259f52d26f0cfc0e964513796dafed572fa52e45d2f8d6ca14db39b2 + sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.3.1" hive: dependency: "direct main" description: @@ -609,10 +681,10 @@ packages: dependency: transitive description: name: http - sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482" + sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" url: "https://pub.dev" source: hosted - version: "0.13.5" + version: "0.13.6" http_multi_server: dependency: transitive description: @@ -665,10 +737,10 @@ packages: dependency: transitive description: name: json_annotation - sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317 + sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 url: "https://pub.dev" source: hosted - version: "4.8.0" + version: "4.8.1" lints: dependency: transitive description: @@ -705,10 +777,10 @@ packages: dependency: transitive description: name: logging - sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d" + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.2.0" lottie: dependency: transitive description: @@ -801,10 +873,10 @@ packages: dependency: "direct main" description: name: package_info_plus - sha256: cbff87676c352d97116af6dbea05aa28c4d65eb0f6d5677a520c11a69ca9a24d + sha256: "10259b111176fba5c505b102e3a5b022b51dd97e30522e906d6922c745584745" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.2" package_info_plus_platform_interface: dependency: transitive description: @@ -841,34 +913,34 @@ packages: dependency: "direct main" description: name: path_provider - sha256: c7edf82217d4b2952b2129a61d3ad60f1075b9299e629e149a8d2e39c2e6aad4 + sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2" url: "https://pub.dev" source: hosted - version: "2.0.14" + version: "2.0.15" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "019f18c9c10ae370b08dce1f3e3b73bc9f58e7f087bb5e921f06529438ac0ae7" + sha256: "2cec049d282c7f13c594b4a73976b0b4f2d7a1838a6dd5aaf7bd9719196bee86" url: "https://pub.dev" source: hosted - version: "2.0.24" + version: "2.0.27" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "818b2dc38b0f178e0ea3f7cf3b28146faab11375985d815942a68eee11c2d0f7" + sha256: "1995d88ec2948dac43edf8fe58eb434d35d22a2940ecee1a9fefcd62beee6eb3" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.3" path_provider_linux: dependency: transitive description: name: path_provider_linux - sha256: "2ae08f2216225427e64ad224a24354221c2c7907e448e6e0e8b57b1eb9f10ad1" + sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57 url: "https://pub.dev" source: hosted - version: "2.1.10" + version: "2.1.11" path_provider_platform_interface: dependency: transitive description: @@ -881,10 +953,10 @@ packages: dependency: transitive description: name: path_provider_windows - sha256: f53720498d5a543f9607db4b0e997c4b5438884de25b0f73098cc2671a51b130 + sha256: "1cb68ba4cd3a795033de62ba1b7b4564dace301f952de6bfb3cd91b202b6ee96" url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.1.7" pedantic: dependency: transitive description: @@ -897,34 +969,34 @@ packages: dependency: "direct main" description: name: permission_handler - sha256: "33c6a1253d1f95fd06fa74b65b7ba907ae9811f9d5c1d3150e51417d04b8d6a8" + sha256: "1b6b3e73f0bcbc856548bbdfb1c33084a401c4f143e220629a9055233d76c331" url: "https://pub.dev" source: hosted - version: "10.2.0" + version: "10.3.0" permission_handler_android: dependency: transitive description: name: permission_handler_android - sha256: "8028362b40c4a45298f1cbfccd227c8dd6caf0e27088a69f2ba2ab15464159e2" + sha256: "8f6a95ccbca13766882f95d32684d7c9bfe6c45650c32bedba948ef1c6a4ddf7" url: "https://pub.dev" source: hosted - version: "10.2.0" + version: "10.2.3" permission_handler_apple: dependency: transitive description: name: permission_handler_apple - sha256: ee96ac32f5a8e6f80756e25b25b9f8e535816c8e6665a96b6d70681f8c4f7e85 + sha256: "08dcb6ce628ac0b257e429944b4c652c2a4e6af725bdf12b498daa2c6b2b1edb" url: "https://pub.dev" source: hosted - version: "9.0.8" + version: "9.1.0" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface - sha256: "68abbc472002b5e6dfce47fe9898c6b7d8328d58b5d2524f75e277c07a97eb84" + sha256: de20a5c3269229c1ae2e5a6b822f6cb59578b23e8255c93fbeebfc82116e6b11 url: "https://pub.dev" source: hosted - version: "3.9.0" + version: "3.10.0" permission_handler_windows: dependency: transitive description: @@ -1009,10 +1081,10 @@ packages: dependency: transitive description: name: pointycastle - sha256: c3120a968135aead39699267f4c74bc9a08e4e909e86bc1b0af5bfd78691123c + sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" url: "https://pub.dev" source: hosted - version: "3.7.2" + version: "3.7.3" pool: dependency: transitive description: @@ -1049,18 +1121,18 @@ packages: dependency: transitive description: name: pub_semver - sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17" + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.4" pubspec_parse: dependency: transitive description: name: pubspec_parse - sha256: ec85d7d55339d85f44ec2b682a82fea340071e8978257e5a43e69f79e98ef50c + sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.3" qr: dependency: transitive description: @@ -1081,10 +1153,18 @@ packages: dependency: transitive description: name: rive - sha256: "22e3755b75f4ea4492d2fecf4fc2acf1c8d0073df39781d290a20cbfe74c3760" + sha256: bb7a16bc6a88484fe3136890030b71776ffea368edd2a8368fe99d6430e4a802 url: "https://pub.dev" source: hosted - version: "0.9.1" + version: "0.11.2" + rive_common: + dependency: transitive + description: + name: rive_common + sha256: "5a0dbf689527c51ee5430608181d81460c9c45ab6cc3bd52dd9bbb3d8c4455b6" + url: "https://pub.dev" + source: hosted + version: "0.0.9" rxdart: dependency: transitive description: @@ -1113,34 +1193,34 @@ packages: dependency: "direct main" description: name: searchfield - sha256: deb363c95b9e64ea9ffd1a3b69926b0fe0344daedab872fc42014755a8199de9 + sha256: "62b6653d7194de5a7ea724124846b996d0d211cd3eeeec2e5ca84423650f66f8" url: "https://pub.dev" source: hosted - version: "0.7.5" + version: "0.7.8" shared_preferences: dependency: transitive description: name: shared_preferences - sha256: "858aaa72d8f61637d64e776aca82e1c67e6d9ee07979123c5d17115031c1b13b" + sha256: "396f85b8afc6865182610c0a2fc470853d56499f75f7499e2a73a9f0539d23d0" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.2" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "8304d8a1f7d21a429f91dee552792249362b68a331ac5c3c1caf370f658873f6" + sha256: "6478c6bbbecfe9aced34c483171e90d7c078f5883558b30ec3163cf18402c749" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.4" shared_preferences_foundation: dependency: transitive description: name: shared_preferences_foundation - sha256: cf2a42fb20148502022861f71698db12d937c7459345a1bdaa88fc91a91b3603 + sha256: e014107bb79d6d3297196f4f2d0db54b5d1f85b8ea8ff63b8e8b391a02700feb url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.2" shared_preferences_linux: dependency: transitive description: @@ -1177,18 +1257,18 @@ packages: dependency: transitive description: name: shelf - sha256: c24a96135a2ccd62c64b69315a14adc5c3419df63b4d7c05832a346fdb73682c + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.4.1" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - sha256: a988c0e8d8ffbdb8a28aa7ec8e449c260f3deb808781fe1284d22c5bba7156e8 + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.0.4" signature: dependency: "direct main" description: @@ -1201,10 +1281,10 @@ packages: dependency: "direct main" description: name: simple_chips_input - sha256: "522b2e715fe67f325693e003acfd09fc0b8ab25a2c0c87fb8e5ce5b23a8a2ec1" + sha256: "758c2439c74f9105ebb18407095c028e247631108195c38480ea3fb4967c8f6f" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.1.0" sky_engine: dependency: transitive description: flutter @@ -1238,18 +1318,18 @@ packages: dependency: transitive description: name: sqflite - sha256: "500d6fec583d2c021f2d25a056d96654f910662c64f836cd2063167b8f1fa758" + sha256: b4d6710e1200e96845747e37338ea8a819a12b51689a3bcf31eff0003b37a0b9 url: "https://pub.dev" source: hosted - version: "2.2.6" + version: "2.2.8+4" sqflite_common: dependency: transitive description: name: sqflite_common - sha256: "963dad8c4aa2f814ce7d2d5b1da2f36f31bd1a439d8f27e3dc189bb9d26bc684" + sha256: e77abf6ff961d69dfef41daccbb66b51e9983cdd5cb35bf30733598057401555 url: "https://pub.dev" source: hosted - version: "2.4.3" + version: "2.4.5" stack_trace: dependency: transitive description: @@ -1286,10 +1366,10 @@ packages: dependency: transitive description: name: synchronized - sha256: "33b31b6beb98100bf9add464a36a8dd03eb10c7a8cf15aeec535e9b054aaf04b" + sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.1.0" system_info2: dependency: "direct main" description: @@ -1326,18 +1406,18 @@ packages: dependency: "direct main" description: name: toggle_switch - sha256: "82c778c4bfe93af154a41e346ccd1d5b0cb6a50f8f187941412edd0a9bbf51ee" + sha256: "9e6af1f0c5a97d9de41109dc7b9e1b3bbe73417f89b10e0e44dc834fb493d4cb" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.0" typed_data: dependency: transitive description: name: typed_data - sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" uuid: dependency: transitive description: @@ -1366,18 +1446,18 @@ packages: dependency: transitive description: name: web_socket_channel - sha256: ca49c0bc209c687b887f30527fb6a9d80040b072cc2990f34b9bec3e7663101b + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.4.0" win32: dependency: transitive description: name: win32 - sha256: a6f0236dbda0f63aa9a25ad1ff9a9d8a4eaaa5012da0dc59d21afdb1dc361ca4 + sha256: "5a751eddf9db89b3e5f9d50c20ab8612296e4e8db69009788d6c8b060a84191c" url: "https://pub.dev" source: hosted - version: "3.1.4" + version: "4.1.4" xdg_directories: dependency: transitive description: @@ -1398,10 +1478,10 @@ packages: dependency: transitive description: name: yaml - sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370" + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.1.2" sdks: dart: ">2.19.0 <3.0.0" flutter: ">=3.7.0" diff --git a/pubspec.yaml b/pubspec.yaml index cc3557e..8bdae44 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -80,6 +80,9 @@ dependencies: platform_device_id: ^1.0.1 multi_dropdown: ^1.0.9 searchable_paginated_dropdown: ^1.2.0 + audioplayers: ^4.1.0 + assets_audio_player: ^3.0.6 + dev_dependencies: flutter_test: @@ -110,6 +113,7 @@ flutter: - assets/svgs/ - assets/pngs/ - assets/fonts/ + - assets/ # - images/a_dot_ham.jpeg # An image asset can refer to one or more resolution-specific "variants", see diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 458cc68..362613e 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,15 +6,21 @@ #include "generated_plugin_registrant.h" +#include #include #include #include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { + AudioplayersWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin")); ModalProgressHudNsnPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("ModalProgressHudNsnPlugin")); PermissionHandlerWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); PlatformDeviceIdWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("PlatformDeviceIdWindowsPlugin")); + RivePluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("RivePlugin")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 4c970ab..8c00e3e 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,9 +3,11 @@ # list(APPEND FLUTTER_PLUGIN_LIST + audioplayers_windows modal_progress_hud_nsn permission_handler_windows platform_device_id_windows + rive_common ) list(APPEND FLUTTER_FFI_PLUGIN_LIST From 62cb054c4cbf861be77f0c80a737b39fbc0cd9dc Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Fri, 28 Jul 2023 10:21:42 +0800 Subject: [PATCH 73/86] Finish Implementation of RBAC operations --- lib/bloc/rbac/rbac_bloc.dart | 45 + lib/bloc/rbac/rbac_event.dart | 29 + .../rbac_operations/agency/agency_bloc.dart | 47 + .../rbac_operations/agency/agency_event.dart | 16 + .../rbac_operations/agency/agency_state.dart | 33 + .../rbac_operations/module/module_bloc.dart | 84 ++ .../rbac_operations/module/module_event.dart | 48 + .../rbac_operations/module/module_state.dart | 36 + .../module_objects/module_objects_bloc.dart | 76 ++ .../module_objects/module_objects_event.dart | 26 + .../module_objects/module_objects_state.dart | 37 + .../rbac_operations/object/object_bloc.dart | 87 ++ .../rbac_operations/object/object_event.dart | 46 + .../rbac_operations/object/object_state.dart | 36 + .../operation/operation_bloc.dart | 84 ++ .../operation/operation_event.dart | 45 + .../operation/operation_state.dart | 34 + .../permission/permission_bloc.dart | 81 ++ .../permission/permission_event.dart | 25 + .../permission/permission_state.dart | 37 + .../rbac/rbac_operations/role/role_bloc.dart | 83 ++ .../rbac/rbac_operations/role/role_event.dart | 45 + .../rbac/rbac_operations/role/role_state.dart | 37 + .../role_extend/role_extend_bloc.dart | 75 ++ .../role_extend/role_extend_event.dart | 21 + .../role_extend/role_extend_state.dart | 34 + .../role_module/role_module_bloc.dart | 72 ++ .../role_module/role_module_event.dart | 25 + .../role_module/role_module_state.dart | 36 + .../roles_under/roles_under_bloc.dart | 67 + .../roles_under/roles_under_event.dart | 21 + .../roles_under/roles_under_state.dart | 34 + .../rbac_operations/station/station_bloc.dart | 47 + .../station/station_event.dart | 18 + .../station/station_state.dart | 32 + lib/bloc/rbac/rbac_state.dart | 36 + lib/bloc/role/pass_check/pass_check_bloc.dart | 8 +- .../role_assignment/role_assignment_bloc.dart | 96 ++ .../role_assignment_event.dart | 28 + .../role_assignment_state.dart | 37 + lib/model/login_data/user_info/module.dart | 12 +- lib/model/login_data/user_info/role.dart | 30 - .../primary-information.dart | 5 +- lib/model/rbac/assigned_role.dart | 130 ++ lib/model/rbac/new_permission.dart | 70 + lib/model/rbac/permission.dart | 79 ++ lib/model/rbac/rbac.dart | 94 ++ lib/model/rbac/rbac_rbac.dart | 122 ++ lib/model/rbac/rbac_station.dart | 164 +++ lib/model/rbac/role_extend.dart | 37 + lib/model/rbac/role_module.dart | 124 ++ lib/model/rbac/role_under.dart | 37 + .../roles/pass_check/station_assign_area.dart | 14 +- .../edit_basic_info_modal.dart | 1 + .../profile/components/education_screen.dart | 79 +- .../learning_development/add_modal.dart | 1 - .../components/work_history/add_modal.dart | 2 +- lib/screens/profile/profile.dart | 1 - .../profile/shared/add_for_empty_search.dart | 15 +- .../superadmin/agency.dart/agency_screen.dart | 267 ++++ .../superadmin/module/module_screen.dart | 373 ++++++ .../module_objects/module_objects_screen.dart | 298 +++++ .../superadmin/object/object_screen.dart | 376 ++++++ .../operation/operation_screen.dart | 375 ++++++ .../permission/permission_screen.dart | 283 ++++ lib/screens/superadmin/role/role_screen.dart | 373 ++++++ .../superadmin/role/shared_pop_up_menu.dart | 21 + .../role_assignment_screen.dart | 289 +++++ .../role_extend/role_extend_screen.dart | 296 +++++ .../role_module/role_module_scree.dart | 289 +++++ .../roles_under/roles_under_screen.dart | 294 +++++ .../superadmin/stations/stations_screen.dart | 289 +++++ lib/screens/unit2/basic-info/basic-info.dart | 30 +- .../unit2/basic-info/components/qr_image.dart | 25 + .../homepage.dart/components/dashboard.dart | 272 ---- .../components/dashboard/dashboard.dart | 479 +++++++ .../dashboard/dashboard_icon_generator.dart | 66 + .../dashboard/shared_card_label.dart | 61 + .../dashboard/superadmin_expanded_menu.dart | 317 +++++ .../unit2/homepage.dart/module-screen.dart | 126 +- lib/screens/unit2/login/login.dart | 20 +- .../qr_code_scanner.dart/settings_screen.dart | 1134 ++++++++--------- lib/screens/unit2/roles/rbac/add_rbac.dart | 82 ++ lib/screens/unit2/roles/rbac/rbac.dart | 746 +++++++++++ lib/screens/utils/formatters.dart | 1 + lib/sevices/roles/pass_check_services.dart | 8 +- .../rbac_operations/agency_services.dart | 73 ++ .../module_objects_services.dart | 98 ++ .../rbac_operations/module_services.dart | 146 +++ .../rbac_operations/object_services.dart | 144 +++ .../rbac_operations/operation_services.dart | 144 +++ .../rbac_operations/permission_service.dart | 100 ++ .../role_assignment_services.dart | 137 ++ .../rbac_operations/role_extend_services.dart | 96 ++ .../rbac_operations/role_module_services.dart | 100 ++ .../roles/rbac_operations/role_services.dart | 146 +++ .../rbac_operations/roles_under_services.dart | 96 ++ .../rbac_operations/station_services.dart | 39 + lib/sevices/roles/rbac_services.dart | 218 ++++ lib/utils/app_router.dart | 10 +- lib/utils/urls.dart | 377 ++++-- macos/Podfile.lock | 28 +- pubspec.lock | 32 + pubspec.yaml | 4 + 104 files changed, 10831 insertions(+), 1168 deletions(-) create mode 100644 lib/bloc/rbac/rbac_bloc.dart create mode 100644 lib/bloc/rbac/rbac_event.dart create mode 100644 lib/bloc/rbac/rbac_operations/agency/agency_bloc.dart create mode 100644 lib/bloc/rbac/rbac_operations/agency/agency_event.dart create mode 100644 lib/bloc/rbac/rbac_operations/agency/agency_state.dart create mode 100644 lib/bloc/rbac/rbac_operations/module/module_bloc.dart create mode 100644 lib/bloc/rbac/rbac_operations/module/module_event.dart create mode 100644 lib/bloc/rbac/rbac_operations/module/module_state.dart create mode 100644 lib/bloc/rbac/rbac_operations/module_objects/module_objects_bloc.dart create mode 100644 lib/bloc/rbac/rbac_operations/module_objects/module_objects_event.dart create mode 100644 lib/bloc/rbac/rbac_operations/module_objects/module_objects_state.dart create mode 100644 lib/bloc/rbac/rbac_operations/object/object_bloc.dart create mode 100644 lib/bloc/rbac/rbac_operations/object/object_event.dart create mode 100644 lib/bloc/rbac/rbac_operations/object/object_state.dart create mode 100644 lib/bloc/rbac/rbac_operations/operation/operation_bloc.dart create mode 100644 lib/bloc/rbac/rbac_operations/operation/operation_event.dart create mode 100644 lib/bloc/rbac/rbac_operations/operation/operation_state.dart create mode 100644 lib/bloc/rbac/rbac_operations/permission/permission_bloc.dart create mode 100644 lib/bloc/rbac/rbac_operations/permission/permission_event.dart create mode 100644 lib/bloc/rbac/rbac_operations/permission/permission_state.dart create mode 100644 lib/bloc/rbac/rbac_operations/role/role_bloc.dart create mode 100644 lib/bloc/rbac/rbac_operations/role/role_event.dart create mode 100644 lib/bloc/rbac/rbac_operations/role/role_state.dart create mode 100644 lib/bloc/rbac/rbac_operations/role_extend/role_extend_bloc.dart create mode 100644 lib/bloc/rbac/rbac_operations/role_extend/role_extend_event.dart create mode 100644 lib/bloc/rbac/rbac_operations/role_extend/role_extend_state.dart create mode 100644 lib/bloc/rbac/rbac_operations/role_module/role_module_bloc.dart create mode 100644 lib/bloc/rbac/rbac_operations/role_module/role_module_event.dart create mode 100644 lib/bloc/rbac/rbac_operations/role_module/role_module_state.dart create mode 100644 lib/bloc/rbac/rbac_operations/roles_under/roles_under_bloc.dart create mode 100644 lib/bloc/rbac/rbac_operations/roles_under/roles_under_event.dart create mode 100644 lib/bloc/rbac/rbac_operations/roles_under/roles_under_state.dart create mode 100644 lib/bloc/rbac/rbac_operations/station/station_bloc.dart create mode 100644 lib/bloc/rbac/rbac_operations/station/station_event.dart create mode 100644 lib/bloc/rbac/rbac_operations/station/station_state.dart create mode 100644 lib/bloc/rbac/rbac_state.dart create mode 100644 lib/bloc/role_assignment/role_assignment_bloc.dart create mode 100644 lib/bloc/role_assignment/role_assignment_event.dart create mode 100644 lib/bloc/role_assignment/role_assignment_state.dart create mode 100644 lib/model/rbac/assigned_role.dart create mode 100644 lib/model/rbac/new_permission.dart create mode 100644 lib/model/rbac/permission.dart create mode 100644 lib/model/rbac/rbac.dart create mode 100644 lib/model/rbac/rbac_rbac.dart create mode 100644 lib/model/rbac/rbac_station.dart create mode 100644 lib/model/rbac/role_extend.dart create mode 100644 lib/model/rbac/role_module.dart create mode 100644 lib/model/rbac/role_under.dart create mode 100644 lib/screens/superadmin/agency.dart/agency_screen.dart create mode 100644 lib/screens/superadmin/module/module_screen.dart create mode 100644 lib/screens/superadmin/module_objects/module_objects_screen.dart create mode 100644 lib/screens/superadmin/object/object_screen.dart create mode 100644 lib/screens/superadmin/operation/operation_screen.dart create mode 100644 lib/screens/superadmin/permission/permission_screen.dart create mode 100644 lib/screens/superadmin/role/role_screen.dart create mode 100644 lib/screens/superadmin/role/shared_pop_up_menu.dart create mode 100644 lib/screens/superadmin/role_assignment.dart/role_assignment_screen.dart create mode 100644 lib/screens/superadmin/role_extend/role_extend_screen.dart create mode 100644 lib/screens/superadmin/role_module/role_module_scree.dart create mode 100644 lib/screens/superadmin/roles_under/roles_under_screen.dart create mode 100644 lib/screens/superadmin/stations/stations_screen.dart create mode 100644 lib/screens/unit2/basic-info/components/qr_image.dart delete mode 100644 lib/screens/unit2/homepage.dart/components/dashboard.dart create mode 100644 lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart create mode 100644 lib/screens/unit2/homepage.dart/components/dashboard/dashboard_icon_generator.dart create mode 100644 lib/screens/unit2/homepage.dart/components/dashboard/shared_card_label.dart create mode 100644 lib/screens/unit2/homepage.dart/components/dashboard/superadmin_expanded_menu.dart create mode 100644 lib/screens/unit2/roles/rbac/add_rbac.dart create mode 100644 lib/screens/unit2/roles/rbac/rbac.dart create mode 100644 lib/screens/utils/formatters.dart create mode 100644 lib/sevices/roles/rbac_operations/agency_services.dart create mode 100644 lib/sevices/roles/rbac_operations/module_objects_services.dart create mode 100644 lib/sevices/roles/rbac_operations/module_services.dart create mode 100644 lib/sevices/roles/rbac_operations/object_services.dart create mode 100644 lib/sevices/roles/rbac_operations/operation_services.dart create mode 100644 lib/sevices/roles/rbac_operations/permission_service.dart create mode 100644 lib/sevices/roles/rbac_operations/role_assignment_services.dart create mode 100644 lib/sevices/roles/rbac_operations/role_extend_services.dart create mode 100644 lib/sevices/roles/rbac_operations/role_module_services.dart create mode 100644 lib/sevices/roles/rbac_operations/role_services.dart create mode 100644 lib/sevices/roles/rbac_operations/roles_under_services.dart create mode 100644 lib/sevices/roles/rbac_operations/station_services.dart create mode 100644 lib/sevices/roles/rbac_services.dart diff --git a/lib/bloc/rbac/rbac_bloc.dart b/lib/bloc/rbac/rbac_bloc.dart new file mode 100644 index 0000000..8d8287a --- /dev/null +++ b/lib/bloc/rbac/rbac_bloc.dart @@ -0,0 +1,45 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/model/rbac/permission.dart'; +import 'package:unit2/sevices/roles/rbac_services.dart'; + +import '../../model/rbac/new_permission.dart'; +import '../../model/rbac/rbac.dart'; + +part 'rbac_event.dart'; +part 'rbac_state.dart'; + +class RbacBloc extends Bloc { + + RbacBloc() : super(RbacInitial()) { + List? modules; + List? objects; + List? roles; + List? permissions; + List? operations; + on((event, emit) async { + emit(RbacLoadingState()); + try { + modules = await RbacServices.instance.getModules(); + objects = await RbacServices.instance.getObjects(); + roles = await RbacServices.instance.getRole(); + permissions = await RbacServices.instance.getPermission(); + operations = await RbacServices.instance.getOperations(); + emit(RbacScreenSetted(modules: modules!, objects: objects!, operations: operations!, permission: permissions!, role: roles!)); + } catch (e) { + emit(RbacErrorState(message: e.toString())); + } + });on((event, emit)async{ + try{ + emit(RbacLoadingState()); + Map responseStatus = await RbacServices.instance.assignRBAC(assigneeId: event.assigneeId, assignerId: event.assignerId, selectedRole: event.selectedRole, selectedModule: event.selectedModule, permissionId: event.permissionId, newPermissions: event.newPermissions); + emit(RbacAssignedState(responseStatus: responseStatus)); + }catch(e){ + emit(RbacErrorState(message: e.toString())); + } + }); + on((event,emit){ + emit(RbacScreenSetted(modules: modules!, objects: objects!, operations: operations!, permission: permissions!, role: roles!)); + }); + } +} diff --git a/lib/bloc/rbac/rbac_event.dart b/lib/bloc/rbac/rbac_event.dart new file mode 100644 index 0000000..4daa43e --- /dev/null +++ b/lib/bloc/rbac/rbac_event.dart @@ -0,0 +1,29 @@ +part of 'rbac_bloc.dart'; + +abstract class RbacEvent extends Equatable { + const RbacEvent(); + + @override + List get props => []; +} + +class SetRbacScreen extends RbacEvent {} + +class AssignedRbac extends RbacEvent { + final int assigneeId; + final int assignerId; + final RBAC? selectedRole; + final RBAC? selectedModule; + final List permissionId; + final List newPermissions; + const AssignedRbac( + {required this.assigneeId, + required this.assignerId, + required this.newPermissions, + required this.permissionId, + required this.selectedModule, + required this.selectedRole}); +} +class LoadRbac extends RbacEvent{ + +} diff --git a/lib/bloc/rbac/rbac_operations/agency/agency_bloc.dart b/lib/bloc/rbac/rbac_operations/agency/agency_bloc.dart new file mode 100644 index 0000000..de3cc89 --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/agency/agency_bloc.dart @@ -0,0 +1,47 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/model/utils/agency.dart'; +import 'package:unit2/utils/profile_utilities.dart'; +import '../../../../model/utils/category.dart'; +import '../../../../sevices/roles/rbac_operations/agency_services.dart'; + +part 'agency_event.dart'; +part 'agency_state.dart'; + +class AgencyBloc extends Bloc { + AgencyBloc() : super(AgencyInitial()) { + List agencies = []; + List agencyCategory = []; + on((event, emit) async { + emit(AgencyLoadingState()); + try { + if (agencies.isEmpty) { + agencies = await AgencyServices.instance.getAgencies(); + } + if (agencyCategory.isEmpty) { + agencyCategory = await ProfileUtilities.instance.agencyCategory(); + } + emit( + AgenciesLoaded(agencies: agencies, agencyCategory: agencyCategory)); + } catch (e) { + emit(AgencyErrorState(message: e.toString())); + } + }); + on((event, emit) async { + emit(AgencyLoadingState()); + try { + Map statusResponse = + await AgencyServices.instance.add(agency: event.agency); + if (statusResponse['success']) { + Agency newAgency = Agency.fromJson(statusResponse['data']); + agencies.add(newAgency); + emit(AgencyAddesState(response: statusResponse)); + } else { + emit(AgencyAddesState(response: statusResponse)); + } + } catch (e) { + emit(AgencyErrorState(message: e.toString())); + } + }); + } +} diff --git a/lib/bloc/rbac/rbac_operations/agency/agency_event.dart b/lib/bloc/rbac/rbac_operations/agency/agency_event.dart new file mode 100644 index 0000000..8371eba --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/agency/agency_event.dart @@ -0,0 +1,16 @@ +part of 'agency_bloc.dart'; + +abstract class AgencyEvent extends Equatable { + const AgencyEvent(); + + @override + List get props => []; +} + +class GetAgencies extends AgencyEvent{ + +} +class AddAgency extends AgencyEvent{ + final Agency agency; + const AddAgency({required this.agency}); +} diff --git a/lib/bloc/rbac/rbac_operations/agency/agency_state.dart b/lib/bloc/rbac/rbac_operations/agency/agency_state.dart new file mode 100644 index 0000000..22aadcb --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/agency/agency_state.dart @@ -0,0 +1,33 @@ +part of 'agency_bloc.dart'; + +abstract class AgencyState extends Equatable { + const AgencyState(); + + @override + List get props => []; +} + +class AgenciesLoaded extends AgencyState { + final List agencies; + final List agencyCategory; + const AgenciesLoaded({required this.agencies, required this.agencyCategory}); +} + +class AgencyErrorState extends AgencyState { + final String message; + const AgencyErrorState({required this.message}); +} + +class AgencyLoadingState extends AgencyState {} + +class AgencyAddesState extends AgencyState { + final Map response; + const AgencyAddesState({required this.response}); +} + +class AgencyDeletedState extends AgencyState { + final bool success; + const AgencyDeletedState({required this.success}); +} + +class AgencyInitial extends AgencyState {} diff --git a/lib/bloc/rbac/rbac_operations/module/module_bloc.dart b/lib/bloc/rbac/rbac_operations/module/module_bloc.dart new file mode 100644 index 0000000..6586411 --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/module/module_bloc.dart @@ -0,0 +1,84 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/sevices/roles/rbac_operations/module_services.dart'; +import '../../../../model/rbac/rbac.dart'; +part 'module_event.dart'; +part 'module_state.dart'; + +class ModuleBloc extends Bloc { + ModuleBloc() : super(ModuleInitial()) { + List modules = []; + on((event, emit) async { + emit(ModuleLoadingState()); + try { + if (modules.isEmpty) { + modules = await RbacModuleServices.instance.getRbacModule(); + } + + emit(ModuleLoaded(module: modules)); + } catch (e) { + emit(ModuleErrorState(message: e.toString())); + } + }); ////Add + on((event, emit) async { + try { + emit(ModuleLoadingState()); + Map statusResponse = await RbacModuleServices.instance + .add( + name: event.name!, + slug: event.slug, + short: event.shorthand, + id: event.id); + if (statusResponse['success']) { + RBAC newRole = RBAC.fromJson(statusResponse['data']); + modules.add(newRole); + emit(ModuleAddedState(response: statusResponse)); + } else { + emit(ModuleAddedState(response: statusResponse)); + } + } catch (e) { + emit(ModuleErrorState(message: e.toString())); + } + }); + ////update + on((event, emit) async { + emit(ModuleLoadingState()); + try { + Map statusResponse = await RbacModuleServices.instance + .update( + name: event.name, + slug: event.slug, + short: event.short, + moduleId: event.moduleId, + createdBy: event.createdBy, + updatedBy: event.updatedBy); + + if (statusResponse['success']) { + modules.removeWhere((element) => element.id == event.moduleId); + RBAC newRole = RBAC.fromJson(statusResponse['data']); + modules.add(newRole); + emit(ModuleUpdatedState(response: statusResponse)); + } else { + emit(ModuleUpdatedState(response: statusResponse)); + } + } catch (e) { + emit(ModuleErrorState(message: e.toString())); + } + }); + on((event, emit) async { + emit(ModuleLoadingState()); + try { + bool success = await RbacModuleServices.instance + .deleteRbacModule(moduleId: event.moduleId); + if (success) { + modules.removeWhere((element) => element.id == event.moduleId); + emit(ModuleDeletedState(success: success)); + } else { + emit(ModuleDeletedState(success: success)); + } + } catch (e) { + emit(ModuleErrorState(message: e.toString())); + } + }); + } +} diff --git a/lib/bloc/rbac/rbac_operations/module/module_event.dart b/lib/bloc/rbac/rbac_operations/module/module_event.dart new file mode 100644 index 0000000..53b9ed9 --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/module/module_event.dart @@ -0,0 +1,48 @@ +part of 'module_bloc.dart'; + +abstract class ModuleEvent extends Equatable { + const ModuleEvent(); + + @override + List get props => []; +} + + +class GetModule extends ModuleEvent{ + +} + +class AddRbacModule extends ModuleEvent { + final String? name; + final String? slug; + final String? shorthand; + final int id; + const AddRbacModule( + {required this.id, + required this.name, + required this.shorthand, + required this.slug}); +} + + +class UpdateRbacModule extends ModuleEvent { + final int moduleId; + final String name; + final String? slug; + final String? short; + final int? createdBy; + final int updatedBy; + const UpdateRbacModule({ + required this.moduleId, + required this.createdBy, + required this.name, + required this.short, + required this.slug, + required this.updatedBy, + }); +} + +class DeleteRbacModule extends ModuleEvent { + final int moduleId; + const DeleteRbacModule({required this.moduleId}); +} \ No newline at end of file diff --git a/lib/bloc/rbac/rbac_operations/module/module_state.dart b/lib/bloc/rbac/rbac_operations/module/module_state.dart new file mode 100644 index 0000000..7dcde63 --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/module/module_state.dart @@ -0,0 +1,36 @@ +part of 'module_bloc.dart'; + +abstract class ModuleState extends Equatable { + const ModuleState(); + + @override + List get props => []; +} + +class ModuleInitial extends ModuleState {} + +class ModuleLoaded extends ModuleState{ + final List module; + const ModuleLoaded({required this.module}); +} + +class ModuleLoadingState extends ModuleState{ + +} +class ModuleErrorState extends ModuleState{ + final String message; + const ModuleErrorState({required this.message}); +} + +class ModuleAddedState extends ModuleState { + final Map response; + const ModuleAddedState({required this.response}); +} +class ModuleUpdatedState extends ModuleState { + final Map response; + const ModuleUpdatedState({required this.response}); +} +class ModuleDeletedState extends ModuleState{ + final bool success; + const ModuleDeletedState({required this.success}); +} diff --git a/lib/bloc/rbac/rbac_operations/module_objects/module_objects_bloc.dart b/lib/bloc/rbac/rbac_operations/module_objects/module_objects_bloc.dart new file mode 100644 index 0000000..0892399 --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/module_objects/module_objects_bloc.dart @@ -0,0 +1,76 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/model/login_data/user_info/module.dart'; +import 'package:unit2/model/rbac/rbac_rbac.dart'; +import 'package:unit2/sevices/roles/rbac_operations/module_objects_services.dart'; +import 'package:unit2/sevices/roles/rbac_operations/object_services.dart'; + +import '../../../../model/rbac/rbac.dart'; +import '../../../../sevices/roles/rbac_operations/module_services.dart'; +part 'module_objects_event.dart'; +part 'module_objects_state.dart'; + +class ModuleObjectsBloc extends Bloc { + ModuleObjectsBloc() : super(ModuleObjectsInitial()) { + List moduleObjects = []; + List objects = []; + List modules = []; + on((event, emit) async { + emit(ModuleObjectLoadingState()); + try { + if (moduleObjects.isEmpty) { + moduleObjects = + await RbacModuleObjectsServices.instance.getModuleObjects(); + } + if (objects.isEmpty) { + objects = await RbacObjectServices.instance.getRbacObjects(); + } + if (modules.isEmpty) { + modules = await RbacModuleServices.instance.getRbacModule(); + } + emit(ModuleObjectLoadedState( + moduleObjects: moduleObjects, objecs: objects, modules: modules)); + } catch (e) { + emit(ModuleObjectsErrorState(message: e.toString())); + } + }); + on((event, emit) async { + try { + emit(ModuleObjectLoadingState()); + Map statusResponse = + await RbacModuleObjectsServices.instance.add( + assignerId: event.assignerId, + moduleId: event.moduleId, + objectsId: event.objectsId); + + if (statusResponse['success']) { + statusResponse['data'].forEach((var permission) { + ModuleObjects newModuleObject = ModuleObjects.fromJson(permission); + moduleObjects.add(newModuleObject); + emit(ModuleObjectAddedState(response: statusResponse)); + }); + } else { + emit(ModuleObjectAddedState(response: statusResponse)); + } + } catch (e) { + emit(ModuleObjectsErrorState(message: e.toString())); + } + }); + on((event, emit) async { + emit(ModuleObjectLoadingState()); + try { + bool success = await RbacModuleObjectsServices.instance + .deleteRbacModuleObject(moduleObjectId: event.moduleObjectId); + if (success) { + moduleObjects + .removeWhere((element) => element.id == event.moduleObjectId); + emit(ModuleObjectDeletedState(success: success)); + } else { + emit(ModuleObjectDeletedState(success: success)); + } + } catch (e) { + emit(ModuleObjectsErrorState(message: e.toString())); + } + }); + } +} diff --git a/lib/bloc/rbac/rbac_operations/module_objects/module_objects_event.dart b/lib/bloc/rbac/rbac_operations/module_objects/module_objects_event.dart new file mode 100644 index 0000000..65d9e89 --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/module_objects/module_objects_event.dart @@ -0,0 +1,26 @@ +part of 'module_objects_bloc.dart'; + +abstract class ModuleObjectsEvent extends Equatable { + const ModuleObjectsEvent(); + + @override + List get props => []; +} + +class GetModuleObjects extends ModuleObjectsEvent {} + +class DeleteRbacModuleObject extends ModuleObjectsEvent { + final int moduleObjectId; + const DeleteRbacModuleObject({required this.moduleObjectId}); +} + +class AddRbacModuleObjects extends ModuleObjectsEvent { + final int moduleId; + final List objectsId; + final int assignerId; + const AddRbacModuleObjects( + {required this.assignerId, + required this.moduleId, + required this.objectsId}); +} + diff --git a/lib/bloc/rbac/rbac_operations/module_objects/module_objects_state.dart b/lib/bloc/rbac/rbac_operations/module_objects/module_objects_state.dart new file mode 100644 index 0000000..9028df0 --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/module_objects/module_objects_state.dart @@ -0,0 +1,37 @@ +part of 'module_objects_bloc.dart'; + +abstract class ModuleObjectsState extends Equatable { + const ModuleObjectsState(); + + @override + List get props => []; +} + +class ModuleObjectsInitial extends ModuleObjectsState {} + +class ModuleObjectLoadedState extends ModuleObjectsState { + final List moduleObjects; + final List objecs; + final List modules; + const ModuleObjectLoadedState( + {required this.moduleObjects, + required this.modules, + required this.objecs}); +} + +class ModuleObjectsErrorState extends ModuleObjectsState { + final String message; + const ModuleObjectsErrorState({required this.message}); +} + +class ModuleObjectLoadingState extends ModuleObjectsState {} + +class ModuleObjectAddedState extends ModuleObjectsState { + final Map response; + const ModuleObjectAddedState({required this.response}); +} + +class ModuleObjectDeletedState extends ModuleObjectsState { + final bool success; + const ModuleObjectDeletedState({required this.success}); +} diff --git a/lib/bloc/rbac/rbac_operations/object/object_bloc.dart b/lib/bloc/rbac/rbac_operations/object/object_bloc.dart new file mode 100644 index 0000000..024aa4d --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/object/object_bloc.dart @@ -0,0 +1,87 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/sevices/roles/rbac_operations/object_services.dart'; + +import '../../../../model/rbac/rbac.dart'; + +part 'object_event.dart'; +part 'object_state.dart'; + +class ObjectBloc extends Bloc { + ObjectBloc() : super(ObjectInitial()) { + List objects = []; + on((event, emit) async { + emit(ObjectLoadingState()); + try { + if (objects.isEmpty) { + objects = await RbacObjectServices.instance.getRbacObjects(); + } + emit(ObjectLoaded(objects: objects)); + } catch (e) { + emit(ObjectErrorState(message: e.toString())); + } + }); + ////Add + on((event, emit) async { + try { + emit(ObjectLoadingState()); + Map statusResponse = await RbacObjectServices.instance + .add( + name: event.name!, + slug: event.slug, + short: event.shorthand, + id: event.id); + if (statusResponse['success']) { + RBAC newObject = RBAC.fromJson(statusResponse['data']); + objects.add(newObject); + emit(ObjectAddedState(response: statusResponse)); + } else { + emit(ObjectAddedState(response: statusResponse)); + } + } catch (e) { + emit(ObjectErrorState(message: e.toString())); + } + }); + on((event, emit) async { + emit(ObjectLoadingState()); + try { + Map statusResponse = await RbacObjectServices.instance + .update( + name: event.name, + slug: event.slug, + short: event.short, + objectId: event.objectId, + createdBy: event.createdBy, + updatedBy: event.updatedBy); + + if (statusResponse['success']) { + objects.removeWhere((element) => element.id == event.objectId); + RBAC newObject = RBAC.fromJson(statusResponse['data']); + objects.add(newObject); + emit(ObjectUpdatedState(response: statusResponse)); + } else { + emit(ObjectUpdatedState(response: statusResponse)); + } + } catch (e) { + emit(ObjectErrorState(message: e.toString())); + } + }); + on((event, emit) async { + emit(ObjectLoadingState()); + try { + bool success = await RbacObjectServices.instance + .deleteRbacRole(objectId: event.objectId); + if (success) { + objects.removeWhere((element) => element.id == event.objectId); + emit(ObjectDeletedState(success: success)); + } else { + emit(ObjectDeletedState(success: success)); + } + } catch (e) { + emit(ObjectErrorState(message: e.toString())); + } + }); + } + } + + diff --git a/lib/bloc/rbac/rbac_operations/object/object_event.dart b/lib/bloc/rbac/rbac_operations/object/object_event.dart new file mode 100644 index 0000000..a654e0d --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/object/object_event.dart @@ -0,0 +1,46 @@ +part of 'object_bloc.dart'; + +abstract class ObjectEvent extends Equatable { + const ObjectEvent(); + + @override + List get props => []; +} + +class GetObjects extends ObjectEvent{ + +} +class AddRbacObject extends ObjectEvent { + final String? name; + final String? slug; + final String? shorthand; + final int id; + const AddRbacObject( + {required this.id, + required this.name, + required this.shorthand, + required this.slug}); +} + + +class UpdateRbacObject extends ObjectEvent { + final int objectId; + final String name; + final String? slug; + final String? short; + final int? createdBy; + final int updatedBy; + const UpdateRbacObject({ + required this.objectId, + required this.createdBy, + required this.name, + required this.short, + required this.slug, + required this.updatedBy, + }); +} + +class DeleteRbacObject extends ObjectEvent { + final int objectId; + const DeleteRbacObject({required this.objectId}); +} diff --git a/lib/bloc/rbac/rbac_operations/object/object_state.dart b/lib/bloc/rbac/rbac_operations/object/object_state.dart new file mode 100644 index 0000000..1c45114 --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/object/object_state.dart @@ -0,0 +1,36 @@ +part of 'object_bloc.dart'; + +abstract class ObjectState extends Equatable { + const ObjectState(); + + @override + List get props => []; +} + +class ObjectInitial extends ObjectState {} + +class ObjectLoaded extends ObjectState{ + final List objects; + const ObjectLoaded({required this.objects}); +} + +class ObjectLoadingState extends ObjectState{ + +} + +class ObjectErrorState extends ObjectState{ + final String message; + const ObjectErrorState({required this.message}); +} +class ObjectAddedState extends ObjectState { + final Map response; + const ObjectAddedState({required this.response}); +} +class ObjectUpdatedState extends ObjectState { + final Map response; + const ObjectUpdatedState({required this.response}); +} +class ObjectDeletedState extends ObjectState{ + final bool success; + const ObjectDeletedState({required this.success}); +} \ No newline at end of file diff --git a/lib/bloc/rbac/rbac_operations/operation/operation_bloc.dart b/lib/bloc/rbac/rbac_operations/operation/operation_bloc.dart new file mode 100644 index 0000000..5dcf5e8 --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/operation/operation_bloc.dart @@ -0,0 +1,84 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/model/rbac/rbac.dart'; +import 'package:unit2/sevices/roles/rbac_operations/operation_services.dart'; + +part 'operation_event.dart'; +part 'operation_state.dart'; + +class OperationBloc extends Bloc { + OperationBloc() : super(OperationInitial()) { + List operations = []; + on((event, emit) async { + emit(OperationLoadingState()); + try { + if (operations.isEmpty) { + operations = await RbacOperationServices.instance.getRbacOperations(); + } + + emit(OperationsLoaded(operations: operations)); + } catch (e) { + emit(OperationErrorState(message: e.toString())); + } + }); + on((event, emit) async { + try { + emit(OperationLoadingState()); + Map statusResponse = + await RbacOperationServices.instance.add( + name: event.name!, + slug: event.slug, + short: event.shorthand, + id: event.id); + if (statusResponse['success']) { + RBAC newOperation = RBAC.fromJson(statusResponse['data']); + operations.add(newOperation); + emit(OperationAddedState(response: statusResponse)); + } else { + emit(OperationAddedState(response: statusResponse)); + } + } catch (e) { + emit(OperationErrorState(message: e.toString())); + } + }); + on((event, emit) async { + emit(OperationLoadingState()); + try { + Map statusResponse = + await RbacOperationServices.instance.update( + name: event.name, + slug: event.slug, + short: event.short, + operationId: event.operationId, + createdBy: event.createdBy, + updatedBy: event.updatedBy); + + if (statusResponse['success']) { + operations.removeWhere((element) => element.id == event.operationId); + RBAC newRole = RBAC.fromJson(statusResponse['data']); + operations.add(newRole); + emit(OperationUpdatedState(response: statusResponse)); + } else { + emit(OperationUpdatedState(response: statusResponse)); + } + } catch (e) { + emit(OperationErrorState(message: e.toString())); + } + }); + on((event, emit) async { + emit(OperationLoadingState()); + try { + bool success = await RbacOperationServices.instance + .delete(operation: event.operationId); + if (success) { + operations.removeWhere((element) => element.id == event.operationId); + emit(OperationDeletedState(success: success)); + } else { + emit(OperationDeletedState(success: success)); + } + } catch (e) { + emit(OperationErrorState(message: e.toString())); + } + }); + } +} diff --git a/lib/bloc/rbac/rbac_operations/operation/operation_event.dart b/lib/bloc/rbac/rbac_operations/operation/operation_event.dart new file mode 100644 index 0000000..35bd990 --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/operation/operation_event.dart @@ -0,0 +1,45 @@ +part of 'operation_bloc.dart'; + +abstract class OperationEvent extends Equatable { + const OperationEvent(); + + @override + List get props => []; +} +class GetOperations extends OperationEvent{ + +} +class AddRbacOperation extends OperationEvent { + final String? name; + final String? slug; + final String? shorthand; + final int id; + const AddRbacOperation( + {required this.id, + required this.name, + required this.shorthand, + required this.slug}); +} + + +class UpdateRbacOperation extends OperationEvent { + final int operationId; + final String name; + final String? slug; + final String? short; + final int? createdBy; + final int updatedBy; + const UpdateRbacOperation({ + required this.operationId, + required this.createdBy, + required this.name, + required this.short, + required this.slug, + required this.updatedBy, + }); +} + +class DeleteRbacOperation extends OperationEvent { + final int operationId; + const DeleteRbacOperation({required this.operationId}); +} diff --git a/lib/bloc/rbac/rbac_operations/operation/operation_state.dart b/lib/bloc/rbac/rbac_operations/operation/operation_state.dart new file mode 100644 index 0000000..4490f67 --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/operation/operation_state.dart @@ -0,0 +1,34 @@ +part of 'operation_bloc.dart'; + +abstract class OperationState extends Equatable { + const OperationState(); + + @override + List get props => []; +} + +class OperationInitial extends OperationState {} + +class OperationsLoaded extends OperationState { + final List operations; + const OperationsLoaded({required this.operations}); +} + +class OperationLoadingState extends OperationState {} + +class OperationErrorState extends OperationState { + final String message; + const OperationErrorState({required this.message}); +} +class OperationAddedState extends OperationState { + final Map response; + const OperationAddedState({required this.response}); +} +class OperationUpdatedState extends OperationState { + final Map response; + const OperationUpdatedState({required this.response}); +} +class OperationDeletedState extends OperationState{ + final bool success; + const OperationDeletedState({required this.success}); +} diff --git a/lib/bloc/rbac/rbac_operations/permission/permission_bloc.dart b/lib/bloc/rbac/rbac_operations/permission/permission_bloc.dart new file mode 100644 index 0000000..fe14035 --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/permission/permission_bloc.dart @@ -0,0 +1,81 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/model/rbac/permission.dart'; +import 'package:unit2/model/rbac/rbac.dart'; +import 'package:unit2/screens/unit2/roles/rbac/add_rbac.dart'; +import 'package:unit2/sevices/roles/rbac_operations/permission_service.dart'; + +import '../../../../sevices/roles/rbac_operations/object_services.dart'; +import '../../../../sevices/roles/rbac_operations/operation_services.dart'; + +part 'permission_event.dart'; +part 'permission_state.dart'; + +class PermissionBloc extends Bloc { + PermissionBloc() : super(PermissionInitial()) { + List permissions = []; + List objects = []; + List operations = []; + on((event, emit) async { + try { + emit(PermissonLoadingState()); + if (permissions.isEmpty) { + permissions = + await RbacPermissionServices.instance.getRbacPermission(); + } + if (objects.isEmpty) { + objects = await RbacObjectServices.instance.getRbacObjects(); + } + if (operations.isEmpty) { + operations = await RbacOperationServices.instance.getRbacOperations(); + } + emit(PermissionLoaded( + permissions: permissions, + objects: objects, + operations: operations)); + } catch (e) { + emit(PermissionErrorState(message: e.toString())); + } + }); + ////Add + on((event, emit) async { + try { + emit(PermissonLoadingState()); + Map statusResponse = + await RbacPermissionServices.instance.add( + assignerId: event.assignerId, + objectId: event.objectId, + operationsId: event.operationIds); + if (statusResponse['success']) { + statusResponse['data'].forEach((var permission) { + RBACPermission newPermission = RBACPermission.fromJson(permission); + permissions.add(newPermission); + }); + + emit(PermissionAddedState(response: statusResponse)); + } else { + emit(PermissionAddedState(response: statusResponse)); + } + } catch (e) { + emit(PermissionErrorState(message: e.toString())); + } + }); + ////Delete + on((event, emit) async { + emit(PermissonLoadingState()); + try { + bool success = await RbacPermissionServices.instance + .deletePermission(permissionId: event.permissionId); + if (success) { + permissions + .removeWhere((element) => element.id == event.permissionId); + emit(PermissionDeletedState(success: success)); + } else { + emit(PermissionDeletedState(success: success)); + } + } catch (e) { + emit(PermissionErrorState(message: e.toString())); + } + }); + } +} diff --git a/lib/bloc/rbac/rbac_operations/permission/permission_event.dart b/lib/bloc/rbac/rbac_operations/permission/permission_event.dart new file mode 100644 index 0000000..c1c2cc3 --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/permission/permission_event.dart @@ -0,0 +1,25 @@ +part of 'permission_bloc.dart'; + +abstract class PermissionEvent extends Equatable { + const PermissionEvent(); + + @override + List get props => []; +} + +class GetPermissions extends PermissionEvent {} + +class AddRbacPermission extends PermissionEvent { + final int objectId; + final List operationIds; + final int assignerId; + const AddRbacPermission( + {required this.assignerId, + required this.objectId, + required this.operationIds}); +} + +class DeleteRbacPermission extends PermissionEvent { + final int permissionId; + const DeleteRbacPermission({required this.permissionId}); +} diff --git a/lib/bloc/rbac/rbac_operations/permission/permission_state.dart b/lib/bloc/rbac/rbac_operations/permission/permission_state.dart new file mode 100644 index 0000000..c8d2487 --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/permission/permission_state.dart @@ -0,0 +1,37 @@ +part of 'permission_bloc.dart'; + +abstract class PermissionState extends Equatable { + const PermissionState(); + + @override + List get props => []; +} + +class PermissionInitial extends PermissionState {} + +class PermissionLoaded extends PermissionState { + final List permissions; + final List objects; + final List operations; + const PermissionLoaded( + {required this.permissions, + required this.objects, + required this.operations}); +} + +class PermissionAddedState extends PermissionState { + final Map response; + const PermissionAddedState({required this.response}); +} + +class PermissonLoadingState extends PermissionState {} + +class PermissionErrorState extends PermissionState { + final String message; + const PermissionErrorState({required this.message}); +} + +class PermissionDeletedState extends PermissionState { + final bool success; + const PermissionDeletedState({required this.success}); +} diff --git a/lib/bloc/rbac/rbac_operations/role/role_bloc.dart b/lib/bloc/rbac/rbac_operations/role/role_bloc.dart new file mode 100644 index 0000000..5f9980b --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/role/role_bloc.dart @@ -0,0 +1,83 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/model/rbac/rbac.dart'; +import 'package:unit2/sevices/roles/rbac_operations/role_services.dart'; +part 'role_event.dart'; +part 'role_state.dart'; + +class RoleBloc extends Bloc { + RoleBloc() : super(RoleInitial()) { + List roles = []; + on((event, emit) async { + try { + emit(RoleLoadingState()); + if (roles.isEmpty) { + roles = await RbacRoleServices.instance.getRbacRoles(); + } + emit(RoleLoadedState(roles: roles)); + } catch (e) { + emit(RoleErrorState(message: e.toString())); + } + ////Add + }); + on((event, emit) async { + try { + emit(RoleLoadingState()); + Map statusResponse = await RbacRoleServices.instance + .add( + name: event.name!, + slug: event.slug, + short: event.shorthand, + id: event.id); + if (statusResponse['success']) { + RBAC newRole = RBAC.fromJson(statusResponse['data']); + roles.add(newRole); + emit(RoleAddedState(response: statusResponse)); + } else { + emit(RoleAddedState(response: statusResponse)); + } + } catch (e) { + emit(RoleErrorState(message: e.toString())); + } + }); + on((event, emit) async { + emit(RoleLoadingState()); + try { + Map statusResponse = await RbacRoleServices.instance + .update( + name: event.name, + slug: event.slug, + short: event.short, + roleId: event.roleId, + createdBy: event.createdBy, + updatedBy: event.updatedBy); + + if (statusResponse['success']) { + roles.removeWhere((element) => element.id == event.roleId); + RBAC newRole = RBAC.fromJson(statusResponse['data']); + roles.add(newRole); + emit(RoleUpdatedState(response: statusResponse)); + } else { + emit(RoleUpdatedState(response: statusResponse)); + } + } catch (e) { + emit(RoleErrorState(message: e.toString())); + } + }); + on((event, emit) async { + emit(RoleLoadingState()); + try { + bool success = await RbacRoleServices.instance + .deleteRbacRole(roleId: event.roleId); + if (success) { + roles.removeWhere((element) => element.id == event.roleId); + emit(RoleDeletedState(success: success)); + } else { + emit(RoleDeletedState(success: success)); + } + } catch (e) { + emit(RoleErrorState(message: e.toString())); + } + }); + } +} diff --git a/lib/bloc/rbac/rbac_operations/role/role_event.dart b/lib/bloc/rbac/rbac_operations/role/role_event.dart new file mode 100644 index 0000000..60987d9 --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/role/role_event.dart @@ -0,0 +1,45 @@ +part of 'role_bloc.dart'; + +abstract class RoleEvent extends Equatable { + const RoleEvent(); + + @override + List get props => []; +} + +class GetRoles extends RoleEvent {} + +class AddRbacRole extends RoleEvent { + final String? name; + final String? slug; + final String? shorthand; + final int id; + const AddRbacRole( + {required this.id, + required this.name, + required this.shorthand, + required this.slug}); +} + + +class UpdateRbacRole extends RoleEvent { + final int roleId; + final String name; + final String? slug; + final String? short; + final int? createdBy; + final int updatedBy; + const UpdateRbacRole({ + required this.roleId, + required this.createdBy, + required this.name, + required this.short, + required this.slug, + required this.updatedBy, + }); +} + +class DeleteRbacRole extends RoleEvent { + final int roleId; + const DeleteRbacRole({required this.roleId}); +} diff --git a/lib/bloc/rbac/rbac_operations/role/role_state.dart b/lib/bloc/rbac/rbac_operations/role/role_state.dart new file mode 100644 index 0000000..255ce6e --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/role/role_state.dart @@ -0,0 +1,37 @@ +part of 'role_bloc.dart'; + +abstract class RoleState extends Equatable { + const RoleState(); + + @override + List get props => []; +} + +class RoleInitial extends RoleState {} + +class RoleLoadedState extends RoleState { + final List roles; + const RoleLoadedState({required this.roles}); +} + +class RoleLoadingState extends RoleState {} + +class RoleErrorState extends RoleState { + final String message; + const RoleErrorState({required this.message}); +} + +class RoleAddedState extends RoleState { + final Map response; + const RoleAddedState({required this.response}); +} +class RoleUpdatedState extends RoleState { + final Map response; + const RoleUpdatedState({required this.response}); +} +class RoleDeletedState extends RoleState{ + final bool success; + const RoleDeletedState({required this.success}); +} + + diff --git a/lib/bloc/rbac/rbac_operations/role_extend/role_extend_bloc.dart b/lib/bloc/rbac/rbac_operations/role_extend/role_extend_bloc.dart new file mode 100644 index 0000000..f4c75d8 --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/role_extend/role_extend_bloc.dart @@ -0,0 +1,75 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/roles_under/roles_under_bloc.dart'; +import 'package:unit2/model/rbac/role_extend.dart'; +import 'package:unit2/sevices/roles/rbac_operations/role_extend_services.dart'; + +import '../../../../model/rbac/rbac.dart'; +import '../../../../sevices/roles/rbac_operations/role_services.dart'; + +part 'role_extend_event.dart'; +part 'role_extend_state.dart'; + +class RoleExtendBloc extends Bloc { + RoleExtendBloc() : super(RoleExtendInitial()) { + List rolesExtend = []; + List roleExtendIds = []; + List roles = []; + on((event, emit) async { + emit(RoleExtendLoadingState()); + try { + if (rolesExtend.isEmpty) { + rolesExtend = await RbacRoleExtendServices.instance.getRolesExtend(); + } + + if (roles.isEmpty) { + roles = await RbacRoleServices.instance.getRbacRoles(); + } + for (var roleExt in rolesExtend) { + roleExtendIds.add(roleExt.id); + } + emit(RoleExtendLoadedState(rolesExtend: rolesExtend, roles: roles)); + } catch (e) { + emit(RoleExtendErrorState(message: e.toString())); + } + }); + on((event, emit) async { + try { + emit(RoleExtendLoadingState()); + Map statusResponse = await RbacRoleExtendServices + .instance + .add(roleId: event.roleId, rolesExtendsId: event.roleExtendsId); + + if (statusResponse['success']) { + statusResponse['data'].forEach((var roleExt) { + RolesExtend newRoleExtend = RolesExtend.fromJson(roleExt); + if(!roleExtendIds.contains(newRoleExtend.id)){ + rolesExtend.add(newRoleExtend); + } + }); + emit(RoleExtendAddedState(response: statusResponse)); + } else { + emit(RoleExtendAddedState(response: statusResponse)); + } + } catch (e) { + emit(RoleExtendErrorState(message: e.toString())); + } + }); + on((event, emit) async { + emit(RoleExtendLoadingState()); + try { + bool success = await RbacRoleExtendServices.instance + .delete(roleExtendId: event.roleExtendId); + if (success) { + rolesExtend + .removeWhere((element) => element.id == event.roleExtendId); + emit(RoleExtendDeletedState(success: success)); + } else { + emit(RoleExtendDeletedState(success: success)); + } + } catch (e) { + emit(RoleExtendErrorState(message: e.toString())); + } + }); + } +} diff --git a/lib/bloc/rbac/rbac_operations/role_extend/role_extend_event.dart b/lib/bloc/rbac/rbac_operations/role_extend/role_extend_event.dart new file mode 100644 index 0000000..c0b4637 --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/role_extend/role_extend_event.dart @@ -0,0 +1,21 @@ +part of 'role_extend_bloc.dart'; + +abstract class RoleExtendEvent extends Equatable { + const RoleExtendEvent(); + + @override + List get props => []; +} + +class GetRoleExtend extends RoleExtendEvent {} + +class DeleteRoleExtend extends RoleExtendEvent { + final int roleExtendId; + const DeleteRoleExtend({required this.roleExtendId}); +} + +class AddRoleExtend extends RoleExtendEvent { + final int roleId; + final List roleExtendsId; + const AddRoleExtend({required this.roleId, required this.roleExtendsId}); +} diff --git a/lib/bloc/rbac/rbac_operations/role_extend/role_extend_state.dart b/lib/bloc/rbac/rbac_operations/role_extend/role_extend_state.dart new file mode 100644 index 0000000..8e788cb --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/role_extend/role_extend_state.dart @@ -0,0 +1,34 @@ +part of 'role_extend_bloc.dart'; + +abstract class RoleExtendState extends Equatable { + const RoleExtendState(); + + @override + List get props => []; +} + +class RoleExtendInitial extends RoleExtendState {} + +class RoleExtendLoadedState extends RoleExtendState { + final List rolesExtend; + final List roles; + + const RoleExtendLoadedState({required this.rolesExtend, required this.roles}); +} + +class RoleExtendLoadingState extends RoleExtendState {} + +class RoleExtendErrorState extends RoleExtendState { + final String message; + const RoleExtendErrorState({required this.message}); +} + +class RoleExtendAddedState extends RoleExtendState { + final Map response; + const RoleExtendAddedState({required this.response}); +} + +class RoleExtendDeletedState extends RoleExtendState { + final bool success; + const RoleExtendDeletedState({required this.success}); +} diff --git a/lib/bloc/rbac/rbac_operations/role_module/role_module_bloc.dart b/lib/bloc/rbac/rbac_operations/role_module/role_module_bloc.dart new file mode 100644 index 0000000..3f52bd4 --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/role_module/role_module_bloc.dart @@ -0,0 +1,72 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/model/rbac/rbac.dart'; +import 'package:unit2/sevices/roles/rbac_operations/role_module_services.dart'; +import 'package:unit2/sevices/roles/rbac_operations/role_services.dart'; +import '../../../../model/rbac/role_module.dart'; +import '../../../../sevices/roles/rbac_operations/module_services.dart'; +part 'role_module_event.dart'; +part 'role_module_state.dart'; + +class RoleModuleBloc extends Bloc { + RoleModuleBloc() : super(RoleModuleInitial()) { + List roleModules = []; + List roles = []; + List modules = []; + on((event, emit) async { + emit(RoleModuleLoadingState()); + try { + if (roleModules.isEmpty) { + roleModules = await RbacRoleModuleServices.instance.getRoleModules(); + } + if (modules.isEmpty) { + modules = await RbacModuleServices.instance.getRbacModule(); + } + if (roles.isEmpty) { + roles = await RbacRoleServices.instance.getRbacRoles(); + } + emit(RoleModuleLoadedState(roleModules: roleModules,modules: modules,roles: roles)); + } catch (e) { + emit(RoleModuleErrorState(message: e.toString())); + } + }); + on((event, emit) async { + try { + emit(RoleModuleLoadingState()); + Map statusResponse = + await RbacRoleModuleServices.instance.add( + assignerId: event.assignerId, + roleId: event.roleId, + moduleIds: event.moduleIds); + + if (statusResponse['success']) { + statusResponse['data'].forEach((var roleMod) { + RoleModules newRoleModule = RoleModules.fromJson(roleMod); + roleModules.add(newRoleModule); + emit(RoleModuleAddedState(response: statusResponse)); + }); + } else { + emit(RoleModuleAddedState(response: statusResponse)); + } + } catch (e) { + emit(RoleModuleErrorState(message: e.toString())); + } + }); + on((event, emit) async { + emit(RoleModuleLoadingState()); + try { + bool success = await RbacRoleModuleServices.instance + .deleteRbacRoleModule(moduleObjectId: event.roleModuleId); + if (success) { + roleModules + .removeWhere((element) => element.id == event.roleModuleId); + emit(RoleModuleDeletedState(success: success)); + } else { + RoleModuleDeletedState(success: success); + } + } catch (e) { + emit(RoleModuleErrorState(message: e.toString())); + } + }); + } +} diff --git a/lib/bloc/rbac/rbac_operations/role_module/role_module_event.dart b/lib/bloc/rbac/rbac_operations/role_module/role_module_event.dart new file mode 100644 index 0000000..b0cfa8a --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/role_module/role_module_event.dart @@ -0,0 +1,25 @@ +part of 'role_module_bloc.dart'; + +abstract class RoleModuleEvent extends Equatable { + const RoleModuleEvent(); + + @override + List get props => []; +} + +class GetRoleModules extends RoleModuleEvent {} + +class DeleteRoleModule extends RoleModuleEvent { + final int roleModuleId; + const DeleteRoleModule({required this.roleModuleId}); +} + +class AddRoleModule extends RoleModuleEvent { + final int roleId; + final List moduleIds; + final int assignerId; + const AddRoleModule( + {required this.assignerId, + required this.roleId, + required this.moduleIds}); +} diff --git a/lib/bloc/rbac/rbac_operations/role_module/role_module_state.dart b/lib/bloc/rbac/rbac_operations/role_module/role_module_state.dart new file mode 100644 index 0000000..733d56c --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/role_module/role_module_state.dart @@ -0,0 +1,36 @@ +part of 'role_module_bloc.dart'; + +abstract class RoleModuleState extends Equatable { + const RoleModuleState(); + + @override + List get props => []; +} + +class RoleModuleInitial extends RoleModuleState {} + +class RoleModuleLoadedState extends RoleModuleState { + final List roleModules; + final List roles; + final List modules; + + const RoleModuleLoadedState( + {required this.roleModules, required this.modules, required this.roles}); +} + +class RoleModuleLoadingState extends RoleModuleState {} + +class RoleModuleErrorState extends RoleModuleState { + final String message; + const RoleModuleErrorState({required this.message}); +} + +class RoleModuleAddedState extends RoleModuleState { + final Map response; + const RoleModuleAddedState({required this.response}); +} + +class RoleModuleDeletedState extends RoleModuleState { + final bool success; + const RoleModuleDeletedState({required this.success}); +} diff --git a/lib/bloc/rbac/rbac_operations/roles_under/roles_under_bloc.dart b/lib/bloc/rbac/rbac_operations/roles_under/roles_under_bloc.dart new file mode 100644 index 0000000..f8fc508 --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/roles_under/roles_under_bloc.dart @@ -0,0 +1,67 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/model/rbac/role_under.dart'; +import 'package:unit2/sevices/roles/rbac_operations/roles_under_services.dart'; + +import '../../../../model/rbac/rbac.dart'; +import '../../../../sevices/roles/rbac_operations/role_services.dart'; + +part 'roles_under_event.dart'; +part 'roles_under_state.dart'; + +class RolesUnderBloc extends Bloc { + RolesUnderBloc() : super(RolesUnderInitial()) { + List rolesUnder = []; + List roles = []; + on((event, emit) async { + emit(RoleUnderLoadingState()); + try { + if (rolesUnder.isEmpty) { + rolesUnder = await RbacRoleUnderServices.instance.getRolesUnder(); + } + + if (roles.isEmpty) { + roles = await RbacRoleServices.instance.getRbacRoles(); + } + emit(RoleUnderLoadedState(rolesUnder: rolesUnder, roles: roles)); + } catch (e) { + emit(RoleUnderErrorState(message: e.toString())); + } + }); + on((event, emit) async { + try { + emit(RoleUnderLoadingState()); + Map statusResponse = await RbacRoleUnderServices + .instance + .add(roleId: event.roleId, rolesId: event.roleUnderIds); + + if (statusResponse['success']) { + statusResponse['data'].forEach((var roleMod) { + RolesUnder newRoleUnder = RolesUnder.fromJson(roleMod); + rolesUnder.add(newRoleUnder); + emit(RoleUnderAddedState(response: statusResponse)); + }); + } else { + emit(RoleUnderAddedState(response: statusResponse)); + } + } catch (e) { + emit(RoleUnderErrorState(message: e.toString())); + } + }); + on((event, emit) async { + emit(RoleUnderLoadingState()); + try { + bool success = await RbacRoleUnderServices.instance + .deleteRbacRoleUnder(roleUnderId: event.roleUnderId); + if (success) { + rolesUnder.removeWhere((element) => element.id == event.roleUnderId); + emit(RoleUnderDeletedState(success: success)); + } else { + emit(RoleUnderDeletedState(success: success)); + } + } catch (e) { + emit(RoleUnderErrorState(message: e.toString())); + } + }); + } +} diff --git a/lib/bloc/rbac/rbac_operations/roles_under/roles_under_event.dart b/lib/bloc/rbac/rbac_operations/roles_under/roles_under_event.dart new file mode 100644 index 0000000..3c17f46 --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/roles_under/roles_under_event.dart @@ -0,0 +1,21 @@ +part of 'roles_under_bloc.dart'; + +abstract class RolesUnderEvent extends Equatable { + const RolesUnderEvent(); + + @override + List get props => []; +} + +class GetRolesUnder extends RolesUnderEvent {} + +class DeleteRoleUnder extends RolesUnderEvent { + final int roleUnderId; + const DeleteRoleUnder({required this.roleUnderId}); +} + +class AddRoleUnder extends RolesUnderEvent{ + final int roleId; + final List roleUnderIds; + const AddRoleUnder({required this.roleId, required this.roleUnderIds}); +} diff --git a/lib/bloc/rbac/rbac_operations/roles_under/roles_under_state.dart b/lib/bloc/rbac/rbac_operations/roles_under/roles_under_state.dart new file mode 100644 index 0000000..0263414 --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/roles_under/roles_under_state.dart @@ -0,0 +1,34 @@ +part of 'roles_under_bloc.dart'; + +abstract class RolesUnderState extends Equatable { + const RolesUnderState(); + + @override + List get props => []; +} + +class RolesUnderInitial extends RolesUnderState {} + +class RoleUnderLoadedState extends RolesUnderState { + final List rolesUnder; + final List roles; + + const RoleUnderLoadedState({required this.rolesUnder, required this.roles}); +} + +class RoleUnderLoadingState extends RolesUnderState {} + +class RoleUnderErrorState extends RolesUnderState { + final String message; + const RoleUnderErrorState({required this.message}); +} + +class RoleUnderAddedState extends RolesUnderState { + final Map response; + const RoleUnderAddedState({required this.response}); +} + +class RoleUnderDeletedState extends RolesUnderState { + final bool success; + const RoleUnderDeletedState({required this.success}); +} diff --git a/lib/bloc/rbac/rbac_operations/station/station_bloc.dart b/lib/bloc/rbac/rbac_operations/station/station_bloc.dart new file mode 100644 index 0000000..2dec814 --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/station/station_bloc.dart @@ -0,0 +1,47 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/model/rbac/rbac_station.dart'; +import 'package:unit2/sevices/roles/rbac_operations/station_services.dart'; +import '../../../../model/utils/agency.dart'; +import '../../../../utils/profile_utilities.dart'; +part 'station_event.dart'; +part 'station_state.dart'; + +class StationBloc extends Bloc { + StationBloc() : super(StationInitial()) { + List stations = []; + List agencies = []; + on((event, emit) async { + emit(StationLoadingState()); + try { + stations = await RbacStationServices.instance + .getStations(agencyId: event.agencyId); + + if (agencies.isEmpty) { + List newAgencies = + await ProfileUtilities.instance.getAgecies(); + agencies = newAgencies; + } + emit(StationLoadedState(stations: stations, agencies: agencies)); + } catch (e) { + emit(StationErrorState(message: e.toString())); + } + }); + on((event, emit)async { + // emit(StationLoadingState()); + try { + stations = await RbacStationServices.instance + .getStations(agencyId: event.agencyId); + + if (agencies.isEmpty) { + List newAgencies = + await ProfileUtilities.instance.getAgecies(); + agencies = newAgencies; + } + emit(StationLoadedState(stations: stations, agencies: agencies)); + } catch (e) { + emit(StationErrorState(message: e.toString())); + } + }); + } +} diff --git a/lib/bloc/rbac/rbac_operations/station/station_event.dart b/lib/bloc/rbac/rbac_operations/station/station_event.dart new file mode 100644 index 0000000..ea2c989 --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/station/station_event.dart @@ -0,0 +1,18 @@ +part of 'station_bloc.dart'; + +abstract class StationEvent extends Equatable { + const StationEvent(); + + @override + List get props => []; +} + +class GetStations extends StationEvent { + final int agencyId; + const GetStations({required this.agencyId}); +} + +class FilterStation extends StationEvent { + final int agencyId; + const FilterStation({required this.agencyId}); +} diff --git a/lib/bloc/rbac/rbac_operations/station/station_state.dart b/lib/bloc/rbac/rbac_operations/station/station_state.dart new file mode 100644 index 0000000..7ab511f --- /dev/null +++ b/lib/bloc/rbac/rbac_operations/station/station_state.dart @@ -0,0 +1,32 @@ +part of 'station_bloc.dart'; + +abstract class StationState extends Equatable { + const StationState(); + + @override + List get props => []; +} + +class StationInitial extends StationState {} + +class StationLoadedState extends StationState { + final List agencies; + final List stations; + const StationLoadedState({required this.stations, required this.agencies}); + @override + List get props => [agencies,stations]; +} + +class StationLoadingState extends StationState {} + +class StationErrorState extends StationState { + final String message; + const StationErrorState({required this.message}); + @override + List get props => [message]; +} + +class FilterStationState extends StationState { + final int agencyId; + const FilterStationState({required this.agencyId}); +} diff --git a/lib/bloc/rbac/rbac_state.dart b/lib/bloc/rbac/rbac_state.dart new file mode 100644 index 0000000..6c793a5 --- /dev/null +++ b/lib/bloc/rbac/rbac_state.dart @@ -0,0 +1,36 @@ +part of 'rbac_bloc.dart'; + +abstract class RbacState extends Equatable { + const RbacState(); + + @override + List get props => []; +} + +class RbacInitial extends RbacState {} + +class RbacScreenSetted extends RbacState { + final List modules; + final List objects; + final List role; + final List permission; + final List operations; + const RbacScreenSetted( + {required this.modules, + required this.objects, + required this.operations, + required this.permission, + required this.role}); +} + +class RbacLoadingState extends RbacState {} + +class RbacErrorState extends RbacState { + final String message; + const RbacErrorState({required this.message}); +} + +class RbacAssignedState extends RbacState{ +final Map responseStatus; + const RbacAssignedState({required this.responseStatus}); +} diff --git a/lib/bloc/role/pass_check/pass_check_bloc.dart b/lib/bloc/role/pass_check/pass_check_bloc.dart index c1d157a..9458357 100644 --- a/lib/bloc/role/pass_check/pass_check_bloc.dart +++ b/lib/bloc/role/pass_check/pass_check_bloc.dart @@ -22,15 +22,15 @@ class PassCheckBloc extends Bloc { int? stationId; String? cpId; on((event, emit) async { - try { + // try { emit(PassCheckLoadingState()); List response = await PassCheckServices.instance .getPassCheckArea(roleId: event.roleId, userId: event.userId); roleId = event.roleId; emit(AssignAreaLoaded(assignedArea: response, roleId: roleId!)); - } catch (e) { - emit(PassCheckErrorState(message: e.toString())); - } + // } catch (e) { + // emit(PassCheckErrorState(message: e.toString())); + // } }); on((event, emit) { otherInputs = event.includeOtherInputs; diff --git a/lib/bloc/role_assignment/role_assignment_bloc.dart b/lib/bloc/role_assignment/role_assignment_bloc.dart new file mode 100644 index 0000000..2d75f2d --- /dev/null +++ b/lib/bloc/role_assignment/role_assignment_bloc.dart @@ -0,0 +1,96 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/sevices/roles/rbac_services.dart'; + +import '../../model/profile/basic_information/primary-information.dart'; +import '../../model/rbac/assigned_role.dart'; +import '../../model/rbac/rbac.dart'; +import '../../sevices/roles/rbac_operations/role_assignment_services.dart'; +import '../../sevices/roles/rbac_operations/role_services.dart'; + +part 'role_assignment_event.dart'; +part 'role_assignment_state.dart'; + +class RoleAssignmentBloc + extends Bloc { + RoleAssignmentBloc() : super(RoleAssignmentInitial()) { + List assignedRoles = []; + int userId; + String? fname; + String? lname; + String? fullname; + Profile? profile; + List roles = []; + on((event, emit) async { + emit(RoleAssignmentLoadingState()); + fname = event.firstname; + lname = event.lastname; + fullname = "${event.firstname} ${event.lastname}"; + + try { + profile = await RbacRoleAssignmentServices.instance.searchUser( + page: 1, name: event.firstname, lastname: event.lastname); + if (profile != null && profile?.id != null) { + assignedRoles = await RbacRoleAssignmentServices.instance + .getAssignedRoles( + firstname: event.firstname, lastname: event.lastname); + + if (roles.isEmpty) { + roles = await RbacRoleServices.instance.getRbacRoles(); + } + emit(AssignedRoleLoaded( + assignedRoles: assignedRoles, fullname: fullname!, roles: roles)); + } else { + emit(UserNotExistError()); + } + + + } catch (e) { + emit(RoleAssignmentErrorState(message: e.toString())); + } + }); + on((event, emit) { + emit(AssignedRoleLoaded( + assignedRoles: assignedRoles, fullname: fullname!, roles: roles)); + }); + on((event, emit) async { + emit(RoleAssignmentLoadingState()); + try { + bool success = await RbacRoleAssignmentServices.instance + .deleteAssignedRole(roleId: event.roleId); + if (success) { + assignedRoles.removeWhere((element) => element.id == event.roleId); + emit(AssignedRoleDeletedState(success: success)); + } else { + emit(AssignedRoleDeletedState(success: success)); + } + } catch (e) { + emit(RoleAssignmentErrorState(message: e.toString())); + } + }); + on((event, emit) async { + emit(RoleAssignmentLoadingState()); + try { + Map statusResponse = + await RbacRoleAssignmentServices.instance.add( + userId: profile!.webuserId!, + assignerId: event.assignerId, + roles: event.roles); + if (statusResponse['success']) { + assignedRoles = []; + statusResponse['data'].forEach((var roles) { + AssignedRole newAssignRole = AssignedRole.fromJson(roles); + if (!event.roles.contains(newAssignRole.id)) { + assignedRoles.add(newAssignRole); + } + }); + emit(AssignedRoleAddedState(response: statusResponse)); + } else { + emit(AssignedRoleAddedState(response: statusResponse)); + } + } catch (e) { + emit(RoleAssignmentErrorState(message: e.toString())); + } + }); + } +} diff --git a/lib/bloc/role_assignment/role_assignment_event.dart b/lib/bloc/role_assignment/role_assignment_event.dart new file mode 100644 index 0000000..016ce40 --- /dev/null +++ b/lib/bloc/role_assignment/role_assignment_event.dart @@ -0,0 +1,28 @@ +part of 'role_assignment_bloc.dart'; + +abstract class RoleAssignmentEvent extends Equatable { + const RoleAssignmentEvent(); + + @override + List get props => []; +} + +class GetAssignedRoles extends RoleAssignmentEvent { + final String firstname; + final String lastname; + const GetAssignedRoles({required this.firstname, required this.lastname}); +} + +class DeleteAssignRole extends RoleAssignmentEvent { + final int roleId; + const DeleteAssignRole({required this.roleId}); +} + +class LoadAssignedRole extends RoleAssignmentEvent {} + +class AssignRole extends RoleAssignmentEvent { + final List roles; + final int assignerId; + const AssignRole( + {required this.assignerId, required this.roles}); +} diff --git a/lib/bloc/role_assignment/role_assignment_state.dart b/lib/bloc/role_assignment/role_assignment_state.dart new file mode 100644 index 0000000..98dc25a --- /dev/null +++ b/lib/bloc/role_assignment/role_assignment_state.dart @@ -0,0 +1,37 @@ +part of 'role_assignment_bloc.dart'; + +abstract class RoleAssignmentState extends Equatable { + const RoleAssignmentState(); + + @override + List get props => []; +} + +class RoleAssignmentInitial extends RoleAssignmentState {} + +class AssignedRoleLoaded extends RoleAssignmentState { + final String fullname; + final List assignedRoles; + final List roles; + const AssignedRoleLoaded( + {required this.assignedRoles, required this.fullname, required this.roles}); +} + +class RoleAssignmentLoadingState extends RoleAssignmentState {} + +class RoleAssignmentErrorState extends RoleAssignmentState { + final String message; + const RoleAssignmentErrorState({required this.message}); +} +class UserNotExistError extends RoleAssignmentState{ + +} +class AssignedRoleDeletedState extends RoleAssignmentState { + final bool success; + const AssignedRoleDeletedState({required this.success}); +} + +class AssignedRoleAddedState extends RoleAssignmentState { + final Map response; + const AssignedRoleAddedState({required this.response}); +} diff --git a/lib/model/login_data/user_info/module.dart b/lib/model/login_data/user_info/module.dart index 700d9a5..a72f278 100644 --- a/lib/model/login_data/user_info/module.dart +++ b/lib/model/login_data/user_info/module.dart @@ -11,7 +11,7 @@ class Module { String? icon; String? name; String? slug; - List? objects; + List? objects; factory Module.fromJson(Map json) => Module( id: json["id"], @@ -20,8 +20,8 @@ class Module { slug: json["slug"], objects: json["objects"] == null ? [] - : List.from( - json["objects"]!.map((x) => Object.fromJson(x))), + : List.from( + json["objects"]!.map((x) => ModuleObject.fromJson(x))), ); Map toJson() => { @@ -35,8 +35,8 @@ class Module { }; } -class Object { - Object({ +class ModuleObject { + ModuleObject({ this.id, this.name, this.slug, @@ -48,7 +48,7 @@ class Object { String? slug; List? operations; - factory Object.fromJson(Map json) => Object( + factory ModuleObject.fromJson(Map json) => ModuleObject( id: json["id"], name: json["name"], slug: json["slug"], diff --git a/lib/model/login_data/user_info/role.dart b/lib/model/login_data/user_info/role.dart index ef371f7..ff7c545 100644 --- a/lib/model/login_data/user_info/role.dart +++ b/lib/model/login_data/user_info/role.dart @@ -41,34 +41,4 @@ class Role { ? [] : List.from(assignedArea!.map((x) => x!.toJson())), }; -}class Object { - Object({ - this.id, - this.name, - this.slug, - this.operations, - }); - - int? id; - String? name; - String? slug; - List? operations; - - factory Object.fromJson(Map json) => Object( - id: json["id"], - name: json["name"], - slug: json["slug"], - operations: json["operations"] == null - ? [] - : List.from(json["operations"]!.map((x) => x)), - ); - - Map toJson() => { - "id": id, - "name": name, - "slug": slug, - "operations": operations == null - ? [] - : List.from(operations!.map((x) => x)), - }; } \ No newline at end of file diff --git a/lib/model/profile/basic_information/primary-information.dart b/lib/model/profile/basic_information/primary-information.dart index 80b7535..c936a39 100644 --- a/lib/model/profile/basic_information/primary-information.dart +++ b/lib/model/profile/basic_information/primary-information.dart @@ -14,6 +14,7 @@ Profile primaryInformationFromJson(String str) => Profile.fromJson(json.decode(s String primaryInformationToJson(Profile data) => json.encode(data.toJson()); class Profile { + int? webuserId; int? id; String? lastName; String? firstName; @@ -40,6 +41,7 @@ class Profile { String? ip; Profile({ + required this.webuserId, required this.id, required this.lastName, required this.firstName, @@ -67,13 +69,14 @@ class Profile { }); factory Profile.fromJson(Map json) => Profile( + webuserId: null, id: json["id"], lastName: json["last_name"], firstName: json["first_name"], middleName: json["middle_name"], nameExtension: json["name_extension"], sex: json["sex"], - birthdate: DateTime.parse(json["birthdate"]), + birthdate:json['birthdate'] ==null?null: DateTime.parse(json["birthdate"]), civilStatus: json["civil_status"], bloodType: json["blood_type"], heightM: json["height_m"]?.toDouble(), diff --git a/lib/model/rbac/assigned_role.dart b/lib/model/rbac/assigned_role.dart new file mode 100644 index 0000000..52a1b78 --- /dev/null +++ b/lib/model/rbac/assigned_role.dart @@ -0,0 +1,130 @@ +// To parse this JSON data, do +// +// final assignedRole = assignedRoleFromJson(jsonString); + +import 'package:meta/meta.dart'; +import 'dart:convert'; + +AssignedRole assignedRoleFromJson(String str) => AssignedRole.fromJson(json.decode(str)); + +String assignedRoleToJson(AssignedRole data) => json.encode(data.toJson()); + +class AssignedRole { + final int? id; + final Role? role; + final CreatedBy? user; + final DateTime? createdAt; + final DateTime? updatedAt; + final CreatedBy? createdBy; + final CreatedBy? updatedBy; + + AssignedRole({ + required this.id, + required this.role, + required this.user, + required this.createdAt, + required this.updatedAt, + required this.createdBy, + required this.updatedBy, + }); + + factory AssignedRole.fromJson(Map json) => AssignedRole( + id: json["id"], + role: json['role'] == null?null: Role.fromJson(json["role"]), + user: json['role'] == null?null: CreatedBy.fromJson(json["user"]), + createdAt:json["created_at"] == null?null: DateTime.parse(json["created_at"]), + updatedAt: json["updated_at"] == null?null: DateTime.parse(json["updated_at"]), + createdBy: json["created_by"] == null? null: CreatedBy.fromJson(json["created_by"]), + updatedBy: json["updated_by"] == null? null: CreatedBy.fromJson(json["updated_by"]), + ); + + Map toJson() => { + "id": id, + "role": role?.toJson(), + "user": user?.toJson(), + "created_at": createdAt?.toIso8601String(), + "updated_at": updatedAt?.toIso8601String(), + "created_by": createdBy?.toJson(), + "updated_by": updatedBy?.toJson(), + }; +} + +class CreatedBy { + final int id; + final String username; + final String firstName; + final String lastName; + final String email; + final bool isActive; + + CreatedBy({ + required this.id, + required this.username, + required this.firstName, + required this.lastName, + required this.email, + required this.isActive, + }); + + factory CreatedBy.fromJson(Map json) => CreatedBy( + id: json["id"], + username: json["username"], + firstName: json["first_name"], + lastName: json["last_name"], + email: json["email"], + isActive: json["is_active"], + ); + + Map toJson() => { + "id": id, + "username": username, + "first_name": firstName, + "last_name": lastName, + "email": email, + "is_active": isActive, + }; +} + +class Role { + final int id; + final String name; + final String slug; + final String shorthand; + final DateTime createdAt; + final DateTime updatedAt; + final CreatedBy createdBy; + final CreatedBy updatedBy; + + Role({ + required this.id, + required this.name, + required this.slug, + required this.shorthand, + required this.createdAt, + required this.updatedAt, + required this.createdBy, + required this.updatedBy, + }); + + factory Role.fromJson(Map json) => Role( + id: json["id"], + name: json["name"], + slug: json["slug"], + shorthand: json["shorthand"], + createdAt: DateTime.parse(json["created_at"]), + updatedAt: DateTime.parse(json["updated_at"]), + createdBy: CreatedBy.fromJson(json["created_by"]), + updatedBy: CreatedBy.fromJson(json["updated_by"]), + ); + + Map toJson() => { + "id": id, + "name": name, + "slug": slug, + "shorthand": shorthand, + "created_at": createdAt.toIso8601String(), + "updated_at": updatedAt.toIso8601String(), + "created_by": createdBy.toJson(), + "updated_by": updatedBy.toJson(), + }; +} diff --git a/lib/model/rbac/new_permission.dart b/lib/model/rbac/new_permission.dart new file mode 100644 index 0000000..8c84b49 --- /dev/null +++ b/lib/model/rbac/new_permission.dart @@ -0,0 +1,70 @@ +// To parse this JSON data, do +// +// final newPermission = newPermissionFromJson(jsonString); + +import 'package:meta/meta.dart'; +import 'dart:convert'; + +NewPermission newPermissionFromJson(String str) => NewPermission.fromJson(json.decode(str)); + +String newPermissionToJson(NewPermission data) => json.encode(data.toJson()); + +class NewPermission { + final int? newPermissionObjectId; + final String? newObjectName; + final String? newObjectSlug; + final String? newObjectShorthand; + final List newPermissionOperationIds; + final List newOperations; + + NewPermission({ + required this.newPermissionObjectId, + required this.newObjectName, + required this.newObjectSlug, + required this.newObjectShorthand, + required this.newPermissionOperationIds, + required this.newOperations, + }); + + factory NewPermission.fromJson(Map json) => NewPermission( + newPermissionObjectId: json["_new_permission_object_id"], + newObjectName: json["_new_object_name"], + newObjectSlug: json["_new_object_slug"], + newObjectShorthand: json["_new_object_shorthand"], + newPermissionOperationIds: List.from(json["_new_permission_operation_ids"].map((x) => x)), + newOperations: List.from(json["_new_operations"].map((x) => NewOperation.fromJson(x))), + ); + + Map toJson() => { + "_new_permission_object_id": newPermissionObjectId, + "_new_object_name": newObjectName, + "_new_object_slug": newObjectSlug, + "_new_object_shorthand": newObjectShorthand, + "_new_permission_operation_ids": List.from(newPermissionOperationIds.map((x) => x)), + "_new_operations": List.from(newOperations.map((x) => x.toJson())), + }; +} + +class NewOperation { + final String newOperationName; + final String newOperationSlug; + final String newOperationShorthand; + + NewOperation({ + required this.newOperationName, + required this.newOperationSlug, + required this.newOperationShorthand, + }); + + factory NewOperation.fromJson(Map json) => NewOperation( + newOperationName: json["_new_operation_name"], + newOperationSlug: json["_new_operation_slug"], + newOperationShorthand: json["_new_operation_shorthand"], + ); + + Map toJson() => { + "_new_operation_name": newOperationName, + "_new_operation_slug": newOperationSlug, + "_new_operation_shorthand": newOperationShorthand, + }; +} diff --git a/lib/model/rbac/permission.dart b/lib/model/rbac/permission.dart new file mode 100644 index 0000000..dd2c2e3 --- /dev/null +++ b/lib/model/rbac/permission.dart @@ -0,0 +1,79 @@ +import 'package:unit2/model/rbac/rbac.dart'; + +class RBACPermission { + final int? id; + final RBAC? object; + final RBAC? operation; + final DateTime? createdAt; + final dynamic updatedAt; + final CreatedBy? createdBy; + final dynamic updatedBy; + + RBACPermission({ + required this.id, + required this.object, + required this.operation, + required this.createdAt, + required this.updatedAt, + required this.createdBy, + required this.updatedBy, + }); + + factory RBACPermission.fromJson(Map json) => RBACPermission( + id: json["id"], + object: json['object'] == null?null:RBAC.fromJson(json["object"]), + operation: json['operation'] == null?null: RBAC.fromJson(json["operation"]), + createdAt: DateTime.parse(json["created_at"]), + updatedAt: json["updated_at"], + createdBy: CreatedBy.fromJson(json["created_by"]), + updatedBy: json["updated_by"], + ); + + Map toJson() => { + "id": id, + "object": object?.toJson(), + "operation": operation?.toJson(), + "created_at": createdAt?.toIso8601String(), + "updated_at": updatedAt, + "created_by": createdBy?.toJson(), + "updated_by": updatedBy, + }; +} + +class CreatedBy { + final int id; + final String username; + final String firstName; + final String lastName; + final String email; + final bool isActive; + + CreatedBy({ + required this.id, + required this.username, + required this.firstName, + required this.lastName, + required this.email, + required this.isActive, + }); + + factory CreatedBy.fromJson(Map json) => CreatedBy( + id: json["id"], + username: json["username"], + firstName: json["first_name"], + lastName: json["last_name"], + email: json["email"], + isActive: json["is_active"], + ); + + Map toJson() => { + "id": id, + "username": username, + "first_name": firstName, + "last_name": lastName, + "email": email, + "is_active": isActive, + }; +} + + diff --git a/lib/model/rbac/rbac.dart b/lib/model/rbac/rbac.dart new file mode 100644 index 0000000..9fb897d --- /dev/null +++ b/lib/model/rbac/rbac.dart @@ -0,0 +1,94 @@ +// To parse this JSON data, do +// +// final rbac = rbacFromJson(jsonString); + +import 'package:meta/meta.dart'; +import 'dart:convert'; + +RBAC rbacFromJson(String str) => RBAC.fromJson(json.decode(str)); + +String rbacToJson(RBAC data) => json.encode(data.toJson()); + +class RBAC { + final int? id; + final String? name; + final String? slug; + final String? shorthand; + final String? fontawesomeIcon; + final DateTime? createdAt; + final dynamic updatedAt; + final CreatedBy? createdBy; + final dynamic updatedBy; + + RBAC({ + required this.id, + required this.name, + required this.slug, + required this.shorthand, + required this.fontawesomeIcon, + required this.createdAt, + required this.updatedAt, + required this.createdBy, + required this.updatedBy, + }); + + factory RBAC.fromJson(Map json) => RBAC( + id: json["id"], + name: json["name"], + slug: json["slug"], + shorthand: json["shorthand"], + fontawesomeIcon: json["fontawesome_icon"], + createdAt: json['created_at'] == null?null: DateTime.parse(json["created_at"]), + updatedAt: json["updated_at"], + createdBy: json['created_by']==null?null: CreatedBy.fromJson(json["created_by"]), + updatedBy: json["updated_by"], + ); + + Map toJson() => { + "id": id, + "name": name, + "slug": slug, + "shorthand": shorthand, + "fontawesome_icon": fontawesomeIcon, + "created_at": createdAt?.toIso8601String(), + "updated_at": updatedAt, + "created_by": createdBy?.toJson(), + "updated_by": updatedBy, + }; +} + +class CreatedBy { + final int id; + final String username; + final String firstName; + final String lastName; + final String email; + final bool isActive; + + CreatedBy({ + required this.id, + required this.username, + required this.firstName, + required this.lastName, + required this.email, + required this.isActive, + }); + + factory CreatedBy.fromJson(Map json) => CreatedBy( + id: json["id"], + username: json["username"], + firstName: json["first_name"], + lastName: json["last_name"], + email: json["email"], + isActive: json["is_active"], + ); + + Map toJson() => { + "id": id, + "username": username, + "first_name": firstName, + "last_name": lastName, + "email": email, + "is_active": isActive, + }; +} diff --git a/lib/model/rbac/rbac_rbac.dart b/lib/model/rbac/rbac_rbac.dart new file mode 100644 index 0000000..ec5bc5f --- /dev/null +++ b/lib/model/rbac/rbac_rbac.dart @@ -0,0 +1,122 @@ +class ModuleObjects { + final int id; + final Module object; + final Module module; + final DateTime? createdAt; + final dynamic updatedAt; + final AtedBy createdBy; + final dynamic updatedBy; + + ModuleObjects({ + required this.id, + required this.object, + required this.module, + required this.createdAt, + required this.updatedAt, + required this.createdBy, + required this.updatedBy, + }); + + factory ModuleObjects.fromJson(Map json) => ModuleObjects( + id:json['id'], + object: Module.fromJson(json["object"]), + module: Module.fromJson(json["module"]), + createdAt: DateTime.parse(json["created_at"]), + updatedAt: json["updated_at"], + createdBy: AtedBy.fromJson(json["created_by"]), + updatedBy: json["updated_by"], + ); + + Map toJson() => { + "object": object.toJson(), + "module": module.toJson(), + "created_at": createdAt?.toIso8601String(), + "updated_at": updatedAt, + "created_by": createdBy.toJson(), + "updated_by": updatedBy, + }; +} + +class AtedBy { + final int id; + final String username; + final String firstName; + final String lastName; + final String email; + final bool isActive; + + AtedBy({ + required this.id, + required this.username, + required this.firstName, + required this.lastName, + required this.email, + required this.isActive, + }); + + factory AtedBy.fromJson(Map json) => AtedBy( + id: json["id"], + username: json["username"], + firstName: json["first_name"], + lastName: json["last_name"], + email: json["email"], + isActive: json["is_active"], + ); + + Map toJson() => { + "id": id, + "username": username, + "first_name": firstName, + "last_name": lastName, + "email": email, + "is_active": isActive, + }; +} + +class Module { + final int id; + final String? name; + final String? slug; + final String? shorthand; + final String? fontawesomeIcon; + final DateTime? createdAt; + final DateTime? updatedAt; + final AtedBy? createdBy; + final AtedBy? updatedBy; + + Module({ + required this.id, + required this.name, + required this.slug, + required this.shorthand, + required this.fontawesomeIcon, + required this.createdAt, + required this.updatedAt, + required this.createdBy, + required this.updatedBy, + }); + + factory Module.fromJson(Map json) => Module( + id: json["id"], + name: json["name"], + slug: json["slug"], + shorthand: json["shorthand"], + fontawesomeIcon: json['fontawesome_icon'] == null?null: json["fontawesome_icon"], + createdAt:json["created_at"] == null? null: DateTime.parse(json["created_at"]), + updatedAt: json["updated_at"] == null? null: DateTime.parse(json["updated_at"]), + createdBy: json["created_by"] == null? null:AtedBy.fromJson(json["created_by"]), + updatedBy: json["updated_by"] == null? null:AtedBy.fromJson(json["updated_by"]), + ); + + Map toJson() => { + "id": id, + "name": name, + "slug": slug, + "shorthand": shorthand, + "fontawesome_icon": fontawesomeIcon, + "created_at": createdAt?.toIso8601String(), + "updated_at": updatedAt?.toIso8601String(), + "created_by": createdBy?.toJson(), + "updated_by": updatedBy?.toJson(), + }; +} diff --git a/lib/model/rbac/rbac_station.dart b/lib/model/rbac/rbac_station.dart new file mode 100644 index 0000000..2a73bd5 --- /dev/null +++ b/lib/model/rbac/rbac_station.dart @@ -0,0 +1,164 @@ +// To parse this JSON data, do +// +// final assignArea = assignAreaFromJson(jsonString); + + + +class RbacStation { + final int? id; + final String? stationName; + final StationType? stationType; + final int? hierarchyOrderNo; + final String? headPosition; + final GovernmentAgency? governmentAgency; + final String? acronym; + final int? parentStation; + final String? code; + final String? fullcode; + final List? childStationInfo; + final bool? islocationUnderParent; + final int? mainParentStation; + final String? description; + final bool? ishospital; + final bool? isactive; + final bool? sellingStation; + + RbacStation({ + required this.id, + required this.stationName, + required this.stationType, + required this.hierarchyOrderNo, + required this.headPosition, + required this.governmentAgency, + required this.acronym, + required this.parentStation, + required this.code, + required this.fullcode, + required this.childStationInfo, + required this.islocationUnderParent, + required this.mainParentStation, + required this.description, + required this.ishospital, + required this.isactive, + required this.sellingStation, + }); + + factory RbacStation.fromJson(Map json) => RbacStation( + id: json["id"], + stationName: json["station_name"], + stationType:json["station_type"] ==null?null: StationType.fromJson(json["station_type"]), + hierarchyOrderNo: json["hierarchy_order_no"], + headPosition: json["head_position"], + governmentAgency: json["government_agency"] == null?null:GovernmentAgency.fromJson(json["government_agency"]), + acronym: json["acronym"], + parentStation: json["parent_station"], + code: json["code"], + fullcode: json["fullcode"], + childStationInfo: null, + islocationUnderParent: json["islocation_under_parent"], + mainParentStation: json["main_parent_station"], + description: json["description"], + ishospital: json["ishospital"], + isactive: json["isactive"], + sellingStation: json["selling_station"], + ); + + Map toJson() => { + "id": id, + "station_name": stationName, + "station_type": stationType?.toJson(), + "hierarchy_order_no": hierarchyOrderNo, + "head_position": headPosition, + "government_agency": governmentAgency?.toJson(), + "acronym": acronym, + "parent_station": parentStation, + "code": code, + "fullcode": fullcode, + "child_station_info": List.from(childStationInfo!.map((x) => x.toJson())), + "islocation_under_parent": islocationUnderParent, + "main_parent_station": mainParentStation, + "description": description, + "ishospital": ishospital, + "isactive": isactive, + "selling_station": sellingStation, + }; +} + +class ChildStationInfo { + final int? id; + final String? stationName; + final String? acroym; + bool? motherStation; + + ChildStationInfo({ + required this.id, + required this.stationName, + required this.acroym, + this.motherStation + }); + + factory ChildStationInfo.fromJson(Map json) => ChildStationInfo( + id: json["id"], + stationName: json["station_name"], + acroym: json["acroym"], + + ); + + Map toJson() => { + "id": id, + "station_name": stationName, + "acroym": acroym, + }; +} + +class GovernmentAgency { + final int? agencyid; + final String? agencyname; + final int? agencycatid; + final bool? privateEntity; + final int? contactinfoid; + + GovernmentAgency({ + required this.agencyid, + required this.agencyname, + required this.agencycatid, + required this.privateEntity, + required this.contactinfoid, + }); + + factory GovernmentAgency.fromJson(Map json) => GovernmentAgency( + agencyid: json["agencyid"], + agencyname: json["agencyname"], + agencycatid: json["agencycatid"], + privateEntity: json["private_entity"], + contactinfoid: json["contactinfoid"], + ); + + Map toJson() => { + "agencyid": agencyid, + "agencyname": agencyname, + "agencycatid": agencycatid, + "private_entity": privateEntity, + "contactinfoid": contactinfoid, + }; +} + +class StationType { + final int? id; + final String? typeName; + + StationType({ + required this.id, + required this.typeName, + }); + + factory StationType.fromJson(Map json) => StationType( + id: json["id"], + typeName: json["type_name"], + ); + + Map toJson() => { + "id": id, + "type_name": typeName, + }; +} diff --git a/lib/model/rbac/role_extend.dart b/lib/model/rbac/role_extend.dart new file mode 100644 index 0000000..acd95fb --- /dev/null +++ b/lib/model/rbac/role_extend.dart @@ -0,0 +1,37 @@ +// To parse this JSON data, do +// +// final rolesExtend = rolesExtendFromJson(jsonString); + +import 'package:meta/meta.dart'; +import 'dart:convert'; + +import 'package:unit2/model/rbac/rbac.dart'; + +RolesExtend rolesExtendFromJson(String str) => RolesExtend.fromJson(json.decode(str)); + +String rolesExtendToJson(RolesExtend data) => json.encode(data.toJson()); + +class RolesExtend { + final int id; + final RBAC roleExtendMain; + final RBAC roleExtendChild; + + RolesExtend({ + required this.id, + required this.roleExtendMain, + required this.roleExtendChild, + }); + + factory RolesExtend.fromJson(Map json) => RolesExtend( + id: json["id"], + roleExtendMain: RBAC.fromJson(json["role_extend_main"]), + roleExtendChild: RBAC.fromJson(json["role_extend_child"]), + ); + + Map toJson() => { + "id": id, + "role_extend_main": roleExtendMain.toJson(), + "role_extend_child": roleExtendChild.toJson(), + }; +} + diff --git a/lib/model/rbac/role_module.dart b/lib/model/rbac/role_module.dart new file mode 100644 index 0000000..295f1cf --- /dev/null +++ b/lib/model/rbac/role_module.dart @@ -0,0 +1,124 @@ + +class RoleModules { + final int? id; + final Module? role; + final Module? module; + final DateTime? createdAt; + final dynamic updatedAt; + final AtedBy? createdBy; + final dynamic updatedBy; + + RoleModules({ + required this.id, + required this.role, + required this.module, + required this.createdAt, + required this.updatedAt, + required this.createdBy, + required this.updatedBy, + }); + + factory RoleModules.fromJson(Map json) => RoleModules( + id: json["id"], + role:json['role'] == null?null: Module.fromJson(json["role"]), + module: json['module'] == null?null:Module.fromJson(json["module"]), + createdAt: json["created_at"] == null? null: DateTime.parse(json["created_at"]), + updatedAt: json["updated_at"], + createdBy: json["created_by"]==null? null: AtedBy.fromJson(json["created_by"]), + updatedBy: json["updated_by"], + ); + + Map toJson() => { + "id": id, + "role": role?.toJson(), + "module": module?.toJson(), + "created_at": createdAt?.toIso8601String(), + "updated_at": updatedAt, + "created_by": createdBy?.toJson(), + "updated_by": updatedBy, + }; +} + +class AtedBy { + final int id; + final String username; + final String firstName; + final String lastName; + final String email; + final bool isActive; + + AtedBy({ + required this.id, + required this.username, + required this.firstName, + required this.lastName, + required this.email, + required this.isActive, + }); + + factory AtedBy.fromJson(Map json) => AtedBy( + id: json["id"], + username: json["username"], + firstName: json["first_name"], + lastName: json["last_name"], + email: json["email"], + isActive: json["is_active"], + ); + + Map toJson() => { + "id": id, + "username": username, + "first_name": firstName, + "last_name": lastName, + "email": email, + "is_active": isActive, + }; +} + +class Module { + final int? id; + final String? name; + final String? slug; + final String? shorthand; + final String? fontawesomeIcon; + final DateTime? createdAt; + final DateTime? updatedAt; + final AtedBy? createdBy; + final AtedBy? updatedBy; + + Module({ + required this.id, + required this.name, + required this.slug, + required this.shorthand, + required this.fontawesomeIcon, + required this.createdAt, + required this.updatedAt, + required this.createdBy, + required this.updatedBy, + }); + + factory Module.fromJson(Map json) => Module( + id: json["id"], + name: json["name"], + slug: json["slug"], + shorthand: json["shorthand"], + fontawesomeIcon: null, + createdAt:json['created_at'] == null?null: DateTime.parse(json["created_at"]), + updatedAt:json["updated_at"] == null?null: DateTime.parse(json["updated_at"]), + createdBy:json["created_by"] ==null?null: AtedBy.fromJson(json["created_by"]), + updatedBy:json["updated_by"] == null?null: AtedBy.fromJson(json["updated_by"]), + ); + + Map toJson() => { + "id": id, + "name": name, + "slug": slug, + "shorthand": shorthand, + "fontawesome_icon": fontawesomeIcon, + "created_at": createdAt?.toIso8601String(), + "updated_at": updatedAt?.toIso8601String(), + "created_by": createdBy?.toJson(), + "updated_by": updatedBy?.toJson(), + }; +} diff --git a/lib/model/rbac/role_under.dart b/lib/model/rbac/role_under.dart new file mode 100644 index 0000000..dedc7c2 --- /dev/null +++ b/lib/model/rbac/role_under.dart @@ -0,0 +1,37 @@ +// To parse this JSON data, do +// +// final rolesUnder = rolesUnderFromJson(jsonString); + +import 'package:meta/meta.dart'; +import 'dart:convert'; + +import 'package:unit2/model/rbac/rbac.dart'; + +RolesUnder rolesUnderFromJson(String str) => RolesUnder.fromJson(json.decode(str)); + +String rolesUnderToJson(RolesUnder data) => json.encode(data.toJson()); + +class RolesUnder { + final int id; + final RBAC roleUnderMain; + final RBAC roleUnderChild; + + RolesUnder({ + required this.id, + required this.roleUnderMain, + required this.roleUnderChild, + }); + + factory RolesUnder.fromJson(Map json) => RolesUnder( + id: json["id"], + roleUnderMain: RBAC.fromJson(json["role_under_main"]), + roleUnderChild: RBAC.fromJson(json["role_under_child"]), + ); + + Map toJson() => { + "id": id, + "role_under_main": roleUnderMain.toJson(), + "role_under_child": roleUnderChild.toJson(), + }; +} + diff --git a/lib/model/roles/pass_check/station_assign_area.dart b/lib/model/roles/pass_check/station_assign_area.dart index 4251403..cbece06 100644 --- a/lib/model/roles/pass_check/station_assign_area.dart +++ b/lib/model/roles/pass_check/station_assign_area.dart @@ -4,7 +4,7 @@ class StationAssignArea { final bool? isactive; - final Area? area; + final Station? area; StationAssignArea({ required this.isactive, @@ -13,7 +13,7 @@ class StationAssignArea { factory StationAssignArea.fromJson(Map json) => StationAssignArea( isactive: json["isactive"], - area: json["area"] == null?null: Area.fromJson(json["area"]), + area: json["area"] == null?null: Station.fromJson(json["area"]), ); Map toJson() => { @@ -22,7 +22,7 @@ class StationAssignArea { }; } -class Area { +class Station { final int? id; final String? stationName; final StationType? stationType; @@ -41,7 +41,7 @@ class Area { final bool? isactive; final bool? sellingStation; - Area({ + Station({ required this.id, required this.stationName, required this.stationType, @@ -61,7 +61,7 @@ class Area { required this.sellingStation, }); - factory Area.fromJson(Map json) => Area( + factory Station.fromJson(Map json) => Station( id: json["id"], stationName: json["station_name"], stationType:json["station_type"] ==null?null: StationType.fromJson(json["station_type"]), @@ -72,7 +72,7 @@ class Area { parentStation: json["parent_station"], code: json["code"], fullcode: json["fullcode"], - childStationInfo: json['child_station_info']==null?[]:List.from(json["child_station_info"].map((x) => ChildStationInfo.fromJson(x))), + childStationInfo: json['child_station_info']==null || json['child_station_info'].isEmpty ?[]:List.from(json["child_station_info"].map((x) => ChildStationInfo.fromJson(x))), islocationUnderParent: json["islocation_under_parent"], mainParentStation: json["main_parent_station"], description: json["description"], @@ -81,7 +81,7 @@ class Area { sellingStation: json["selling_station"], ); - Map toJson() => { + Map toJson() => { "id": id, "station_name": stationName, "station_type": stationType?.toJson(), diff --git a/lib/screens/profile/components/basic_information/edit_basic_info_modal.dart b/lib/screens/profile/components/basic_information/edit_basic_info_modal.dart index 253ea7d..095ca78 100644 --- a/lib/screens/profile/components/basic_information/edit_basic_info_modal.dart +++ b/lib/screens/profile/components/basic_information/edit_basic_info_modal.dart @@ -493,6 +493,7 @@ class _EditBasicProfileInfoScreenState : double.tryParse( _formKey.currentState?.value['weigth']); Profile primaryInformation = Profile( + webuserId: null, id: state.primaryInformation.id, lastName: lastName, firstName: firstName, diff --git a/lib/screens/profile/components/education_screen.dart b/lib/screens/profile/components/education_screen.dart index 3da29ed..4514284 100644 --- a/lib/screens/profile/components/education_screen.dart +++ b/lib/screens/profile/components/education_screen.dart @@ -3,14 +3,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; -import 'package:fluttericon/font_awesome_icons.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/educational_background.dart'; import 'package:unit2/screens/profile/components/education/add_modal.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; -import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; @@ -18,28 +16,37 @@ import '../../../bloc/profile/education/education_bloc.dart'; import '../../../utils/alerts.dart'; import '../../../widgets/Leadings/close_leading.dart'; import 'education/edit_modal.dart'; - class EducationScreen extends StatelessWidget { const EducationScreen({super.key}); - @override Widget build(BuildContext context) { int profileId; String? token; return Scaffold( appBar: AppBar( - title:context.watch().state is AddEducationState? const FittedBox(child: Text("Add Educational Background")): context.watch().state is EditEducationState?const FittedBox(child: Text("Edit Educational Background")):const Text("Education Background"), + title: context.watch().state is AddEducationState + ? const FittedBox(child: Text("Add Educational Background")) + : context.watch().state is EditEducationState + ? const FittedBox(child: Text("Edit Educational Background")) + : const Text("Education Background"), centerTitle: true, backgroundColor: primary, - actions: context.watch().state is EducationalBackgroundLoadedState?[ - AddLeading(onPressed: () { - context.read().add(ShowAddEducationForm()); - }) - ]:(context.watch().state is AddEducationState || context.watch().state is EditEducationState)?[ - CloseLeading(onPressed: () { - context.read().add( LoadEducations()); - }) - ]:[], + actions: context.watch().state + is EducationalBackgroundLoadedState + ? [ + AddLeading(onPressed: () { + context.read().add(ShowAddEducationForm()); + }) + ] + : (context.watch().state is AddEducationState || + context.watch().state + is EditEducationState) + ? [ + CloseLeading(onPressed: () { + context.read().add(LoadEducations()); + }) + ] + : [], ), //userbloc body: ProgressHUD( @@ -66,7 +73,8 @@ class EducationScreen extends StatelessWidget { state is EducationalBackgroundErrorState || state is AddEducationState || state is EditEducationState || - state is EducationDeletedState || state is EditedEducationState) { + state is EducationDeletedState || + state is EditedEducationState) { final progress = ProgressHUD.of(context); progress!.dismiss(); } @@ -92,7 +100,6 @@ class EducationScreen extends StatelessWidget { } } ////EDITED STATE - if (state is EditedEducationState) { if (state.response['success']) { successAlert(context, "Update Successfull!", @@ -188,14 +195,11 @@ class EducationScreen extends StatelessWidget { Row( children: [ Expanded( - child: Text( - level, - style: Theme.of( - context) - .textTheme - .titleSmall! - - )), + child: Text(level, + style: Theme.of( + context) + .textTheme + .titleSmall!)), Text( "$periodFrom - $periodTo", style: Theme.of( @@ -212,7 +216,12 @@ class EducationScreen extends StatelessWidget { school, style: Theme.of(context) .textTheme - .titleMedium!.copyWith(color: primary,fontWeight: FontWeight.w500), + .titleMedium! + .copyWith( + color: primary, + fontWeight: + FontWeight + .w500), ), Container( padding: @@ -228,7 +237,9 @@ class EducationScreen extends StatelessWidget { CrossAxisAlignment .start, children: [ - const SizedBox(height: 8,), + const SizedBox( + height: 8, + ), const Text( " honors: ", style: TextStyle( @@ -237,8 +248,10 @@ class EducationScreen extends StatelessWidget { ), Column( children: honors - .map((Honor honor) => - Text("-${honor.name!.trim()}",style: Theme.of(context).textTheme.labelMedium,)) + .map((Honor honor) => Text( + "-${honor.name!.trim()}", + style: Theme.of(context).textTheme.labelMedium, + )) .toList(), ), ], @@ -312,11 +325,10 @@ class EducationScreen extends StatelessWidget { text: "Remove", value: 2, icon: Icons.delete), - popMenuItem( + popMenuItem( text: "Attach", value: 2, icon: Icons.attach_file), - ], icon: const Icon( Icons.more_vert, @@ -341,8 +353,11 @@ class EducationScreen extends StatelessWidget { } if (state is EducationalBackgroundErrorState) { return SomethingWentWrong( - message: state.message, onpressed: () { - context.read().add(GetEducationalBackground(profileId: profileId, token: token!)); + message: state.message, + onpressed: () { + context.read().add( + GetEducationalBackground( + profileId: profileId, token: token!)); }); } if (state is AddEducationState) { diff --git a/lib/screens/profile/components/learning_development/add_modal.dart b/lib/screens/profile/components/learning_development/add_modal.dart index 5a0e4f7..9eed392 100644 --- a/lib/screens/profile/components/learning_development/add_modal.dart +++ b/lib/screens/profile/components/learning_development/add_modal.dart @@ -150,7 +150,6 @@ class _AddLearningAndDevelopmentScreenState setState(() { show = false; showOtherInputs = true; - selectedTrainingController .text = addTrainingController.text diff --git a/lib/screens/profile/components/work_history/add_modal.dart b/lib/screens/profile/components/work_history/add_modal.dart index 9f16525..f7ee97d 100644 --- a/lib/screens/profile/components/work_history/add_modal.dart +++ b/lib/screens/profile/components/work_history/add_modal.dart @@ -14,7 +14,7 @@ import 'package:unit2/model/utils/category.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/btn-style.dart'; import 'package:unit2/theme-data.dart/colors.dart'; -import 'package:unit2/theme-data.dart/form-style.dart'; +import 'package:unit2/theme-data.dart/form-style.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/utils/validators.dart'; diff --git a/lib/screens/profile/profile.dart b/lib/screens/profile/profile.dart index a7dc9f9..b601337 100644 --- a/lib/screens/profile/profile.dart +++ b/lib/screens/profile/profile.dart @@ -13,7 +13,6 @@ import 'package:unit2/bloc/profile/primary_information/citizenship/citizenship_b import 'package:unit2/bloc/profile/primary_information/contact/contact_bloc.dart'; import 'package:unit2/bloc/profile/primary_information/identification/identification_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; -import 'package:unit2/model/login_data/user_info/user_data.dart'; import 'package:unit2/screens/profile/components/basic_information/address_screen.dart'; import 'package:unit2/screens/profile/components/basic_information/citizenship_screen.dart'; import 'package:unit2/screens/profile/components/basic_information/contact_information_screen.dart'; diff --git a/lib/screens/profile/shared/add_for_empty_search.dart b/lib/screens/profile/shared/add_for_empty_search.dart index 8d90f98..2d67130 100644 --- a/lib/screens/profile/shared/add_for_empty_search.dart +++ b/lib/screens/profile/shared/add_for_empty_search.dart @@ -11,7 +11,10 @@ class EmptyWidget extends StatelessWidget { final Function()? onpressed; final String title; const EmptyWidget( - {super.key, required this.controller, required this.onpressed,required this.title}); + {super.key, + required this.controller, + required this.onpressed, + required this.title}); @override Widget build(BuildContext context) { @@ -36,15 +39,13 @@ class EmptyWidget extends StatelessWidget { context: context, builder: (BuildContext context) { return AlertDialog( - title: Text(title), + title: Text(title), content: SizedBox( height: 130, child: Column( children: [ TextFormField( - inputFormatters: [ - UpperCaseTextFormatter() - ], + inputFormatters: [UpperCaseTextFormatter()], controller: controller, decoration: normalTextFieldStyle("", ""), ), @@ -65,8 +66,8 @@ class EmptyWidget extends StatelessWidget { ); }); }, - child: Text(title)) + child: Text(title)) ]), ); } -} \ No newline at end of file +} diff --git a/lib/screens/superadmin/agency.dart/agency_screen.dart b/lib/screens/superadmin/agency.dart/agency_screen.dart new file mode 100644 index 0000000..909f387 --- /dev/null +++ b/lib/screens/superadmin/agency.dart/agency_screen.dart @@ -0,0 +1,267 @@ +import 'package:app_popup_menu/app_popup_menu.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:searchfield/searchfield.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/agency/agency_bloc.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/object/object_bloc.dart'; +import 'package:unit2/model/utils/agency.dart'; +import 'package:unit2/model/utils/category.dart'; +import 'package:unit2/screens/superadmin/role/shared_pop_up_menu.dart'; +import 'package:unit2/utils/global_context.dart'; +import 'package:unit2/widgets/error_state.dart'; +import '../../../theme-data.dart/box_shadow.dart'; +import '../../../theme-data.dart/btn-style.dart'; +import '../../../theme-data.dart/colors.dart'; +import '../../../theme-data.dart/form-style.dart'; +import '../../../utils/alerts.dart'; +import '../../../utils/formatters.dart'; +import '../../../utils/global.dart'; +import '../../../widgets/Leadings/add_leading.dart'; +import '../../../widgets/empty_data.dart'; + +class RbacAgencyScreen extends StatelessWidget { + final int id; + const RbacAgencyScreen({super.key, required this.id}); + + @override + Widget build(BuildContext context) { + final formKey = GlobalKey(); + List agencyCategory = []; + Category? selectedAgencyCategory; + bool? isPrivate; + final agencyCategoryFocusNode = FocusNode(); + BuildContext parent; + return Scaffold( + appBar: AppBar( + backgroundColor: primary, + title: const Text("Agencies"), + actions: [ + AddLeading(onPressed: () { + parent = context; + showDialog( + context: NavigationService.navigatorKey.currentContext!, + builder: (BuildContext context) { + return AlertDialog( + title: const Text("Add Agency"), + content: FormBuilder( + key: formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "name", + decoration: normalTextFieldStyle( + "Agency name ", "Agency name"), + ), + const SizedBox( + height: 12, + ), + SearchField( + focusNode: agencyCategoryFocusNode, + itemHeight: 80, + suggestions: agencyCategory + .map((Category category) => + SearchFieldListItem(category.name!, + item: category, + child: ListTile( + title: Text(category.name!), + subtitle: Text( + category.industryClass!.name!), + ))) + .toList(), + emptyWidget: Container( + height: 100, + decoration: box1(), + child: const Center( + child: Text("No result found ...")), + ), + onSuggestionTap: (agencyCategory) { + selectedAgencyCategory = agencyCategory.item; + agencyCategoryFocusNode.unfocus(); + }, + searchInputDecoration: + normalTextFieldStyle("Category *", "") + .copyWith( + suffixIcon: IconButton( + icon: const Icon(Icons.arrow_drop_down), + onPressed: () { + agencyCategoryFocusNode.unfocus(); + }, + )), + validator: (value) { + if (value!.isEmpty) { + return "This field is required"; + } + return null; + }, + ), + FormBuilderRadioGroup( + decoration: InputDecoration( + border: InputBorder.none, + label: Row( + children: [ + Text( + "Is this private sector? ", + style: Theme.of(context) + .textTheme + .headlineSmall! + .copyWith(fontSize: 24), + ), + const Icon(FontAwesome.help_circled) + ], + ), + ), + + ////onvhange private sector + onChanged: (value) { + if (value.toString() == "YES") { + isPrivate = true; + } else { + isPrivate = false; + } + }, + + name: 'isPrivate', + validator: FormBuilderValidators.required(), + options: ["YES", "NO"] + .map((lang) => + FormBuilderFieldOption(value: lang)) + .toList(growable: false), + ), + const SizedBox( + height: 12, + ), + SizedBox( + height: 50, + width: double.maxFinite, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState! + .saveAndValidate()) { + String name = + formKey.currentState!.value['name']; + parent.read().add(AddAgency( + agency: Agency( + category: selectedAgencyCategory, + id: null, + name: name, + privateEntity: isPrivate))); + Navigator.pop(context); + } + + }, + child: const Text("Add")), + ) + ], + )), + ); + }); + }) + ], + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocConsumer( + listener: (context, state) { + if (state is AgencyLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is AgenciesLoaded || + state is AgencyAddesState || + state is AgencyErrorState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + + ////Deleted State + if (state is AgencyDeletedState) { + if (state.success) { + successAlert(context, "Delete Successfull!", + "Agency Deleted Successfully", () { + Navigator.of(context).pop(); + context.read().add(GetAgencies()); + }); + } else { + errorAlert(context, "Delete Failed", "Object Delete Failed", + () { + Navigator.of(context).pop(); + context.read().add(GetObjects()); + }); + } + } + }, + builder: (context, state) { + final parent = context; + if (state is AgenciesLoaded) { + agencyCategory = state.agencyCategory; + if (state.agencies.isNotEmpty) { + return ListView.builder( + padding: + const EdgeInsets.symmetric(vertical: 8, horizontal: 10), + itemCount: state.agencies.length, + itemBuilder: (BuildContext context, int index) { + return Column( + children: [ + Container( + width: screenWidth, + decoration: box1(), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + child: Expanded( + child: Row( + children: [ + CircleAvatar( + child: Text('${index + 1}'), + ), + const SizedBox( + width: 12, + ), + Flexible( + child: Text(state.agencies[index].name!, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: FontWeight.w500, + color: primary)), + ), + ], + )), + ), + const SizedBox( + height: 5, + ) + ], + ); + }); + } else { + return const EmptyData( + message: "No Object available. Please click + to add."); + } + } + if (state is AgencyErrorState) { + return SomethingWentWrong( + message: state.message, + onpressed: () { + context.read().add(GetObjects()); + }); + } + return Container(); + }, + ), + ), + ); + } +} diff --git a/lib/screens/superadmin/module/module_screen.dart b/lib/screens/superadmin/module/module_screen.dart new file mode 100644 index 0000000..7520f77 --- /dev/null +++ b/lib/screens/superadmin/module/module_screen.dart @@ -0,0 +1,373 @@ +import 'package:app_popup_menu/app_popup_menu.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/module/module_bloc.dart'; +import 'package:unit2/screens/superadmin/role/shared_pop_up_menu.dart'; +import 'package:unit2/widgets/Leadings/add_leading.dart'; +import 'package:unit2/widgets/error_state.dart'; +import '../../../theme-data.dart/box_shadow.dart'; +import '../../../theme-data.dart/btn-style.dart'; +import '../../../theme-data.dart/colors.dart'; +import '../../../theme-data.dart/form-style.dart'; +import '../../../utils/alerts.dart'; +import '../../../utils/global.dart'; +import '../../../widgets/empty_data.dart'; + +class RbacModuleScreen extends StatelessWidget { + final int id; + const RbacModuleScreen({super.key, required this.id}); + @override + Widget build(BuildContext context) { + final formKey = GlobalKey(); + return Scaffold( + appBar: AppBar( + backgroundColor: primary, + title: const Text("Module Screen"), + actions: [ + AddLeading(onPressed: () { + BuildContext parent = context; + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text("Add New Module"), + content: FormBuilder( + key: formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + FormBuilderTextField( + name: "object_name", + decoration: normalTextFieldStyle( + "Module name *", "Module name "), + validator: FormBuilderValidators.required( + errorText: "This field is required"), + ), + const SizedBox( + height: 8, + ), + FormBuilderTextField( + name: "slug", + decoration: normalTextFieldStyle("Slug ", "Slug"), + ), + const SizedBox( + height: 8, + ), + FormBuilderTextField( + validator: FormBuilderValidators.maxLength(50, + errorText: "Max characters only 50"), + name: "shorthand", + decoration: + normalTextFieldStyle("Shorthand ", "Shorthand"), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState! + .saveAndValidate()) { + String name = formKey + .currentState!.value['object_name']; + String? slug = + formKey.currentState!.value['slug']; + String? short = formKey + .currentState!.value['shorthand']; + parent.read().add( + AddRbacModule( + id: id, + name: name, + shorthand: short, + slug: slug)); + Navigator.pop(context); + } + }, + child: const Text("Add"))), + ], + ), + ), + ); + }); + }) + ], + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocConsumer( + listener: (context, state) { + if (state is ModuleLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is ModuleLoaded || + state is ModuleErrorState || + state is ModuleAddedState || + state is ModuleDeletedState || + state is ModuleUpdatedState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + ////Added State + if (state is ModuleAddedState) { + if (state.response['success']) { + successAlert( + context, "Adding Successfull!", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetModule()); + }); + } else { + errorAlert(context, "Adding Failed", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetModule()); + }); + } + } + ////Updated state + if (state is ModuleUpdatedState) { + if (state.response['success']) { + successAlert( + context, "Updated Successfull!", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetModule()); + }); + } else { + errorAlert(context, "Update Failed", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetModule()); + }); + } + } + ////Deleted State + if (state is ModuleDeletedState) { + if (state.success) { + successAlert(context, "Delete Successfull!", + "Module Deleted Successfully", () { + Navigator.of(context).pop(); + context.read().add(GetModule()); + }); + } else { + errorAlert(context, "Delete Failed", "Module Delete Failed", + () { + Navigator.of(context).pop(); + context.read().add(GetModule()); + }); + } + } + }, + builder: (context, state) { + final parent = context; + if (state is ModuleLoaded) { + if (state.module.isNotEmpty) { + return ListView.builder( + padding: + const EdgeInsets.symmetric(vertical: 8, horizontal: 10), + itemCount: state.module.length, + itemBuilder: (BuildContext context, int index) { + return Column( + children: [ + Container( + width: screenWidth, + decoration: box1(), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + child: Row( + children: [ + Expanded( + child: Row( + children: [ + CircleAvatar(child: Text('${index+1}'),), + const SizedBox(width: 12,), + Text(state.module[index].name!, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: FontWeight.w500, + color: primary)), + ], + )), + AppPopupMenu( + offset: const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + if (value == 2) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text("Update Module"), + content: FormBuilder( + key: formKey, + child: Column( + mainAxisSize: + MainAxisSize.min, + children: [ + FormBuilderTextField( + initialValue: state + .module[index].name, + name: "object_name", + decoration: + normalTextFieldStyle( + "Module name *", + "Module name "), + validator: + FormBuilderValidators + .required( + errorText: + "This field is required"), + ), + const SizedBox( + height: 8, + ), + FormBuilderTextField( + initialValue: state + .module[index].slug, + name: "slug", + decoration: + normalTextFieldStyle( + "Slug ", "Slug"), + ), + const SizedBox( + height: 8, + ), + FormBuilderTextField( + initialValue: state + .module[index] + .shorthand, + validator: + FormBuilderValidators + .maxLength(50, + errorText: + "Max characters only 50"), + name: "shorthand", + decoration: + normalTextFieldStyle( + "Shorthand ", + "Shorthand"), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + style: mainBtnStyle( + primary, + Colors + .transparent, + second), + onPressed: () { + if (formKey + .currentState! + .saveAndValidate()) { + String name = formKey + .currentState! + .value[ + 'object_name']; + String? slug = formKey + .currentState! + .value['slug']; + String? short = formKey + .currentState! + .value[ + 'shorthand']; + + ////Update + parent.read().add(UpdateRbacModule( + moduleId: state + .module[ + index] + .id!, + name: name, + slug: slug, + short: + short, + createdBy: state + .module[ + index] + .createdBy + ?.id, + updatedBy: + id)); + Navigator.pop( + context); + } + }, + child: const Text( + "Update"))), + ], + ), + ), + ); + }); + } + if (value == 1) { + ////delete + confirmAlert(context, () { + context.read().add( + DeleteRbacModule( + moduleId: + state.module[index].id!)); + }, "Delete?", "Confirm Delete?"); + } + }, + menuItems: [ + popMenuItem( + text: "Update", + value: 2, + icon: Icons.edit), + popMenuItem( + text: "Remove", + value: 1, + icon: Icons.delete), + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ), + ], + ), + ), + const SizedBox( + height: 5, + ) + ], + ); + }); + } else { + return const EmptyData( + message: "No Role available. Please click + to add."); + } + } + if (state is ModuleErrorState) { + return SomethingWentWrong( + message: state.message, + onpressed: () { + context.read().add(GetModule()); + }); + } + return Container(); + }, + ), + ), + ); + } +} diff --git a/lib/screens/superadmin/module_objects/module_objects_screen.dart b/lib/screens/superadmin/module_objects/module_objects_screen.dart new file mode 100644 index 0000000..30ff430 --- /dev/null +++ b/lib/screens/superadmin/module_objects/module_objects_screen.dart @@ -0,0 +1,298 @@ +import 'package:app_popup_menu/app_popup_menu.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:group_list_view/group_list_view.dart'; +import 'package:multi_dropdown/models/value_item.dart'; +import 'package:multi_dropdown/multiselect_dropdown.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/module/module_bloc.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/module_objects/module_objects_bloc.dart'; +import 'package:unit2/widgets/Leadings/add_leading.dart'; +import 'package:unit2/widgets/error_state.dart'; +import '../../../model/rbac/rbac.dart'; +import '../../../theme-data.dart/btn-style.dart'; +import '../../../theme-data.dart/colors.dart'; +import '../../../theme-data.dart/form-style.dart'; +import '../../../utils/alerts.dart'; +import '../../../utils/global_context.dart'; +import '../../../widgets/empty_data.dart'; + +class RbacModuleObjectsScreen extends StatelessWidget { + final int id; + const RbacModuleObjectsScreen({super.key, required this.id}); + + @override + Widget build(BuildContext context) { + final parent = context; + Map> moduleObjects = {}; + List modules = []; + List objects = []; + RBAC? selectedModule; + List valueItemObjects = []; + List selectedValueItemObjects = []; + final formKey = GlobalKey(); + return Scaffold( + appBar: AppBar( + backgroundColor: primary, + title: const Text("Module Object Screen"), + actions: [ + AddLeading(onPressed: () { + showDialog( + context: NavigationService.navigatorKey.currentState!.context, + builder: (BuildContext context) { + valueItemObjects = objects.map((e) { + return ValueItem(label: e.name!, value: e.name); + }).toList(); + return AlertDialog( + title: const Text("Add New Module Object"), + content: FormBuilder( + key: formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + FormBuilderDropdown( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "module", + decoration: + normalTextFieldStyle("Module", "Module"), + items: modules.isEmpty + ? [] + : modules.map((e) { + return DropdownMenuItem( + value: e, child: Text(e.name!)); + }).toList(), + onChanged: (RBAC? object) { + selectedModule = object; + }, + ), + const SizedBox( + height: 12, + ), + MultiSelectDropDown( + onOptionSelected: + (List selectedOptions) { + selectedValueItemObjects = selectedOptions; + }, + borderColor: Colors.grey, + borderWidth: 1, + borderRadius: 5, + hint: "Objects", + padding: const EdgeInsets.all(8), + options: valueItemObjects, + selectionType: SelectionType.multi, + chipConfig: + const ChipConfig(wrapType: WrapType.wrap), + dropdownHeight: 300, + optionTextStyle: const TextStyle(fontSize: 16), + selectedOptionIcon: + const Icon(Icons.check_circle), + ), + const SizedBox( + height: 12, + ), + SizedBox( + height: 50, + width: double.maxFinite, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState! + .saveAndValidate() && + selectedValueItemObjects.isNotEmpty) { + int assignerId = id; + int moduleId = selectedModule!.id!; + List objectId = []; + for (var object in objects) { + selectedValueItemObjects + .forEach((element) { + if (element.label.toLowerCase() == + object.name?.toLowerCase()) { + objectId.add(object.id!); + } + }); + } + Navigator.of(context).pop(); + parent.read().add( + AddRbacModuleObjects( + assignerId: assignerId, + moduleId: moduleId, + objectsId: objectId)); + } + }, + child: const Text("Submit")), + ) + ], + )), + ); + }); + }) + ], + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocConsumer( + listener: (context, state) { + print(state); + if (state is ModuleObjectLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is ModuleObjectLoadedState || + state is ModuleObjectsErrorState || + state is ModuleObjectAddedState || + state is ModuleObjectDeletedState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + + ////Deleted State + if (state is ModuleObjectDeletedState) { + if (state.success) { + successAlert( + context, "Delete Successfull!", "Role Deleted Successfully", + () { + Navigator.of(context).pop(); + context.read().add(GetModuleObjects()); + }); + } else { + errorAlert(context, "Delete Failed", "Role Delete Failed", () { + Navigator.of(context).pop(); + context.read().add(GetModuleObjects()); + }); + } + } + ////Added State + if (state is ModuleObjectAddedState) { + if (state.response['success']) { + successAlert( + context, "Adding Successfull!", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetModuleObjects()); + }); + } else { + errorAlert(context, "Adding Failed", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetModuleObjects()); + }); + } + } + }, + builder: (context, state) { + if (state is ModuleObjectLoadedState) { + modules = state.modules; + objects = state.objecs; + moduleObjects = {}; + if (state.moduleObjects.isNotEmpty) { + for (var modObjects in state.moduleObjects) { + if (!moduleObjects.keys + .contains(modObjects.module.name!.toLowerCase())) { + moduleObjects + .addAll({modObjects.module.name!.toLowerCase(): []}); + moduleObjects[modObjects.module.name!.toLowerCase()]!.add( + Content( + id: modObjects.id, name: modObjects.object.name!)); + } else { + moduleObjects[modObjects.module.name!.toLowerCase()]!.add( + Content( + id: modObjects.id, name: modObjects.object.name!)); + } + } + } + + if (state.moduleObjects.isNotEmpty) { + return GroupListView( + sectionsCount: moduleObjects.keys.toList().length, + countOfItemInSection: (int section) { + return moduleObjects.values.toList()[section].length; + }, + separatorBuilder: (context, index) { + return const Divider(); + }, + itemBuilder: (BuildContext context, IndexPath index) { + return ListTile( + trailing: IconButton( + color: Colors.grey.shade600, + icon: const Icon(Icons.delete), + onPressed: () { + confirmAlert(context, () { + context.read().add( + DeleteRbacModuleObject( + moduleObjectId: moduleObjects.values + .toList()[index.section][index.index] + .id)); + }, "Delete?", "Confirm Delete?"); + }, + ), + title: Row( + children: [ + CircleAvatar( + child: Text("${index.index + 1}", + style: Theme.of(context) + .textTheme + .labelLarge! + .copyWith(color: Colors.white))), + const SizedBox( + width: 20, + ), + Expanded( + child: Text( + moduleObjects.values + .toList()[index.section][index.index] + .name + .toUpperCase(), + style: Theme.of(context) + .textTheme + .labelLarge! + .copyWith( + fontWeight: FontWeight.w600, + color: primary), + ), + ), + ], + ), + ); + }, + groupHeaderBuilder: (BuildContext context, int section) { + return ListTile( + tileColor: second, + title: Text( + moduleObjects.keys.toList()[section].toUpperCase(), + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(color: Colors.white), + ), + ); + }, + ); + } else { + return const EmptyData( + message: "No Role available. Please click + to add."); + } + } + if (state is ModuleObjectsErrorState) { + return SomethingWentWrong( + message: state.message, onpressed: () {}); + } + return Container(); + }, + ), + ), + ); + } +} + +class Content { + final int id; + final String name; + const Content({required this.id, required this.name}); +} diff --git a/lib/screens/superadmin/object/object_screen.dart b/lib/screens/superadmin/object/object_screen.dart new file mode 100644 index 0000000..98edea4 --- /dev/null +++ b/lib/screens/superadmin/object/object_screen.dart @@ -0,0 +1,376 @@ +import 'package:app_popup_menu/app_popup_menu.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/object/object_bloc.dart'; +import 'package:unit2/screens/superadmin/role/shared_pop_up_menu.dart'; +import 'package:unit2/widgets/error_state.dart'; +import '../../../theme-data.dart/box_shadow.dart'; +import '../../../theme-data.dart/btn-style.dart'; +import '../../../theme-data.dart/colors.dart'; +import '../../../theme-data.dart/form-style.dart'; +import '../../../utils/alerts.dart'; +import '../../../utils/global.dart'; +import '../../../widgets/Leadings/add_leading.dart'; +import '../../../widgets/empty_data.dart'; + +class RbacObjectScreen extends StatelessWidget { + final int id; + const RbacObjectScreen({super.key, required this.id}); + + @override + Widget build(BuildContext context) { + final formKey = GlobalKey(); + return Scaffold( + appBar: AppBar( + backgroundColor: primary, + title: const Text("Objects Screen"), + actions: [ + AddLeading(onPressed: () { + BuildContext parent = context; + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text("Add New Object"), + content: FormBuilder( + key: formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + FormBuilderTextField( + name: "object_name", + decoration: normalTextFieldStyle( + "Object name *", "Object name "), + validator: FormBuilderValidators.required( + errorText: "This field is required"), + ), + const SizedBox( + height: 8, + ), + FormBuilderTextField( + name: "slug", + decoration: normalTextFieldStyle("Slug ", "Slug"), + ), + const SizedBox( + height: 8, + ), + FormBuilderTextField( + validator: FormBuilderValidators.maxLength(50, + errorText: "Max characters only 50"), + name: "shorthand", + decoration: + normalTextFieldStyle("Shorthand ", "Shorthand"), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState! + .saveAndValidate()) { + String name = formKey + .currentState!.value['object_name']; + String? slug = + formKey.currentState!.value['slug']; + String? short = formKey + .currentState!.value['shorthand']; + parent.read().add( + AddRbacObject( + id: id, + name: name, + shorthand: short, + slug: slug)); + Navigator.pop(context); + } + }, + child: const Text("Add"))), + ], + ), + ), + ); + }); + }) + ], + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocConsumer( + listener: (context, state) { + if (state is ObjectLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is ObjectLoaded || + state is ObjectAddedState || + state is ObjectErrorState || + state is ObjectUpdatedState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + ////Added State + if (state is ObjectAddedState) { + if (state.response['success']) { + successAlert( + context, "Adding Successfull!", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetObjects()); + }); + } else { + errorAlert(context, "Adding Failed", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetObjects()); + }); + } + } + ////Updated state + if (state is ObjectUpdatedState) { + if (state.response['success']) { + successAlert( + context, "Updated Successfull!", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetObjects()); + }); + } else { + errorAlert(context, "Update Failed", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetObjects()); + }); + } + } + ////Deleted State + if (state is ObjectDeletedState) { + if (state.success) { + successAlert(context, "Delete Successfull!", + "Object Deleted Successfully", () { + Navigator.of(context).pop(); + context.read().add(GetObjects()); + }); + } else { + errorAlert(context, "Delete Failed", "Object Delete Failed", + () { + Navigator.of(context).pop(); + context.read().add(GetObjects()); + }); + } + } + }, + builder: (context, state) { + final parent = context; + if (state is ObjectLoaded) { + if (state.objects.isNotEmpty) { + return ListView.builder( + padding: + const EdgeInsets.symmetric(vertical: 8, horizontal: 10), + itemCount: state.objects.length, + itemBuilder: (BuildContext context, int index) { + return Column( + children: [ + Container( + width: screenWidth, + decoration: box1(), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + child: Row( + children: [ + Expanded( + child: Row( + children: [ + CircleAvatar( + child: Text('${index + 1}'), + ), + const SizedBox( + width: 12, + ), + Text(state.objects[index].name!, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: FontWeight.w500, + color: primary)), + ], + )), + AppPopupMenu( + offset: const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + if (value == 2) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: + const Text("Update Object"), + content: FormBuilder( + key: formKey, + child: Column( + mainAxisSize: + MainAxisSize.min, + children: [ + FormBuilderTextField( + initialValue: state + .objects[index].name, + name: "object_name", + decoration: + normalTextFieldStyle( + "Object name *", + "Object name "), + validator: + FormBuilderValidators + .required( + errorText: + "This field is required"), + ), + const SizedBox( + height: 8, + ), + FormBuilderTextField( + initialValue: state + .objects[index].slug, + name: "slug", + decoration: + normalTextFieldStyle( + "Slug ", "Slug"), + ), + const SizedBox( + height: 8, + ), + FormBuilderTextField( + initialValue: state + .objects[index] + .shorthand, + validator: + FormBuilderValidators + .maxLength(50, + errorText: + "Max characters only 50"), + name: "shorthand", + decoration: + normalTextFieldStyle( + "Shorthand ", + "Shorthand"), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + style: mainBtnStyle( + primary, + Colors + .transparent, + second), + onPressed: () { + if (formKey + .currentState! + .saveAndValidate()) { + String name = formKey + .currentState! + .value[ + 'object_name']; + String? slug = formKey + .currentState! + .value['slug']; + String? short = formKey + .currentState! + .value[ + 'shorthand']; + + parent.read().add(UpdateRbacObject( + objectId: state + .objects[ + index] + .id!, + name: name, + slug: slug, + short: + short, + createdBy: state + .objects[ + index] + .createdBy + ?.id, + updatedBy: + id)); + Navigator.pop( + context); + } + }, + child: const Text( + "Update"))), + ], + ), + ), + ); + }); + } + if (value == 1) { + confirmAlert(context, () { + context.read().add( + DeleteRbacObject( + objectId: + state.objects[index].id!)); + }, "Delete?", "Confirm Delete?"); + } + }, + menuItems: [ + popMenuItem( + text: "Update", + value: 2, + icon: Icons.edit), + popMenuItem( + text: "Remove", + value: 1, + icon: Icons.delete), + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ), + ], + ), + ), + const SizedBox( + height: 5, + ) + ], + ); + }); + } else { + return const EmptyData( + message: "No Object available. Please click + to add."); + } + } + if (state is ObjectErrorState) { + return SomethingWentWrong( + message: state.message, + onpressed: () { + context.read().add(GetObjects()); + }); + } + return Container(); + }, + ), + ), + ); + } +} diff --git a/lib/screens/superadmin/operation/operation_screen.dart b/lib/screens/superadmin/operation/operation_screen.dart new file mode 100644 index 0000000..f9a9f8d --- /dev/null +++ b/lib/screens/superadmin/operation/operation_screen.dart @@ -0,0 +1,375 @@ +import 'package:app_popup_menu/app_popup_menu.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/operation/operation_bloc.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/role/role_bloc.dart'; +import 'package:unit2/screens/superadmin/role/shared_pop_up_menu.dart'; +import 'package:unit2/widgets/error_state.dart'; +import '../../../theme-data.dart/box_shadow.dart'; +import '../../../theme-data.dart/btn-style.dart'; +import '../../../theme-data.dart/colors.dart'; +import '../../../theme-data.dart/form-style.dart'; +import '../../../utils/alerts.dart'; +import '../../../utils/global.dart'; +import '../../../widgets/Leadings/add_leading.dart'; +import '../../../widgets/empty_data.dart'; + +class RbacOperationScreen extends StatelessWidget { + final int id; + const RbacOperationScreen({super.key, required this.id}); + + @override + Widget build(BuildContext context) { + final formKey = GlobalKey(); + return Scaffold( + appBar: AppBar( + backgroundColor: primary, + title: const Text("Operations Screen"), + actions: [ + AddLeading(onPressed: () { + BuildContext parent = context; + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text("Add New Operation"), + content: FormBuilder( + key: formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + FormBuilderTextField( + name: "object_name", + decoration: normalTextFieldStyle( + "Operation name *", "Operation name "), + validator: FormBuilderValidators.required( + errorText: "This field is required"), + ), + const SizedBox( + height: 8, + ), + FormBuilderTextField( + name: "slug", + decoration: normalTextFieldStyle("Slug ", "Slug"), + ), + const SizedBox( + height: 8, + ), + FormBuilderTextField( + validator: FormBuilderValidators.maxLength(50, + errorText: "Max characters only 50"), + name: "shorthand", + decoration: + normalTextFieldStyle("Shorthand ", "Shorthand"), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState! + .saveAndValidate()) { + String name = formKey + .currentState!.value['object_name']; + String? slug = + formKey.currentState!.value['slug']; + String? short = formKey + .currentState!.value['shorthand']; + parent.read().add( + AddRbacOperation( + id: id, + name: name, + shorthand: short, + slug: slug)); + Navigator.pop(context); + } + }, + child: const Text("Add"))), + ], + ), + ), + ); + }); + }) + ], + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocConsumer( + listener: (context, state) { + if (state is OperationLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is OperationsLoaded || + state is OperationErrorState || + state is OperationAddedState || + state is OperationUpdatedState || + state is OperationDeletedState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + ////Added State + if (state is OperationAddedState) { + if (state.response['success']) { + successAlert( + context, "Adding Successfull!", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetOperations()); + }); + } else { + errorAlert(context, "Adding Failed", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetOperations()); + }); + } + } + ////Updated state + if (state is OperationUpdatedState) { + if (state.response['success']) { + successAlert( + context, "Updated Successfull!", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetOperations()); + }); + } else { + errorAlert(context, "Update Failed", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetOperations()); + }); + } + } + ////Deleted State + if (state is OperationDeletedState) { + if (state.success) { + successAlert(context, "Delete Successfull!", + "Operation Deleted Successfully", () { + Navigator.of(context).pop(); + context.read().add(GetOperations()); + }); + } else { + errorAlert(context, "Delete Failed", "Operation Delete Failed", + () { + Navigator.of(context).pop(); + context.read().add(GetOperations()); + }); + } + } + }, + builder: (context, state) { + final parent = context; + if (state is OperationsLoaded) { + if (state.operations.isNotEmpty) { + return ListView.builder( + padding: + const EdgeInsets.symmetric(vertical: 8, horizontal: 10), + itemCount: state.operations.length, + itemBuilder: (BuildContext context, int index) { + return Column( + children: [ + Container( + width: screenWidth, + decoration: box1(), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + child: Row( + children: [ + Expanded( + child: Row( + children: [ + CircleAvatar(child: Text('${index+1}'),), + const SizedBox(width: 12,), + Text(state.operations[index].name!, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: FontWeight.w500, + color: primary)), + ], + )), + AppPopupMenu( + offset: const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + if (value == 2) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text("Update Operation"), + content: FormBuilder( + key: formKey, + child: Column( + mainAxisSize: + MainAxisSize.min, + children: [ + FormBuilderTextField( + initialValue: state + .operations[index] + .name, + name: "object_name", + decoration: + normalTextFieldStyle( + "Operation name *", + "Operation name "), + validator: + FormBuilderValidators + .required( + errorText: + "This field is required"), + ), + const SizedBox( + height: 8, + ), + FormBuilderTextField( + initialValue: state + .operations[index] + .slug, + name: "slug", + decoration: + normalTextFieldStyle( + "Slug ", "Slug"), + ), + const SizedBox( + height: 8, + ), + FormBuilderTextField( + initialValue: state + .operations[index] + .shorthand, + validator: + FormBuilderValidators + .maxLength(50, + errorText: + "Max characters only 50"), + name: "shorthand", + decoration: + normalTextFieldStyle( + "Shorthand ", + "Shorthand"), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + style: mainBtnStyle( + primary, + Colors + .transparent, + second), + onPressed: () { + if (formKey + .currentState! + .saveAndValidate()) { + String name = formKey + .currentState! + .value[ + 'object_name']; + String? slug = formKey + .currentState! + .value['slug']; + String? short = formKey + .currentState! + .value[ + 'shorthand']; + + parent.read().add(UpdateRbacOperation( + operationId: state + .operations[ + index] + .id!, + name: name, + slug: slug, + short: + short, + createdBy: state + .operations[ + index] + .createdBy + ?.id, + updatedBy: + id)); + Navigator.pop( + context); + } + }, + child: const Text( + "Update"))), + ], + ), + ), + ); + }); + } + if (value == 1) { + confirmAlert(context, () { + context.read().add( + DeleteRbacOperation( + operationId: state + .operations[index].id!)); + }, "Delete?", "Confirm Delete?"); + } + }, + menuItems: [ + popMenuItem( + text: "Update", + value: 2, + icon: Icons.edit), + popMenuItem( + text: "Remove", + value: 1, + icon: Icons.delete), + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ), + ], + ), + ), + const SizedBox( + height: 5, + ) + ], + ); + }); + } else { + return const EmptyData( + message: "No Role available. Please click + to add."); + } + } + if (state is OperationErrorState) { + return SomethingWentWrong( + message: state.toString(), + onpressed: () { + context.read().add(GetOperations()); + }); + } + return Container(); + }, + ), + ), + ); + } +} diff --git a/lib/screens/superadmin/permission/permission_screen.dart b/lib/screens/superadmin/permission/permission_screen.dart new file mode 100644 index 0000000..28e18c2 --- /dev/null +++ b/lib/screens/superadmin/permission/permission_screen.dart @@ -0,0 +1,283 @@ +import 'dart:math'; +import 'package:app_popup_menu/app_popup_menu.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:multi_dropdown/multiselect_dropdown.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/permission/permission_bloc.dart'; +import 'package:unit2/model/rbac/rbac.dart'; +import 'package:unit2/screens/superadmin/role/shared_pop_up_menu.dart'; +import 'package:unit2/theme-data.dart/btn-style.dart'; +import 'package:unit2/theme-data.dart/form-style.dart'; +import 'package:unit2/utils/global_context.dart'; +import 'package:unit2/widgets/Leadings/add_leading.dart'; +import 'package:unit2/widgets/error_state.dart'; +import '../../../theme-data.dart/box_shadow.dart'; +import '../../../theme-data.dart/colors.dart'; +import '../../../utils/alerts.dart'; +import '../../../utils/global.dart'; +import '../../../widgets/empty_data.dart'; + +class RbacPermissionScreen extends StatelessWidget { + final int id; + const RbacPermissionScreen({super.key, required this.id}); + + @override + Widget build(BuildContext context) { + List objects = []; + RBAC? selectedObject; + List operations = []; + List valueItemOperations = []; + List selectedValueItemOperations = []; + final formKey = GlobalKey(); + BuildContext? parent; + return Scaffold( + appBar: AppBar( + backgroundColor: primary, + title: const Text("Permissions Screen"), + actions: [ + AddLeading(onPressed: () { + showDialog( + context: NavigationService.navigatorKey.currentState!.context, + builder: (BuildContext context) { + valueItemOperations = operations.map((e) { + return ValueItem(label: e.name!, value: e.name); + }).toList(); + return AlertDialog( + title: const Text("Add Permission"), + content: FormBuilder( + key: formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + FormBuilderDropdown( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "object", + decoration: + normalTextFieldStyle("Permission", "Permission"), + items: objects.isEmpty + ? [] + : objects.map((e) { + return DropdownMenuItem( + value: e, child: Text(e.name!)); + }).toList(), + onChanged: (RBAC? object) { + selectedObject = object; + }, + ), + const SizedBox( + height: 12, + ), + MultiSelectDropDown( + onOptionSelected: + (List selectedOptions) { + selectedValueItemOperations = selectedOptions; + }, + borderColor: Colors.grey, + borderWidth: 1, + borderRadius: 5, + hint: "Operations", + padding: const EdgeInsets.all(8), + options: valueItemOperations, + selectionType: SelectionType.multi, + chipConfig: + const ChipConfig(wrapType: WrapType.wrap), + dropdownHeight: 300, + optionTextStyle: const TextStyle(fontSize: 16), + selectedOptionIcon: + const Icon(Icons.check_circle), + ), + const SizedBox( + height: 12, + ), + SizedBox( + height: 50, + width: double.maxFinite, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState! + .saveAndValidate() && + selectedValueItemOperations + .isNotEmpty) { + int assignerId = id; + int objectId = selectedObject!.id!; + + List opIds = []; + for (var operation in operations) { + selectedValueItemOperations + .forEach((element) { + if (element.label.toLowerCase() == + operation.name?.toLowerCase()) { + opIds.add(operation.id!); + } + }); + } + opIds.forEach((element) { + print(element); + }); + Navigator.pop(context); + parent!.read().add( + AddRbacPermission( + assignerId: assignerId, + objectId: objectId, + operationIds: opIds)); + } + }, + child: const Text("Submit")), + ) + ], + )), + ); + }); + }) + ], + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocConsumer( + listener: (context, state) { + if (state is PermissonLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is PermissionLoaded || + state is PermissionErrorState || + state is PermissionAddedState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + ////Added State + if (state is PermissionAddedState) { + if (state.response['success']) { + successAlert( + context, "Adding Successfull!", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetPermissions()); + }); + } else { + errorAlert(context, "Adding Failed", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetPermissions()); + }); + } + } + ////Deleted State + if (state is PermissionDeletedState) { + if (state.success) { + successAlert(context, "Delete Successfull!", + "Permission Deleted Successfully", () { + Navigator.of(context).pop(); + context.read().add(GetPermissions()); + }); + } else { + errorAlert(context, "Delete Failed", "Permission Delete Failed", + () { + Navigator.of(context).pop(); + context.read().add(GetPermissions()); + }); + } + } + }, + builder: (context, state) { + parent = context; + if (state is PermissionLoaded) { + objects = state.objects; + operations = state.operations; + if (state.permissions.isNotEmpty) { + return ListView.builder( + padding: + const EdgeInsets.symmetric(vertical: 8, horizontal: 10), + itemCount: state.permissions.length, + itemBuilder: (BuildContext context, int index) { + return Column( + children: [ + Container( + width: screenWidth, + decoration: box1(), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + child: Row( + children: [ + Expanded( + child: Row( + children: [ + CircleAvatar( + child: Text('${index + 1}'), + ), + const SizedBox( + width: 12, + ), + Flexible( + child: Text( + "${state.permissions[index].object?.name} - ${state.permissions[index].operation?.name}", + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: FontWeight.w500, + color: primary)), + ), + ], + )), + AppPopupMenu( + offset: const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + if (value == 1) { + + confirmAlert(context, () { + context.read().add( + DeleteRbacPermission( + permissionId: state.permissions[index].id!)); + }, "Delete?", "Confirm Delete?"); + + } + }, + menuItems: [ + + popMenuItem( + text: "Remove", + value: 1, + icon: Icons.delete), + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ), + ], + ), + ), + const SizedBox( + height: 5, + ) + ], + ); + }); + } else { + return const EmptyData( + message: "No Permission available. Please click + to add."); + } + } + if (state is PermissionErrorState) { + return SomethingWentWrong( + message: state.message, onpressed: () {}); + } + return Container(); + }, + ), + ), + ); + } +} diff --git a/lib/screens/superadmin/role/role_screen.dart b/lib/screens/superadmin/role/role_screen.dart new file mode 100644 index 0000000..5c65a71 --- /dev/null +++ b/lib/screens/superadmin/role/role_screen.dart @@ -0,0 +1,373 @@ +import 'package:app_popup_menu/app_popup_menu.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/role/role_bloc.dart'; +import 'package:unit2/screens/superadmin/role/shared_pop_up_menu.dart'; +import 'package:unit2/widgets/Leadings/add_leading.dart'; +import 'package:unit2/widgets/error_state.dart'; +import '../../../theme-data.dart/box_shadow.dart'; +import '../../../theme-data.dart/btn-style.dart'; +import '../../../theme-data.dart/colors.dart'; +import '../../../theme-data.dart/form-style.dart'; +import '../../../utils/alerts.dart'; +import '../../../utils/global.dart'; +import '../../../widgets/empty_data.dart'; + +class RbacRoleScreen extends StatelessWidget { + final int id; + const RbacRoleScreen({super.key, required this.id}); + + @override + Widget build(BuildContext context) { + final formKey = GlobalKey(); + return Scaffold( + appBar: AppBar( + centerTitle: true, + backgroundColor: primary, + title: const Text("Role Screen"), + actions: [ + AddLeading(onPressed: () { + BuildContext parent = context; + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text("Add New Role"), + content: FormBuilder( + key: formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + FormBuilderTextField( + name: "object_name", + decoration: normalTextFieldStyle( + "Role name *", "Role name "), + validator: FormBuilderValidators.required( + errorText: "This field is required"), + ), + const SizedBox( + height: 8, + ), + FormBuilderTextField( + name: "slug", + decoration: normalTextFieldStyle("Slug ", "Slug"), + ), + const SizedBox( + height: 8, + ), + FormBuilderTextField( + validator: FormBuilderValidators.maxLength(50, + errorText: "Max characters only 50"), + name: "shorthand", + decoration: + normalTextFieldStyle("Shorthand ", "Shorthand"), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState! + .saveAndValidate()) { + String name = formKey + .currentState!.value['object_name']; + String? slug = + formKey.currentState!.value['slug']; + String? short = formKey + .currentState!.value['shorthand']; + parent.read().add(AddRbacRole( + id: id, + name: name, + shorthand: short, + slug: slug)); + Navigator.pop(context); + } + }, + child: const Text("Add"))), + ], + ), + ), + ); + }); + }) + ], + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocConsumer( + listener: (context, state) { + if (state is RoleLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is RoleLoadedState || + state is RoleErrorState || + state is RoleAddedState || + state is RoleDeletedState || + state is RoleUpdatedState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + ////Added State + if (state is RoleAddedState) { + if (state.response['success']) { + successAlert( + context, "Adding Successfull!", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetRoles()); + }); + } else { + errorAlert(context, "Adding Failed", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetRoles()); + }); + } + } + ////Updated state + if (state is RoleUpdatedState) { + if (state.response['success']) { + successAlert( + context, "Updated Successfull!", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetRoles()); + }); + } else { + errorAlert(context, "Update Failed", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetRoles()); + }); + } + } + ////Deleted State + if (state is RoleDeletedState) { + if (state.success) { + successAlert( + context, "Delete Successfull!", "Role Deleted Successfully", + () { + Navigator.of(context).pop(); + context.read().add(GetRoles()); + }); + } else { + errorAlert(context, "Delete Failed", "Role Delete Failed", () { + Navigator.of(context).pop(); + context.read().add(GetRoles()); + }); + } + } + }, + builder: (context, state) { + final parent = context; + if (state is RoleLoadedState) { + if (state.roles.isNotEmpty) { + return ListView.builder( + padding: + const EdgeInsets.symmetric(vertical: 8, horizontal: 10), + itemCount: state.roles.length, + itemBuilder: (BuildContext context, int index) { + return Column( + children: [ + Container( + width: screenWidth, + decoration: box1(), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + child: Row( + children: [ + Expanded( + child: Row( + children: [ + CircleAvatar(child: Text('${index+1}'),), + const SizedBox(width: 12,), + Flexible( + child: Text(state.roles[index].name!, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: FontWeight.w500, + color: primary)), + ), + ], + )), + AppPopupMenu( + offset: const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + if (value == 2) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text("Update Role"), + content: FormBuilder( + key: formKey, + child: Column( + mainAxisSize: + MainAxisSize.min, + children: [ + FormBuilderTextField( + initialValue: state + .roles[index].name, + name: "object_name", + decoration: + normalTextFieldStyle( + "Role name *", + "Role name "), + validator: + FormBuilderValidators + .required( + errorText: + "This field is required"), + ), + const SizedBox( + height: 8, + ), + FormBuilderTextField( + initialValue: state + .roles[index].slug, + name: "slug", + decoration: + normalTextFieldStyle( + "Slug ", "Slug"), + ), + const SizedBox( + height: 8, + ), + FormBuilderTextField( + initialValue: state + .roles[index] + .shorthand, + validator: + FormBuilderValidators + .maxLength(50, + errorText: + "Max characters only 50"), + name: "shorthand", + decoration: + normalTextFieldStyle( + "Shorthand ", + "Shorthand"), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + style: mainBtnStyle( + primary, + Colors + .transparent, + second), + onPressed: () { + if (formKey + .currentState! + .saveAndValidate()) { + String name = formKey + .currentState! + .value[ + 'object_name']; + String? slug = formKey + .currentState! + .value['slug']; + String? short = formKey + .currentState! + .value[ + 'shorthand']; + + parent.read().add(UpdateRbacRole( + roleId: state + .roles[ + index] + .id!, + name: name, + slug: slug, + short: + short, + createdBy: state + .roles[ + index] + .createdBy + ?.id, + updatedBy: + id)); + Navigator.pop( + context); + } + }, + child: const Text( + "Update"))), + ], + ), + ), + ); + }); + } + if (value == 1) { + confirmAlert(context, () { + context.read().add( + DeleteRbacRole( + roleId: + state.roles[index].id!)); + }, "Delete?", "Confirm Delete?"); + } + }, + menuItems: [ + popMenuItem( + text: "Update", + value: 2, + icon: Icons.edit), + popMenuItem( + text: "Remove", + value: 1, + icon: Icons.delete), + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ), + ], + ), + ), + const SizedBox( + height: 5, + ) + ], + ); + }); + } else { + return const EmptyData( + message: "No Role available. Please click + to add."); + } + } + if (state is RoleErrorState) { + return SomethingWentWrong( + message: state.message, onpressed: () { + context.read().add(GetRoles()); + }); + } + return Container(); + }, + ), + ), + ); + } +} diff --git a/lib/screens/superadmin/role/shared_pop_up_menu.dart b/lib/screens/superadmin/role/shared_pop_up_menu.dart new file mode 100644 index 0000000..3359c56 --- /dev/null +++ b/lib/screens/superadmin/role/shared_pop_up_menu.dart @@ -0,0 +1,21 @@ +import 'package:app_popup_menu/app_popup_menu.dart'; +import 'package:flutter/material.dart'; + +PopupMenuItem popMenuItem({String? text, int? value, IconData? icon}) { + return PopupMenuItem( + value: value, + child: Row( + children: [ + Icon( + icon, + ), + const SizedBox( + width: 10, + ), + Text( + text!, + ), + ], + ), + ); +} \ No newline at end of file diff --git a/lib/screens/superadmin/role_assignment.dart/role_assignment_screen.dart b/lib/screens/superadmin/role_assignment.dart/role_assignment_screen.dart new file mode 100644 index 0000000..a354473 --- /dev/null +++ b/lib/screens/superadmin/role_assignment.dart/role_assignment_screen.dart @@ -0,0 +1,289 @@ +import 'package:app_popup_menu/app_popup_menu.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:fluttericon/font_awesome5_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:multi_dropdown/models/value_item.dart'; +import 'package:multi_dropdown/multiselect_dropdown.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/role/role_bloc.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/role_extend/role_extend_bloc.dart'; +import 'package:unit2/bloc/role_assignment/role_assignment_bloc.dart'; +import 'package:unit2/screens/superadmin/role/shared_pop_up_menu.dart'; +import 'package:unit2/widgets/Leadings/add_leading.dart'; +import 'package:unit2/widgets/error_state.dart'; +import '../../../model/rbac/rbac.dart'; +import '../../../theme-data.dart/box_shadow.dart'; +import '../../../theme-data.dart/btn-style.dart'; +import '../../../theme-data.dart/colors.dart'; +import '../../../theme-data.dart/form-style.dart'; +import '../../../utils/alerts.dart'; +import '../../../utils/global.dart'; +import '../../../widgets/empty_data.dart'; + +class RbacRoleAssignment extends StatelessWidget { + final int id; + const RbacRoleAssignment({super.key, required this.id}); + + @override + Widget build(BuildContext context) { + final formKey = GlobalKey(); + List roles = []; + List valueItemRoles = []; + List selectedValueItemRoles = []; + return Scaffold( + appBar: AppBar( + centerTitle: true, + backgroundColor: primary, + title: const Text("User Roles Screen"), + actions: [ + AddLeading(onPressed: () { + BuildContext parent = context; + showDialog( + context: context, + builder: (BuildContext context) { + valueItemRoles = roles.map((e) { + return ValueItem(label: e.name!, value: e.name); + }).toList(); + return AlertDialog( + title: const Text("Add New Role"), + content: FormBuilder( + key: formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + MultiSelectDropDown( + onOptionSelected: + (List selectedOptions) { + selectedValueItemRoles = selectedOptions; + }, + borderColor: Colors.grey, + borderWidth: 1, + borderRadius: 5, + hint: "Select Roles", + padding: const EdgeInsets.all(8), + options: valueItemRoles, + selectionType: SelectionType.multi, + chipConfig: + const ChipConfig(wrapType: WrapType.wrap), + dropdownHeight: 300, + optionTextStyle: const TextStyle(fontSize: 16), + selectedOptionIcon: const Icon(Icons.check_circle), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState! + .saveAndValidate()) { + List rolesId = []; + for (var role in roles) { + selectedValueItemRoles + .forEach((element) { + if (element.label.toLowerCase() == + role.name?.toLowerCase()) { + rolesId.add(role.id!); + } + }); + } + parent + .read() + .add(AssignRole( + assignerId: id, + roles: rolesId, + )); + Navigator.pop(context); + } + }, + child: const Text("Add"))), + ], + ), + ), + ); + }); + }) + ], + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocConsumer( + listener: (context, state) { + if (state is RoleAssignmentLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is AssignedRoleLoaded || + state is RoleAssignmentErrorState || + state is AssignedRoleDeletedState || + state is UserNotExistError || state is AssignedRoleAddedState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + ////Deleted State + if (state is AssignedRoleDeletedState) { + if (state.success) { + successAlert(context, "Delete Successfull!", + "Role Module Deleted Successfully", () { + Navigator.of(context).pop(); + context.read().add(LoadAssignedRole()); + }); + } else { + errorAlert( + context, "Delete Failed", "Role Module Delete Failed", () { + Navigator.of(context).pop(); + context.read().add(LoadAssignedRole()); + }); + } + } + + ////Added State + if (state is AssignedRoleAddedState) { + if (state.response['success']) { + successAlert( + context, "Adding Successfull!", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(LoadAssignedRole()); + }); + } else { + errorAlert(context, "Adding Failed", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(LoadAssignedRole()); + }); + } + } + }, + builder: (context, state) { + final parent = context; + if (state is AssignedRoleLoaded) { + + roles = state.roles; + if (state.assignedRoles.isNotEmpty) { + return Column( + children: [ + ListTile( + tileColor: second, + leading: const Icon( + FontAwesome5.user_alt, + color: Colors.white, + ), + title: Text(state.fullname.toUpperCase(), + style: Theme.of(context) + .textTheme + .titleLarge! + .copyWith(color: Colors.white)), + subtitle: Text("Person full name", + style: Theme.of(context) + .textTheme + .labelLarge! + .copyWith(color: Colors.white)), + ), + Expanded( + child: ListView.builder( + padding: const EdgeInsets.symmetric( + vertical: 8, horizontal: 10), + itemCount: state.assignedRoles.length, + itemBuilder: (BuildContext context, int index) { + return Column( + children: [ + Container( + width: screenWidth, + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + child: Row( + children: [ + Expanded( + child: Row( + children: [ + CircleAvatar( + child: Text('${index + 1}'), + ), + const SizedBox( + width: 12, + ), + Flexible( + child: Text( + state.assignedRoles[index].role! + .name, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight.w500, + color: primary)), + ), + ], + )), + AppPopupMenu( + offset: const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + if (value == 1) { + confirmAlert(context, () { + context + .read() + .add(DeleteAssignRole( + roleId: state + .assignedRoles[index].id! + )); + }, "Delete?", "Confirm Delete?"); + } + }, + menuItems: [ + popMenuItem( + text: "Remove", + value: 1, + icon: Icons.delete), + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ), + ], + ), + ), + const Divider(), + ], + ); + }), + ), + ], + ); + } else { + return const EmptyData( + message: "No Role available. Please click + to add."); + } + } + if (state is RoleAssignmentErrorState) { + return SomethingWentWrong( + message: state.message, + onpressed: () { + context.read().add(GetRoles()); + }); + } + if (state is UserNotExistError) { + return const Center( + child: Text("User Not Exsit"), + ); + } + return Container(); + }, + ), + ), + ); + } +} diff --git a/lib/screens/superadmin/role_extend/role_extend_screen.dart b/lib/screens/superadmin/role_extend/role_extend_screen.dart new file mode 100644 index 0000000..670c1d4 --- /dev/null +++ b/lib/screens/superadmin/role_extend/role_extend_screen.dart @@ -0,0 +1,296 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:group_list_view/group_list_view.dart'; +import 'package:multi_dropdown/multiselect_dropdown.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/role_extend/role_extend_bloc.dart'; +import 'package:unit2/widgets/Leadings/add_leading.dart'; +import 'package:unit2/widgets/error_state.dart'; +import '../../../model/rbac/rbac.dart'; +import '../../../theme-data.dart/btn-style.dart'; +import '../../../theme-data.dart/colors.dart'; +import '../../../theme-data.dart/form-style.dart'; +import '../../../utils/alerts.dart'; +import '../../../utils/global_context.dart'; +import '../../../widgets/empty_data.dart'; + +class RbacRoleExtendScreen extends StatelessWidget { + final int id; + const RbacRoleExtendScreen({super.key, required this.id}); + + @override + Widget build(BuildContext context) { + final parent = context; + Map> rolesExtend = {}; + List roles = []; + RBAC? selectedRole; + List valueItemRoles = []; + List selectedValueItemRoles = []; + final formKey = GlobalKey(); + return Scaffold( + appBar: AppBar( + elevation: 0, + backgroundColor: primary, + title: const Text("Role Extend"), + actions: [ + AddLeading(onPressed: () { + showDialog( + context: NavigationService.navigatorKey.currentState!.context, + builder: (BuildContext context) { + valueItemRoles = roles.map((e) { + return ValueItem(label: e.name!, value: e.name); + }).toList(); + return AlertDialog( + title: const Text("Add Role Extend"), + content: FormBuilder( + key: formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + FormBuilderDropdown( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "role", + decoration: normalTextFieldStyle( + "Role Extend Main", "Role Extend Main"), + items: roles.isEmpty + ? [] + : roles.map((e) { + return DropdownMenuItem( + value: e, child: Text(e.name!)); + }).toList(), + onChanged: (RBAC? role) { + selectedRole = role; + }, + ), + const SizedBox( + height: 12, + ), + MultiSelectDropDown( + onOptionSelected: + (List selectedOptions) { + selectedValueItemRoles = selectedOptions; + }, + borderColor: Colors.grey, + borderWidth: 1, + borderRadius: 5, + hint: "Roles Extend", + padding: const EdgeInsets.all(8), + options: valueItemRoles, + selectionType: SelectionType.multi, + chipConfig: + const ChipConfig(wrapType: WrapType.wrap), + dropdownHeight: 300, + optionTextStyle: const TextStyle(fontSize: 16), + selectedOptionIcon: + const Icon(Icons.check_circle), + ), + const SizedBox( + height: 12, + ), + SizedBox( + height: 50, + width: double.maxFinite, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState! + .saveAndValidate() && + selectedValueItemRoles.isNotEmpty) { + int roleId = selectedRole!.id!; + List rolesId = []; + for (var role in roles) { + selectedValueItemRoles + .forEach((element) { + if (element.label.toLowerCase() == + role.name?.toLowerCase()) { + rolesId.add(role.id!); + } + }); + } + Navigator.of(context).pop(); + rolesExtend = {}; + parent.read().add( + AddRoleExtend( + roleId: roleId, + roleExtendsId: rolesId)); + } + }, + child: const Text("Submit")), + ) + ], + )), + ); + }); + }) + ], + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocConsumer( + listener: (context, state) { + if (state is RoleExtendLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is RoleExtendLoadedState || + state is RoleExtendAddedState || + state is RoleExtendDeletedState || + state is RoleExtendErrorState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + + ////Deleted State + if (state is RoleExtendDeletedState) { + if (state.success) { + successAlert(context, "Delete Successfull!", + "Role Module Deleted Successfully", () { + Navigator.of(context).pop(); + context.read().add(GetRoleExtend()); + }); + } else { + errorAlert( + context, "Delete Failed", "Role Module Delete Failed", () { + Navigator.of(context).pop(); + context.read().add(GetRoleExtend()); + }); + } + } + ////Added State + if (state is RoleExtendAddedState) { + if (state.response['success']) { + successAlert( + context, "Adding Successfull!", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetRoleExtend()); + }); + } else { + errorAlert(context, "Adding Failed", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetRoleExtend()); + }); + } + } + }, + builder: (context, state) { + if (state is RoleExtendLoadedState) { + rolesExtend = {}; + roles = state.roles; + + if (state.rolesExtend.isNotEmpty) { + for (var roleExt in state.rolesExtend) { + if (!rolesExtend.keys + .contains(roleExt.roleExtendMain.name!.toLowerCase())) { + rolesExtend.addAll( + {roleExt.roleExtendMain.name!.toLowerCase(): []}); + rolesExtend[roleExt.roleExtendMain.name!.toLowerCase()]! + .add(Content( + id: roleExt.id, + name: roleExt.roleExtendChild.name!)); + } else { + rolesExtend[roleExt.roleExtendMain.name!.toLowerCase()]! + .add(Content( + id: roleExt.id, + name: roleExt.roleExtendChild.name!)); + } + } + } + + if (state.rolesExtend.isNotEmpty) { + return GroupListView( + padding: const EdgeInsets.all(0), + sectionsCount: rolesExtend.keys.toList().length, + countOfItemInSection: (int section) { + return rolesExtend.values.toList()[section].length; + }, + itemBuilder: (BuildContext context, IndexPath index) { + return ListTile( + dense: true, + trailing: IconButton( + color: Colors.grey.shade600, + icon: const Icon(Icons.delete), + onPressed: () { + confirmAlert(context, () { + context.read().add(DeleteRoleExtend( + roleExtendId: rolesExtend.values + .toList()[index.section][index.index] + .id)); + }, "Delete?", "Confirm Delete?"); + }, + ), + title: Row( + children: [ + CircleAvatar( + child: Text("${index.index + 1}", + style: Theme.of(context) + .textTheme + .labelLarge! + .copyWith(color: Colors.white))), + const SizedBox( + width: 20, + ), + Expanded( + child: Text( + rolesExtend.values + .toList()[index.section][index.index] + .name + .toUpperCase(), + style: Theme.of(context) + .textTheme + .labelLarge! + .copyWith( + fontWeight: FontWeight.w500, + color: primary), + ), + ), + ], + ), + ); + }, + separatorBuilder: (context, index) { + return const Divider(); + }, + groupHeaderBuilder: (BuildContext context, int section) { + return ListTile( + tileColor: second, + title: Text( + rolesExtend.keys.toList()[section].toUpperCase(), + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(color: Colors.white), + ), + ); + }, + ); + } else { + return const EmptyData( + message: "No Role available. Please click + to add."); + } + } + if (state is RoleExtendErrorState) { + return SomethingWentWrong( + message: state.message, onpressed: () {}); + } + return Container(); + }, + ), + ), + ); + } +} + +class Content { + final int id; + final String name; + const Content({required this.id, required this.name}); +} diff --git a/lib/screens/superadmin/role_module/role_module_scree.dart b/lib/screens/superadmin/role_module/role_module_scree.dart new file mode 100644 index 0000000..58521bf --- /dev/null +++ b/lib/screens/superadmin/role_module/role_module_scree.dart @@ -0,0 +1,289 @@ +import 'package:app_popup_menu/app_popup_menu.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:group_list_view/group_list_view.dart'; +import 'package:multi_dropdown/multiselect_dropdown.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/module_objects/module_objects_bloc.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/role_module/role_module_bloc.dart'; +import 'package:unit2/widgets/Leadings/add_leading.dart'; +import 'package:unit2/widgets/error_state.dart'; +import '../../../model/rbac/rbac.dart'; +import '../../../theme-data.dart/btn-style.dart'; +import '../../../theme-data.dart/colors.dart'; +import '../../../theme-data.dart/form-style.dart'; +import '../../../utils/alerts.dart'; +import '../../../utils/global_context.dart'; +import '../../../widgets/empty_data.dart'; + +class RbacRoleModuleScreen extends StatelessWidget { + final int id; + const RbacRoleModuleScreen({super.key, required this.id}); + + @override + Widget build(BuildContext context) { + final parent = context; + Map> roleModules = {}; + List modules = []; + List roles = []; + RBAC? selectedRole; + List valueItemModules = []; + List selectedValueItemModules = []; + final formKey = GlobalKey(); + return Scaffold( + appBar: AppBar( + backgroundColor: primary, + title: const Text("Role Modules Screen"), + actions: [ + AddLeading(onPressed: () { + showDialog( + context: NavigationService.navigatorKey.currentState!.context, + builder: (BuildContext context) { + valueItemModules = modules.map((e) { + return ValueItem(label: e.name!, value: e.name); + }).toList(); + return AlertDialog( + title: const Text("Add Role Module"), + content: FormBuilder( + key: formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + FormBuilderDropdown( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "role", + decoration: normalTextFieldStyle("Role", "Role"), + items: roles.isEmpty + ? [] + : roles.map((e) { + return DropdownMenuItem( + value: e, child: Text(e.name!)); + }).toList(), + onChanged: (RBAC? role) { + selectedRole = role; + }, + ), + const SizedBox( + height: 12, + ), + MultiSelectDropDown( + onOptionSelected: + (List selectedOptions) { + selectedValueItemModules = selectedOptions; + }, + borderColor: Colors.grey, + borderWidth: 1, + borderRadius: 5, + hint: "Modules", + padding: const EdgeInsets.all(8), + options: valueItemModules, + selectionType: SelectionType.multi, + chipConfig: + const ChipConfig(wrapType: WrapType.wrap), + dropdownHeight: 300, + optionTextStyle: const TextStyle(fontSize: 16), + selectedOptionIcon: + const Icon(Icons.check_circle), + ), + const SizedBox( + height: 12, + ), + SizedBox( + height: 50, + width: double.maxFinite, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState! + .saveAndValidate() && + selectedValueItemModules.isNotEmpty) { + int assignerId = id; + int roleId = selectedRole!.id!; + List modulesId = []; + for (var module in modules) { + selectedValueItemModules + .forEach((element) { + if (element.label.toLowerCase() == + module.name?.toLowerCase()) { + modulesId.add(module.id!); + } + }); + } + Navigator.of(context).pop(); + parent.read().add( + AddRoleModule( + assignerId: assignerId, + roleId: roleId, + moduleIds: modulesId)); + } + }, + child: const Text("Submit")), + ) + ], + )), + ); + }); + }) + ], + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocConsumer( + listener: (context, state) { + if (state is RoleModuleLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is RoleModuleLoadedState || + state is RoleModuleErrorState || + state is RoleModuleDeletedState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + + ////Deleted State + if (state is RoleModuleDeletedState) { + if (state.success) { + successAlert(context, "Delete Successfull!", + "Role Module Deleted Successfully", () { + Navigator.of(context).pop(); + context.read().add(GetRoleModules()); + }); + } else { + errorAlert( + context, "Delete Failed", "Role Module Delete Failed", () { + Navigator.of(context).pop(); + context.read().add(GetRoleModules()); + }); + } + } + ////Added State + if (state is RoleModuleAddedState) { + if (state.response['success']) { + successAlert( + context, "Adding Successfull!", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetRoleModules()); + }); + } else { + errorAlert(context, "Adding Failed", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetRoleModules()); + }); + } + } + }, + builder: (context, state) { + if (state is RoleModuleLoadedState) { + modules = state.modules; + roles = state.roles; + roleModules = {}; + if (state.roleModules.isNotEmpty) { + for (var roleMod in state.roleModules) { + if (!roleModules.keys + .contains(roleMod.role!.name!.toLowerCase())) { + roleModules.addAll({roleMod.role!.name!.toLowerCase(): []}); + roleModules[roleMod.role!.name!.toLowerCase()]!.add( + Content(id: roleMod.id!, name: roleMod.module!.name!)); + } else { + roleModules[roleMod.role!.name!.toLowerCase()]!.add( + Content(id: roleMod.id!, name: roleMod.module!.name!)); + } + } + } + + if (state.roleModules.isNotEmpty) { + return GroupListView( + sectionsCount: roleModules.keys.toList().length, + countOfItemInSection: (int section) { + return roleModules.values.toList()[section].length; + }, + itemBuilder: (BuildContext context, IndexPath index) { + return ListTile( + dense: true, + trailing: IconButton( + color: Colors.grey.shade600, + icon: const Icon(Icons.delete), + onPressed: () { + confirmAlert(context, () { + context.read().add(DeleteRoleModule( + roleModuleId: roleModules.values + .toList()[index.section][index.index] + .id)); + }, "Delete?", "Confirm Delete?"); + }, + ), + title: Row( + children: [ + CircleAvatar( + child: Text("${index.index + 1}", + style: Theme.of(context) + .textTheme + .labelLarge! + .copyWith(color: Colors.white))), + const SizedBox( + width: 20, + ), + Expanded( + child: Text( + roleModules.values + .toList()[index.section][index.index] + .name + .toUpperCase(), + style: Theme.of(context) + .textTheme + .labelLarge! + .copyWith(color: primary), + ), + ), + ], + ), + ); + }, + separatorBuilder: (context, index) { + return const Divider(); + }, + groupHeaderBuilder: (BuildContext context, int section) { + return ListTile( + tileColor: second, + title: Text( + roleModules.keys.toList()[section].toUpperCase(), + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(color: Colors.white), + ), + ); + }, + ); + } else { + return const EmptyData( + message: "No Role available. Please click + to add."); + } + } + if (state is RoleModuleErrorState) { + return SomethingWentWrong( + message: state.message, onpressed: () {}); + } + return Container(); + }, + ), + ), + ); + } +} + +class Content { + final int id; + final String name; + const Content({required this.id, required this.name}); +} diff --git a/lib/screens/superadmin/roles_under/roles_under_screen.dart b/lib/screens/superadmin/roles_under/roles_under_screen.dart new file mode 100644 index 0000000..a085fc5 --- /dev/null +++ b/lib/screens/superadmin/roles_under/roles_under_screen.dart @@ -0,0 +1,294 @@ +import 'package:app_popup_menu/app_popup_menu.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:group_list_view/group_list_view.dart'; +import 'package:multi_dropdown/multiselect_dropdown.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/role_module/role_module_bloc.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/roles_under/roles_under_bloc.dart'; +import 'package:unit2/widgets/Leadings/add_leading.dart'; +import 'package:unit2/widgets/error_state.dart'; +import '../../../model/rbac/rbac.dart'; +import '../../../theme-data.dart/btn-style.dart'; +import '../../../theme-data.dart/colors.dart'; +import '../../../theme-data.dart/form-style.dart'; +import '../../../utils/alerts.dart'; +import '../../../utils/global_context.dart'; +import '../../../widgets/empty_data.dart'; + +class RbacRoleUnderScreen extends StatelessWidget { + final int id; + const RbacRoleUnderScreen({super.key, required this.id}); + + @override + Widget build(BuildContext context) { + final parent = context; + Map> rolesUnder = {}; + List roles = []; + RBAC? selectedRole; + List valueItemRoles = []; + List selectedValueItemRoles = []; + final formKey = GlobalKey(); + return Scaffold( + appBar: AppBar( + backgroundColor: primary, + title: const Text("Role Module"), + actions: [ + AddLeading(onPressed: () { + showDialog( + context: NavigationService.navigatorKey.currentState!.context, + builder: (BuildContext context) { + valueItemRoles = roles.map((e) { + return ValueItem(label: e.name!, value: e.name); + }).toList(); + return AlertDialog( + title: const Text("Add Role Under"), + content: FormBuilder( + key: formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + FormBuilderDropdown( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "role", + decoration: normalTextFieldStyle( + "Main Role", "Main Role"), + items: roles.isEmpty + ? [] + : roles.map((e) { + return DropdownMenuItem( + value: e, child: Text(e.name!)); + }).toList(), + onChanged: (RBAC? role) { + selectedRole = role; + }, + ), + const SizedBox( + height: 12, + ), + MultiSelectDropDown( + onOptionSelected: + (List selectedOptions) { + selectedValueItemRoles = selectedOptions; + }, + borderColor: Colors.grey, + borderWidth: 1, + borderRadius: 5, + hint: "Roles Under", + padding: const EdgeInsets.all(8), + options: valueItemRoles, + selectionType: SelectionType.multi, + chipConfig: + const ChipConfig(wrapType: WrapType.wrap), + dropdownHeight: 300, + optionTextStyle: const TextStyle(fontSize: 16), + selectedOptionIcon: + const Icon(Icons.check_circle), + ), + const SizedBox( + height: 12, + ), + SizedBox( + height: 50, + width: double.maxFinite, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState! + .saveAndValidate() && + selectedValueItemRoles.isNotEmpty) { + int assignerId = id; + int roleId = selectedRole!.id!; + List rolesId = []; + for (var role in roles) { + selectedValueItemRoles + .forEach((element) { + if (element.label.toLowerCase() == + role.name?.toLowerCase()) { + rolesId.add(role.id!); + } + }); + } + Navigator.of(context).pop(); + parent.read().add( + AddRoleUnder( + roleId: roleId, + roleUnderIds: rolesId)); + } + }, + child: const Text("Submit")), + ) + ], + )), + ); + }); + }) + ], + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocConsumer( + listener: (context, state) { + if (state is RoleUnderLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is RoleUnderLoadedState || + state is RoleUnderErrorState || + state is RoleUnderDeletedState || + state is RoleUnderAddedState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + + ////Deleted State + if (state is RoleUnderDeletedState) { + if (state.success) { + successAlert(context, "Delete Successfull!", + "Role Module Deleted Successfully", () { + Navigator.of(context).pop(); + context.read().add(GetRolesUnder()); + }); + } else { + errorAlert( + context, "Delete Failed", "Role Module Delete Failed", () { + Navigator.of(context).pop(); + context.read().add(GetRolesUnder()); + }); + } + } + ////Added State + if (state is RoleUnderAddedState) { + if (state.response['success']) { + successAlert( + context, "Adding Successfull!", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetRolesUnder()); + }); + } else { + errorAlert(context, "Adding Failed", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetRolesUnder()); + }); + } + } + }, + builder: (context, state) { + if (state is RoleUnderLoadedState) { + rolesUnder = {}; + roles = state.roles; + + if (state.rolesUnder.isNotEmpty) { + for (var roleUnder in state.rolesUnder) { + if (!rolesUnder.keys + .contains(roleUnder.roleUnderMain.name!.toLowerCase())) { + rolesUnder.addAll( + {roleUnder.roleUnderMain.name!.toLowerCase(): []}); + rolesUnder[roleUnder.roleUnderMain.name!.toLowerCase()]! + .add(Content( + id: roleUnder.id, + name: roleUnder.roleUnderChild.name!)); + } else { + rolesUnder[roleUnder.roleUnderMain.name!.toLowerCase()]! + .add(Content( + id: roleUnder.id, + name: roleUnder.roleUnderChild.name!)); + } + } + } + + if (state.rolesUnder.isNotEmpty) { + return GroupListView( + sectionsCount: rolesUnder.keys.toList().length, + countOfItemInSection: (int section) { + return rolesUnder.values.toList()[section].length; + }, + itemBuilder: (BuildContext context, IndexPath index) { + return ListTile( + trailing: IconButton( + color: Colors.grey.shade500, + icon: const Icon(Icons.delete), + onPressed: () { + context.read().add(DeleteRoleUnder( + roleUnderId: rolesUnder.values + .toList()[index.section][index.index] + .id)); + }, + ), + title: Row( + children: [ + CircleAvatar( + child: Text("${index.index + 1}", + style: Theme.of(context) + .textTheme + .labelLarge! + .copyWith(color: Colors.white))), + const SizedBox( + width: 20, + ), + Expanded( + child: Text( + rolesUnder.values + .toList()[index.section][index.index] + .name + .toUpperCase(), + style: Theme.of(context) + .textTheme + .labelLarge! + .copyWith( + fontWeight: FontWeight.w500, + color: primary) + ), + ), + ], + ), + ); + }, + separatorBuilder: (context, index) { + return const Divider(); + }, + groupHeaderBuilder: (BuildContext context, int section) { + return ListTile( +tileColor: Colors.white, + dense: true, + title: Text( + rolesUnder.keys.toList()[section].toUpperCase(), + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(color: primary,fontWeight: FontWeight.bold), + ), + ); + }, + ); + } else { + return const EmptyData( + message: "No Role available. Please click + to add."); + } + } + if (state is RoleUnderErrorState) { + return SomethingWentWrong( + message: state.message, onpressed: () {}); + } + return Container(); + }, + ), + ), + ); + } +} + +class Content { + final int id; + final String name; + const Content({required this.id, required this.name}); +} diff --git a/lib/screens/superadmin/stations/stations_screen.dart b/lib/screens/superadmin/stations/stations_screen.dart new file mode 100644 index 0000000..41f8ca3 --- /dev/null +++ b/lib/screens/superadmin/stations/stations_screen.dart @@ -0,0 +1,289 @@ +import 'package:app_popup_menu/app_popup_menu.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:searchfield/searchfield.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/role/role_bloc.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/station/station_bloc.dart'; +import 'package:unit2/model/rbac/rbac_station.dart'; +import 'package:unit2/screens/superadmin/role/shared_pop_up_menu.dart'; +import 'package:unit2/widgets/Leadings/add_leading.dart'; +import 'package:unit2/widgets/error_state.dart'; +import '../../../model/utils/agency.dart'; +import '../../../theme-data.dart/box_shadow.dart'; +import '../../../theme-data.dart/btn-style.dart'; +import '../../../theme-data.dart/colors.dart'; +import '../../../theme-data.dart/form-style.dart'; +import '../../../utils/alerts.dart'; +import '../../../utils/formatters.dart'; +import '../../../utils/global.dart'; +import '../../../widgets/empty_data.dart'; + +class RbacStationScreen extends StatelessWidget { + final int id; + const RbacStationScreen({super.key, required this.id}); + + @override + Widget build(BuildContext context) { + final agencyFocusNode = FocusNode(); + List stations = []; + final formKey = GlobalKey(); + Agency selectedAgency; + return Scaffold( + appBar: AppBar( + centerTitle: true, + backgroundColor: primary, + title: const Text("Station Screen"), + actions: [ + AddLeading(onPressed: () { + BuildContext parent = context; + // showDialog( + // context: context, + // builder: (BuildContext context) { + // return AlertDialog( + // title: const Text("Add New Station"), + // content: FormBuilder( + // key: formKey, + // child: Column( + // mainAxisSize: MainAxisSize.min, + // children: [ + // FormBuilderTextField( + // name: "object_name", + // decoration: normalTextFieldStyle( + // "Role name *", "Role name "), + // validator: FormBuilderValidators.required( + // errorText: "This field is required"), + // ), + // const SizedBox( + // height: 8, + // ), + // FormBuilderTextField( + // name: "slug", + // decoration: normalTextFieldStyle("Slug ", "Slug"), + // ), + // const SizedBox( + // height: 8, + // ), + // FormBuilderTextField( + // validator: FormBuilderValidators.maxLength(50, + // errorText: "Max characters only 50"), + // name: "shorthand", + // decoration: + // normalTextFieldStyle("Shorthand ", "Shorthand"), + // ), + // const SizedBox( + // height: 12, + // ), + // SizedBox( + // width: double.infinity, + // height: 50, + // child: ElevatedButton( + // style: mainBtnStyle( + // primary, Colors.transparent, second), + // onPressed: () { + // if (formKey.currentState! + // .saveAndValidate()) { + // String name = formKey + // .currentState!.value['object_name']; + // String? slug = + // formKey.currentState!.value['slug']; + // String? short = formKey + // .currentState!.value['shorthand']; + // parent.read().add(AddRbacRole( + // id: id, + // name: name, + // shorthand: short, + // slug: slug)); + // Navigator.pop(context); + // } + // }, + // child: const Text("Add"))), + // ], + // ), + // ), + // ); + // }); + }) + ], + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocConsumer( + listener: (context, state) {}, + builder: (context, state) { + final parent = context; + if (state is StationLoadedState) { + stations = state.stations; + if (state.stations.isNotEmpty) { + return Column( + children: [ + Padding( + padding: const EdgeInsets.all(8), + child: SearchField( + inputFormatters: [UpperCaseTextFormatter()], + itemHeight: 70, + focusNode: agencyFocusNode, + suggestions: state.agencies + .map((Agency agency) => + SearchFieldListItem(agency.name!, + item: agency, + child: ListTile( + title: Text( + agency.name!, + overflow: TextOverflow.visible, + ), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle("Filter", "").copyWith( + prefixIcon: const Icon(Icons.filter_list), + suffixIcon: IconButton( + icon: const Icon(Icons.arrow_drop_down), + onPressed: () { + agencyFocusNode.unfocus(); + }, + )), + onSuggestionTap: (agency) { + agencyFocusNode.unfocus(); + selectedAgency = agency.item!; + parent.read().add( + FilterStation(agencyId: selectedAgency.id!)); + }, + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + emptyWidget: const Center( + child: Text("No result found..."), + )), + ), + Expanded( + child: ListView.builder( + padding: const EdgeInsets.symmetric( + vertical: 8, horizontal: 10), + itemCount: state.stations.length, + itemBuilder: (BuildContext context, int index) { + return Column( + children: [ + Container( + width: screenWidth, + decoration: box1(), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + child: Row( + children: [ + Expanded( + child: Row( + children: [ + CircleAvatar( + child: Text('${index + 1}'), + ), + const SizedBox( + width: 12, + ), + Flexible( + child: Text( + state.stations[index] + .stationName!, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight.w500, + color: primary)), + ), + ], + )), + ], + ), + ), + const SizedBox( + height: 3, + ) + ], + ); + }), + ), + ], + ); + } else { + return Column( + children: [ + Padding( + padding: const EdgeInsets.all(8), + child: SearchField( + + inputFormatters: [UpperCaseTextFormatter()], + itemHeight: 70, + focusNode: agencyFocusNode, + suggestions: state.agencies + .map((Agency agency) => + SearchFieldListItem(agency.name!, + item: agency, + child: ListTile( + title: Text( + agency.name!, + overflow: TextOverflow.visible, + ), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle("Filter", "").copyWith( + prefixIcon: const Icon(Icons.filter_list), + suffixIcon: IconButton( + icon: const Icon(Icons.arrow_drop_down), + onPressed: () { + agencyFocusNode.unfocus(); + }, + )), + onSuggestionTap: (agency) { + agencyFocusNode.unfocus(); + selectedAgency = agency.item!; + parent.read().add( + FilterStation(agencyId: selectedAgency.id!)); + }, + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + emptyWidget: const Center( + child: Text("No result found..."), + )), + ), + const SizedBox( + height: 20, + ), + const EmptyData( + message: + "No Station available. Please click + to add."), + ], + ); + } + } + if (state is StationErrorState) { + return SomethingWentWrong( + message: state.message, + onpressed: () { + context.read().add(GetRoles()); + }); + } + if (state is StationLoadingState) { + return CircularProgressIndicator(); + } + return Container(); + }, + ), + ), + ); + } +} diff --git a/lib/screens/unit2/basic-info/basic-info.dart b/lib/screens/unit2/basic-info/basic-info.dart index 2194b21..07539a4 100644 --- a/lib/screens/unit2/basic-info/basic-info.dart +++ b/lib/screens/unit2/basic-info/basic-info.dart @@ -7,6 +7,7 @@ import 'package:intl/intl.dart'; import 'package:qr_flutter/qr_flutter.dart'; import 'package:signature/signature.dart'; import 'package:unit2/model/login_data/user_info/user_data.dart'; +import 'package:unit2/screens/unit2/basic-info/components/qr_image.dart'; import 'package:unit2/theme-data.dart/btn-style.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; @@ -115,8 +116,9 @@ class BuildInformation extends StatelessWidget { final String firstName = userData.user!.login!.user!.firstName!.toUpperCase(); final String lastname = userData.user!.login!.user!.lastName!.toUpperCase(); - final String? middlename = userData.employeeInfo == null?'': - userData.employeeInfo!.profile?.middleName?.toUpperCase(); + final String? middlename = userData.employeeInfo == null + ? '' + : userData.employeeInfo!.profile?.middleName?.toUpperCase(); final String sex = userData.employeeInfo!.profile!.sex!.toUpperCase(); final DateTime? bday = userData.employeeInfo!.profile!.birthdate; final uuid = userData.employeeInfo!.uuid; @@ -129,7 +131,7 @@ class BuildInformation extends StatelessWidget { height: 25, ), Text( - "$firstName ${middlename??''} $lastname", + "$firstName ${middlename ?? ''} $lastname", textAlign: TextAlign.center, style: Theme.of(context) .textTheme @@ -141,27 +143,37 @@ class BuildInformation extends StatelessWidget { ), Text( "${dteFormat2.format(bday!)} | $sex", - style: Theme.of(context).textTheme.bodySmall!.copyWith(fontSize: 18), + style: + Theme.of(context).textTheme.bodySmall!.copyWith(fontSize: 18), ), const SizedBox( height: 15, ), - QrImage( - data: uuid!, - size: blockSizeVertical * 30, + GestureDetector( + onTap: () { + Navigator.push(context, + MaterialPageRoute(builder: (BuildContext context) { + return QRFullScreenImage(uuid: uuid); + })); + }, + child: QrImage( + data: uuid!, + size: blockSizeVertical * 24, + ), ), const SizedBox( height: 25, ), SizedBox( width: screenWidth * .60, - height: blockSizeVertical * 6, + height: blockSizeVertical * 7, child: SizedBox( child: ElevatedButton.icon( style: mainBtnStyle(third, Colors.transparent, Colors.white54), onPressed: () { - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + Navigator.push(context, + MaterialPageRoute(builder: (BuildContext context) { return const SignaturePad(); })); }, diff --git a/lib/screens/unit2/basic-info/components/qr_image.dart b/lib/screens/unit2/basic-info/components/qr_image.dart new file mode 100644 index 0000000..6cd2d39 --- /dev/null +++ b/lib/screens/unit2/basic-info/components/qr_image.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:qr_flutter/qr_flutter.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/global.dart'; + +class QRFullScreenImage extends StatelessWidget { + final String uuid; + const QRFullScreenImage({super.key, required this.uuid}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + centerTitle: true, + backgroundColor: primary,title: const Text("Profile QR Code"),), + body: Center( + child: QrImage( + data: uuid, + size: blockSizeVertical * 50 + ), + ),); + } +} \ No newline at end of file diff --git a/lib/screens/unit2/homepage.dart/components/dashboard.dart b/lib/screens/unit2/homepage.dart/components/dashboard.dart deleted file mode 100644 index e1056ac..0000000 --- a/lib/screens/unit2/homepage.dart/components/dashboard.dart +++ /dev/null @@ -1,272 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:fluttericon/font_awesome5_icons.dart'; -import 'package:unit2/screens/docsms/index.dart'; -import 'package:unit2/screens/unit2/homepage.dart/module-screen.dart'; -import 'package:unit2/theme-data.dart/colors.dart'; -import 'package:unit2/utils/global.dart'; -import 'package:unit2/utils/global_context.dart'; -import 'package:unit2/utils/qr_scanner.dart'; -import '../../../../bloc/docsms/docsms_bloc.dart'; - -class DashBoard extends StatelessWidget { - final List roles; - final int userId; - const DashBoard({super.key, required this.roles,required this.userId}); - @override - Widget build(BuildContext context) { - List finishRoles = []; - return Container( - padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 24), - height: MediaQuery.of(context).size.height, - ////listview builder - child: ListView.builder( - scrollDirection: Axis.vertical, - shrinkWrap: true, - itemCount: roles.length, - itemBuilder: (BuildContext context, int index) { - - //// gridview.count - return roles[index].roles.isNotEmpty - ? SizedBox( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - roles[index].name.toUpperCase(), - style: Theme.of(context) - .textTheme - .labelLarge! - .copyWith(fontSize: 12), - ), - const SizedBox( - height: 8, - ), - GridView.count( - shrinkWrap: true, - crossAxisCount: 4, - crossAxisSpacing: 8, - mainAxisSpacing: 10, - physics: const BouncingScrollPhysics(), - padding: const EdgeInsets.symmetric( - vertical: 5, horizontal: 5), - children: roles[index].roles.map((role) { - if (role.role.name!.toLowerCase() == - 'qr code scanner' && - !finishRoles.contains("security")) { - print("1 true"); - finishRoles.add('scanner'); - for (var element in role.role.modules!) { - if (element!.name!.toLowerCase() == 'unit2') { - for (var element in element.objects!) { - if (element!.id == 9 && - element.operations! - .contains("read")) { - return CardLabel( - ontap: () { - PassCheckArguments passCheckArguments = PassCheckArguments(roleId: role.role.id!, userId: userId); - Navigator.pushNamed(context, '/pass-check',arguments: passCheckArguments); - }, - icon: role.icon, - title: "Pass Check", - ); - } - } - } - } - return Container(); - } else if (role.role.name!.toLowerCase() == - 'security guard' && - !finishRoles.contains('scanner')) { - print("2 true"); - finishRoles.add('security'); - for (var element in role.role.modules!) { - if (element!.name!.toLowerCase() == 'unit2') { - for (var element in element.objects!) { - if (element!.id == 9 && - element.operations! - .contains("read")) { - return CardLabel( - ontap: () {}, - icon: role.icon, - title: "Pass Check", - ); - } - } - } - } - return Container(color: Colors.red,); - } else if (role.role.name!.toLowerCase() == - 'field surveyor') { - print("3 true"); - for (var element in role.role.modules!) { - if (element!.name!.toLowerCase() == 'rpass') { - for (var element in element.objects!) { - if (element!.id == 11 && - element.operations! - .contains("read")) { - return CardLabel( - ontap: () {}, - icon: role.icon, - title: "Field Surveyor", - ); - } - } - } - } - return Container(); - } else if (role.role.name!.toLowerCase() == - 'process server') { - print("4 true"); - for (var element in role.role.modules!) { - if (element!.name!.toLowerCase() == - 'document management') { - for (var element in element.objects!) { - if (element!.id == 3 && - element.operations! - .contains("read")) { - return CardLabel( - ontap: () async { - String? qrBarcode = - await qrScanner(); - if (qrBarcode != null) { - Navigator.push(NavigationService.navigatorKey.currentContext!, MaterialPageRoute(builder: - (BuildContext context) { - return BlocProvider( - create: (context) => DocsmsBloc() - ..add(LoadDocument( - documentId: qrBarcode)), - child: - const AutoReceiveDocument(), - ); - })); - } - }, - icon: role.icon, - title: "Process Server", - ); - } - } - } - } - return Container(); - } else if (role.role.name!.toLowerCase() == - 'establishment point-person' && - !finishRoles.contains('superadmin')) { - finishRoles.add('establishment point-person'); - for (var element in role.role.modules!) { - print("5 true"); - if (element!.name!.toLowerCase() == 'unit2') { - for (var element in element.objects!) { - if (element!.id == 7 && - element.operations! - .contains("upload")) { - return CardLabel( - ontap: () {}, - icon: FontAwesome5.building, - title: "Establishment", - ); - } - } - } - } - return Container(); - - } else if (role.role.name!.toLowerCase() == - 'establishment point-person' && - !finishRoles.contains('superadmin')) { - finishRoles.add('establishment point-person'); - for (var element in role.role.modules!) { - if (element!.name!.toLowerCase() == 'unit2') { - for (var element in element.objects!) { - if (element!.id == 7 && - element.operations! - .contains("upload")) { - return CardLabel( - ontap: () {}, - icon: FontAwesome5.building, - title: "Establishment", - ); - } - } - } - } - return Container(); - - } else{ - return Wrap(); - } - - - }).toList()), - const SizedBox( - height: 8, - ) - ], - ), - ) - : Container(); - }), - ); - } -} - -class PassCheckArguments{ - final int roleId; - final int userId; - const PassCheckArguments({required this.roleId, required this.userId}); -} - -// ignore: must_be_immutable -class CardLabel extends StatelessWidget { - final String title; - final IconData icon; - final Function()? ontap; - const CardLabel( - {super.key, - required this.icon, - required this.title, - required this.ontap}); - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: ontap, - child: Container( - padding: const EdgeInsetsDirectional.fromSTEB(8, 5, 8, 13), - alignment: Alignment.center, - decoration: const BoxDecoration( - color: Colors.white, - boxShadow: [ - BoxShadow(color: Colors.black12, spreadRadius: 2, blurRadius: 3) - ], - borderRadius: BorderRadius.all(Radius.circular(8))), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - child: Icon( - icon, - size: 20, - weight: 100, - grade: 100, - color: second, - ), - ), - const SizedBox( - height: 5, - ), - Text( - title, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.labelLarge!.copyWith( - fontSize: blockSizeVertical * 1.2, - fontWeight: FontWeight.bold), - ), - ]), - ), - ); - } -} diff --git a/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart b/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart new file mode 100644 index 0000000..efeb14d --- /dev/null +++ b/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart @@ -0,0 +1,479 @@ +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; +import 'package:fluttericon/font_awesome5_icons.dart'; +import 'package:unit2/screens/unit2/homepage.dart/components/dashboard/dashboard_icon_generator.dart'; +import 'package:unit2/screens/unit2/homepage.dart/components/dashboard/superadmin_expanded_menu.dart'; +import 'package:unit2/screens/unit2/homepage.dart/module-screen.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import './shared_card_label.dart'; + +class DashBoard extends StatefulWidget { + final List cards; + final int userId; + const DashBoard({super.key, required this.cards, required this.userId}); + + @override + State createState() => _DashBoardState(); +} + +List finishRoles = []; +List unit2Cards = []; +List superadminCards = []; +List rpassCards = []; +List docSmsCards = []; +List tempSuperAdminCards = []; +List tempUnit2Cards = []; + +class _DashBoardState extends State { + @override + Widget build(BuildContext context) { + setState(() { + finishRoles.clear(); + unit2Cards.clear(); + superadminCards.clear(); + rpassCards.clear(); + docSmsCards.clear(); + tempSuperAdminCards.clear(); + }); + widget.cards.forEach((e) { + if (e.moduleName == "unit2") { + if (!finishRoles.contains(e.object.name)) { + unit2Cards.add(e); + } + finishRoles.add(e.object.name!); + } + if (e.moduleName == 'superadmin') { + superadminCards.add(e); + } + if (e.moduleName == 'rpass') { + rpassCards.add(e); + } + if (e.moduleName == 'document management') { + docSmsCards.add(e); + } + }); + + if (superadminCards.length > 3) { + tempSuperAdminCards = superadminCards.sublist(0, 4); + } + if (unit2Cards.length > 3) { + tempUnit2Cards = unit2Cards.sublist(0, 4); + } + + return Container( + padding: + const EdgeInsetsDirectional.symmetric(vertical: 24, horizontal: 24), + child: ListView( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ////unit2 module operations + Container(child: unit2Cards.isEmpty?const SizedBox():Text( + "Unit2 module operations", + style: Theme.of(context).textTheme.displaySmall!.copyWith( + fontSize: 16, color: primary, fontWeight: FontWeight.w300), + ),), + SizedBox( + height: unit2Cards.isEmpty?0: 8, + ), + Container(child: unit2Cards.isEmpty?const SizedBox():GridView.count( + shrinkWrap: true, + crossAxisCount: 4, + crossAxisSpacing: 8, + mainAxisSpacing: 10, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 5), + children: unit2Cards.length > 3 + ? tempUnit2Cards.map(( + e, + ) { + int index = tempUnit2Cards.indexOf(e); + //// if unit2 cards is less then 3 + return Container( + child: index == 3 + ? CardLabel( + icon: FontAwesome5.chevron_circle_right, + title: "See More", + ontap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text( + "Unit2 Admin Module Operations", + textAlign: TextAlign.center, + ), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + height: 200, + width: double.maxFinite, + child: GridView.count( + shrinkWrap: true, + crossAxisCount: 3, + crossAxisSpacing: 8, + mainAxisSpacing: 10, + physics: + const BouncingScrollPhysics(), + padding: + const EdgeInsets + .symmetric( + vertical: 5, + horizontal: 5), + children: + unit2Cards.map(( + e, + ) { + int index = unit2Cards + .indexOf(e); + //// if unit2 cards is less then 3 + return AnimationConfiguration + .staggeredGrid( + position: index, + columnCount: 3, + child: + ScaleAnimation( + child: + FadeInAnimation( + child: Container( + child: (e.roleName == 'superadmin' || e.roleName == 'qr code scanner' || e.roleName == 'security guard' || e.roleName == 'establishment point-person' || e.roleName == 'registration in-charge') && e.moduleName == 'unit2' + ? CardLabel( + icon: iconGenerator(name: e.object.name!), + title: e.object.name!.toLowerCase() == 'role based access control' + ? "RBAC" + : e.object.name!.toLowerCase() == "person basic information" + ? "Basic Info" + : e.object.name!, + ontap: () { + if (e.object.name!.toLowerCase() == 'pass check') { + PassCheckArguments passCheckArguments = PassCheckArguments(roleId: 10, userId: widget.userId); + Navigator.pushNamed(context, '/pass-check', arguments: passCheckArguments); + } + if (e.object.name!.toLowerCase() == 'role based access control') { + Navigator.pushNamed(context, '/rbac'); + } + }) + : Container( + color: + Colors.black, + )), + ), + ), + ); + }).toList()), + ), + ], + ), + ); + }); + }) + : (e.roleName == 'superadmin' || + e.roleName == 'qr code scanner' || + e.roleName == 'security guard' || + e.roleName == + 'establishment point-person' || + e.roleName == + 'registration in-charge') && + e.moduleName == 'unit2' + ? CardLabel( + icon: + iconGenerator(name: e.object.name!), + title: e.object.name!.toLowerCase() == + 'role based access control' + ? "RBAC" + : e.object.name!.toLowerCase() == + "person basic information" + ? "Basic Info" + : e.object.name!, + ontap: () { + if (e.object.name!.toLowerCase() == + 'pass check') { + PassCheckArguments + passCheckArguments = + PassCheckArguments( + roleId: 10, + userId: widget.userId); + Navigator.pushNamed( + context, '/pass-check', + arguments: passCheckArguments); + } + if (e.object.name!.toLowerCase() == + 'role based access control') { + Navigator.pushNamed( + context, '/rbac'); + } + }) + : Container( + color: Colors.black, + )); + }).toList() + : unit2Cards.map(( + e, + ) { + ////if unit2 cards is greater than 3 + return Container( + child: (e.roleName == 'superadmin' || + e.roleName == 'qr code scanner' || + e.roleName == 'security guard' || + e.roleName == + 'establishment point-person' || + e.roleName == + 'registration in-charge') && + e.moduleName == 'unit2' + ? CardLabel( + icon: iconGenerator(name: e.object.name!), + title: e.object.name!.toLowerCase() == + 'role based access control' + ? "RBAC" + : e.object.name!.toLowerCase() == + "person basic information" + ? "Basic Info" + : e.object.name!, + ontap: () { + if (e.object.name!.toLowerCase() == + 'pass check') { + PassCheckArguments passCheckArguments = + PassCheckArguments( + roleId: 10, + userId: widget.userId); + Navigator.pushNamed( + context, '/pass-check', + arguments: passCheckArguments); + } + if (e.object.name!.toLowerCase() == + 'role based access control') { + Navigator.pushNamed(context, '/rbac'); + } + }) + : Container( + color: Colors.black, + )); + }).toList(), + ),), + SizedBox( + height: unit2Cards.isEmpty?0:24, + ), + Container(child: superadminCards.isEmpty?const SizedBox(): Text( + "Superadmin module operations", + style: Theme.of(context).textTheme.displaySmall!.copyWith( + fontSize: 16, color: primary, fontWeight: FontWeight.w300), + ),), + SizedBox( + height: superadminCards.isEmpty? 0: 8, + ), + Container(child: superadminCards.isEmpty?const SizedBox():GridView.count( + shrinkWrap: true, + crossAxisCount: 4, + crossAxisSpacing: 8, + mainAxisSpacing: 10, + physics: const BouncingScrollPhysics(), + padding: + const EdgeInsets.symmetric(vertical: 5, horizontal: 5), + children: superadminCards.length > 3 + //// in superadmincards lenght is greaterthan 3 + ? tempSuperAdminCards.map((e) { + int index = tempSuperAdminCards.indexOf(e); + return Container( + child: index == 3 + ? CardLabel( + icon: FontAwesome5.chevron_circle_right, + title: "See More", + ontap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text( + "Super Admin Module Operations", + textAlign: TextAlign.center, + ), + content: Column( + mainAxisSize: + MainAxisSize.min, + children: [ + SizedBox( + height: 480, + width: double.maxFinite, + child: GridView.count( + shrinkWrap: true, + crossAxisCount: 3, + crossAxisSpacing: 8, + mainAxisSpacing: 10, + physics: + const BouncingScrollPhysics(), + padding: + const EdgeInsets + .symmetric( + vertical: 5, + horizontal: 5), + children: + superadminCards + .map((e) { + int index = + superadminCards + .indexOf(e); + return SuperAdminMenu( + id: widget.userId, + columnCount: 3, + index: index, + object: e, + ); + }).toList(), + ), + ), + ], + ), + ); + }); + }) + : SuperAdminMenu( + id: widget.userId, + object: e, + index: index, + columnCount: 3, + )); + }).toList() + //// in superadmincards lenght is lessthan 3 + : superadminCards.map((e) { + int index = tempSuperAdminCards.indexOf(e); + return Container( + child: index == 3 + ? CardLabel( + icon: FontAwesome5.chevron_circle_right, + title: "See More", + ontap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text( + "Super Admin Module Operations", + textAlign: TextAlign.center, + ), + content: Column( + mainAxisSize: + MainAxisSize.min, + children: [ + SizedBox( + height: 480, + width: double.maxFinite, + child: GridView.count( + shrinkWrap: true, + crossAxisCount: 3, + crossAxisSpacing: 8, + mainAxisSpacing: 10, + physics: + const BouncingScrollPhysics(), + padding: + const EdgeInsets + .symmetric( + vertical: 5, + horizontal: 5), + children: + superadminCards + .map((e) { + return SuperAdminMenu( + id: widget.userId, + columnCount: 4, + index: index, + object: e, + ); + }).toList(), + ), + ), + ], + ), + ); + }); + }) + : SuperAdminMenu( + id: widget.userId, + object: e, + index: index, + columnCount: 4, + )); + }).toList())), + const SizedBox( + height: 24, + ), + Container(child: rpassCards.isEmpty?const SizedBox(): Text( + "RPAss module operations", + style: Theme.of(context).textTheme.displaySmall!.copyWith( + fontSize: 16, color: primary, fontWeight: FontWeight.w300), + ),) +, SizedBox( + height:rpassCards.isEmpty?0: 8, + ), + Container(child: rpassCards.isEmpty?const SizedBox():GridView.count( + shrinkWrap: true, + crossAxisCount: 4, + crossAxisSpacing: 8, + mainAxisSpacing: 10, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 5), + children: rpassCards.map((e) { + return Container( + child: (e.roleName == 'field surveyor') && + e.moduleName == 'rpass' + ? CardLabel( + icon: iconGenerator(name: e.object.name!), + title: e.object.name == 'Real Property' + ? "Field Surveyor" + : e.object.name!, + ontap: () {}) + : Container( + color: Colors.black, + )); + }).toList(), + ),), + const SizedBox( + height: 24, + ), + Container(child: docSmsCards.isEmpty?const SizedBox(): Text( + "DocSMS module operations", + style: Theme.of(context).textTheme.displaySmall!.copyWith( + fontSize: 16, color: primary, fontWeight: FontWeight.w300), + ),), + const SizedBox( + height: 8, + ), + GridView.count( + shrinkWrap: true, + crossAxisCount: 4, + crossAxisSpacing: 8, + mainAxisSpacing: 10, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 5), + children: docSmsCards.map((e) { + return Container( + child: (e.roleName == 'process server') && + e.moduleName == 'document management' + ? CardLabel( + icon: iconGenerator(name: e.object.name!), + title: e.object.name == "Document" + ? "Process Server" + : e.object.name!, + ontap: () { + + }) + : Container( + color: Colors.black, + )); + }).toList(), + ) + ], + ) + ], + ), + ); + } +} + +class PassCheckArguments { + final int roleId; + final int userId; + const PassCheckArguments({required this.roleId, required this.userId}); +} diff --git a/lib/screens/unit2/homepage.dart/components/dashboard/dashboard_icon_generator.dart b/lib/screens/unit2/homepage.dart/components/dashboard/dashboard_icon_generator.dart new file mode 100644 index 0000000..ff8e816 --- /dev/null +++ b/lib/screens/unit2/homepage.dart/components/dashboard/dashboard_icon_generator.dart @@ -0,0 +1,66 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttericon/font_awesome5_icons.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:fluttericon/maki_icons.dart'; +import 'package:fluttericon/modern_pictograms_icons.dart'; +import 'package:fluttericon/octicons_icons.dart'; +import 'package:fluttericon/web_symbols_icons.dart'; + +IconData? iconGenerator({required String name}) { + if (name.toLowerCase() == 'agency') { + return FontAwesome5.building; + } else if (name.toLowerCase() == 'assignable role') { + return FontAwesome5.user_plus; + } else if (name.toLowerCase() == 'role') { + return FontAwesome5.user; + } else if (name.toLowerCase() == 'operation') { + return FontAwesome.export_alt; + } else if (name.toLowerCase() == 'module') { + return Icons.view_module; + } else if (name.toLowerCase() == 'area') { + return FontAwesome5.map_marked; + } else if (name.toLowerCase() == 'object') { + return FontAwesome.box; + } else if (name.toLowerCase() == 'permission') { + return FontAwesome5.door_open; + } else if (name.toLowerCase() == 'station') { + return ModernPictograms.home; + } else if (name.toLowerCase() == 'purok') { + return WebSymbols.list_numbered; + } else if (name.toLowerCase() == 'barangay') { + return Maki.industrial_building; + } else if (name.toLowerCase() == 'role module') { + return FontAwesome5.person_booth; + } else if (name.toLowerCase() == 'module object') { + return FontAwesome5.th_list; + } else if (name.toLowerCase() == 'roles extend') { + return FontAwesome5.external_link_square_alt; + } else if (name.toLowerCase() == 'real property') { + return FontAwesome5.eye; + } else if (name.toLowerCase() == 'document') { + return FontAwesome5.newspaper; + } else if (name.toLowerCase() == 'role based access control') { + return FontAwesome5.tasks; + } else if (name.toLowerCase() == 'pass check') { + return FontAwesome5.qrcode; + } else if (name.toLowerCase() == 'list of persons') { + return FontAwesome5.users; + } else if (name.toLowerCase() == 'person basic information') { + return FontAwesome5.info_circle; + }else if(name.toLowerCase() == "role member"){ + return FontAwesome5.users_cog; + } + + + + + + else if (name.toLowerCase() == 'security location') { + return FontAwesome5.location_arrow; + } + + else { + return null; + } +} diff --git a/lib/screens/unit2/homepage.dart/components/dashboard/shared_card_label.dart b/lib/screens/unit2/homepage.dart/components/dashboard/shared_card_label.dart new file mode 100644 index 0000000..9dd8c3d --- /dev/null +++ b/lib/screens/unit2/homepage.dart/components/dashboard/shared_card_label.dart @@ -0,0 +1,61 @@ +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:flutter/material.dart'; + +import '../../../../../theme-data.dart/colors.dart'; +import '../../../../../utils/global.dart'; + +class CardLabel extends StatelessWidget { + final String title; + final IconData? icon; + final Function()? ontap; + const CardLabel( + {super.key, + required this.icon, + required this.title, + required this.ontap}); + + @override + Widget build(BuildContext context) { + return InkWell( + splashColor: second, + onTap: ontap, + child: Container( + padding: const EdgeInsetsDirectional.fromSTEB(8, 5, 8, 13), + alignment: Alignment.center, + decoration: const BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow(color: Colors.black12, spreadRadius: 1, blurRadius: 2) + ], + borderRadius: BorderRadius.all(Radius.circular(8))), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Flexible( + flex: 1, + child: Icon( + icon, + size: 20, + weight: 100, + grade: 100, + color: second, + ), + ), + const Expanded(child: SizedBox()), + Flexible( + flex: 2, + child: AutoSizeText( + minFontSize: 10, + title, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.labelLarge!.copyWith( + fontSize: blockSizeVertical * 1.2, + fontWeight: FontWeight.bold), + ), + ), + ]), + ), + ); + } +} \ No newline at end of file diff --git a/lib/screens/unit2/homepage.dart/components/dashboard/superadmin_expanded_menu.dart b/lib/screens/unit2/homepage.dart/components/dashboard/superadmin_expanded_menu.dart new file mode 100644 index 0000000..23d42ca --- /dev/null +++ b/lib/screens/unit2/homepage.dart/components/dashboard/superadmin_expanded_menu.dart @@ -0,0 +1,317 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/agency/agency_bloc.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/module/module_bloc.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/module_objects/module_objects_bloc.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/object/object_bloc.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/operation/operation_bloc.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/permission/permission_bloc.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/role/role_bloc.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/role_extend/role_extend_bloc.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/role_module/role_module_bloc.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/roles_under/roles_under_bloc.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/station/station_bloc.dart'; +import 'package:unit2/bloc/role_assignment/role_assignment_bloc.dart'; +import 'package:unit2/screens/superadmin/module/module_screen.dart'; +import 'package:unit2/screens/superadmin/object/object_screen.dart'; +import 'package:unit2/screens/superadmin/operation/operation_screen.dart'; +import 'package:unit2/screens/superadmin/permission/permission_screen.dart'; +import 'package:unit2/screens/superadmin/role/role_screen.dart'; +import 'package:unit2/screens/superadmin/role_assignment.dart/role_assignment_screen.dart'; +import 'package:unit2/screens/superadmin/role_extend/role_extend_screen.dart'; +import 'package:unit2/screens/superadmin/roles_under/roles_under_screen.dart'; +import 'package:unit2/screens/superadmin/stations/stations_screen.dart'; +import 'package:unit2/screens/unit2/homepage.dart/module-screen.dart'; +import 'package:unit2/theme-data.dart/btn-style.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/theme-data.dart/form-style.dart'; +import '../../../../superadmin/agency.dart/agency_screen.dart'; +import '../../../../superadmin/module_objects/module_objects_screen.dart'; +import '../../../../superadmin/role_module/role_module_scree.dart'; +import './shared_card_label.dart'; +import 'dashboard_icon_generator.dart'; + +class SuperAdminMenu extends StatelessWidget { + final int id; + final DisplayCard object; + final int index; + final int columnCount; + const SuperAdminMenu( + {super.key, + required this.id, + required this.object, + required this.index, + required this.columnCount}); + + @override + Widget build(BuildContext context) { + final roleAssignmentKey = GlobalKey(); + return AnimationConfiguration.staggeredGrid( + position: index, + columnCount: columnCount, + child: ScaleAnimation( + child: FadeInAnimation( + child: Container( + child: (object.roleName == 'superadmin' || + object.object.name == 'Agency' || + object.object.name == 'Assignable Role' || + object.object.name == 'Role' || + object.object.name == 'Module' || + object.object.name == 'Object' || + object.object.name == 'Operation' || + object.object.name == 'Permission' || + object.object.name == 'Area' || + object.object.name == 'Station' || + object.object.name == 'Purok' || + object.object.name == 'Barangay' || + object.object.name == 'Role Module' || + object.object.name == 'Module Object' || + object.object.name == 'Roles Extend' || + object.object.name == "Role Member") && + object.moduleName == 'superadmin' + ? CardLabel( + icon: iconGenerator(name: object.object.name!), + title: + object.object.name!.toLowerCase() == 'assignable role' + ? "Role Assignment" + : object.object.name!, + ontap: () { + if (object.object.name == 'Role') { + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return BlocProvider( + create: (context) => RoleBloc()..add(GetRoles()), + child: RbacRoleScreen( + id: id, + ), + ); + })); + } + if (object.object.name == 'Operation') { + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return BlocProvider( + create: (context) => + OperationBloc()..add(GetOperations()), + child: RbacOperationScreen( + id: id, + ), + ); + })); + } + if (object.object.name == 'Module') { + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return BlocProvider( + create: (context) => + ModuleBloc()..add(GetModule()), + child: RbacModuleScreen( + id: id, + ), + ); + })); + } + if (object.object.name == 'Object') { + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return BlocProvider( + create: (context) => + ObjectBloc()..add(GetObjects()), + child: RbacObjectScreen( + id: id, + ), + ); + })); + } + if (object.object.name == 'Permission') { + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return BlocProvider( + create: (context) => + PermissionBloc()..add(GetPermissions()), + child: RbacPermissionScreen( + id: id, + ), + ); + })); + } + if (object.object.name == 'Module Object') { + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return BlocProvider( + create: (context) => + ModuleObjectsBloc()..add(GetModuleObjects()), + child: RbacModuleObjectsScreen( + id: id, + ), + ); + })); + } + if (object.object.name == 'Agency') { + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return BlocProvider( + create: (context) => + AgencyBloc()..add(GetAgencies()), + child: RbacAgencyScreen( + id: id, + ), + ); + })); + } + if (object.object.name == 'Role Module') { + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return BlocProvider( + create: (context) => + RoleModuleBloc()..add(GetRoleModules()), + child: RbacRoleModuleScreen( + id: id, + ), + ); + })); + } + if (object.object.name == 'Assignable Role') { + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return BlocProvider( + create: (context) => + RolesUnderBloc()..add(GetRolesUnder()), + child: RbacRoleUnderScreen( + id: id, + ), + ); + })); + } + if (object.object.name == 'Roles Extend') { + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return BlocProvider( + create: (context) => + RoleExtendBloc()..add(GetRoleExtend()), + child: RbacRoleExtendScreen( + id: id, + ), + ); + })); + } + if (object.object.name == 'Station') { + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return BlocProvider( + create: (context) => StationBloc() + ..add(const GetStations(agencyId: 1)), + child: RbacStationScreen( + id: id, + ), + ); + })); + } + if (object.object.name == 'Station') { + Navigator.push(context, MaterialPageRoute( + builder: (BuildContext context) { + return BlocProvider( + create: (context) => StationBloc() + ..add(const GetStations(agencyId: 1)), + child: RbacStationScreen( + id: id, + ), + ); + })); + } + if (object.object.name == 'Role Member') { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Row( + children: [ + const Expanded(child: Text("Search User")), + IconButton(onPressed: (){ + Navigator.pop(context); + }, icon: const Icon(Icons.close)) + ], + ), + content: FormBuilder( + key: roleAssignmentKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + FormBuilderTextField( + name: "firstname", + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + decoration: normalTextFieldStyle( + "First name", "first name"), + ), + const SizedBox( + height: 8, + ), + FormBuilderTextField( + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + name: "lastname", + decoration: normalTextFieldStyle( + "Last name", "last tname"), + ), + const SizedBox( + height: 24, + ), + SizedBox( + height: 60, + width: double.maxFinite, + child: ElevatedButton( + onPressed: () { + if (roleAssignmentKey + .currentState! + .saveAndValidate()) { + + String fname = + roleAssignmentKey + .currentState! + .value['firstname']; + String lname = + roleAssignmentKey + .currentState! + .value['lastname']; + Navigator.push(context, + MaterialPageRoute(builder: + (BuildContext + context) { + return BlocProvider( + create: (context) => + RoleAssignmentBloc() + ..add(GetAssignedRoles( + firstname: + fname, + lastname: + lname),),child:RbacRoleAssignment(id:id) ,); + })); + } + }, + style: mainBtnStyle(primary, + Colors.transparent, second), + child: const Text("Submit"), + ), + ) + ], + )), + ); + }); + } + }) + : Container( + color: Colors.black, + )), + ), + ), + ); + } +} diff --git a/lib/screens/unit2/homepage.dart/module-screen.dart b/lib/screens/unit2/homepage.dart/module-screen.dart index e8a9585..a41389a 100644 --- a/lib/screens/unit2/homepage.dart/module-screen.dart +++ b/lib/screens/unit2/homepage.dart/module-screen.dart @@ -1,14 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_zoom_drawer/flutter_zoom_drawer.dart'; -import 'package:fluttericon/font_awesome5_icons.dart'; -import 'package:fluttericon/font_awesome_icons.dart'; -import 'package:fluttericon/rpg_awesome_icons.dart'; -import 'package:fluttericon/typicons_icons.dart'; -import 'package:unit2/screens/unit2/homepage.dart/components/dashboard.dart'; +import 'package:unit2/screens/unit2/homepage.dart/components/dashboard/dashboard.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/text_container.dart'; import '../../../bloc/user/user_bloc.dart'; +import '../../../model/login_data/user_info/module.dart'; import '../../../model/login_data/user_info/role.dart'; import 'components/empty_module.dart'; @@ -20,14 +17,17 @@ class MainScreen extends StatefulWidget { } class _MainScreenState extends State { - List roles = [ - Module(name: 'UniT2 module operations', roles: []), - Module(name: 'DocSms module operations', roles: []), - Module(name: "RPAss module operations",roles:[] ) - ]; + List roles = []; + List cards = []; int? userId; @override Widget build(BuildContext context) { + setState(() { + cards.clear(); + cards=[]; + roles.clear(); + roles = []; + }); return WillPopScope( onWillPop: () async { return Future.value(true); @@ -35,85 +35,63 @@ class _MainScreenState extends State { child: BlocBuilder(builder: (context, state) { if (state is UserLoggedIn) { userId = state.userData!.user!.login!.user!.id; - for (var element in roles) { - element.roles.clear(); - } for (var role in state.userData!.user!.login!.user!.roles!) { Role? getRole = role; - for (var module in role!.modules!) { - if (module!.name!.toLowerCase() == 'unit2') { - IconData iconData = iconGenerator(getRole!.name!); - Roles newRole = Roles(role: getRole, icon: iconData); - roles[0].roles.add(newRole); - } - if (module.name!.toLowerCase() == 'document management') { - IconData iconData = iconGenerator(getRole!.name!); - Roles newRole = Roles(role: getRole, icon: iconData); - roles[1].roles.add(newRole); - } if (module.name!.toLowerCase() == 'rpass') { - IconData iconData = iconGenerator(getRole!.name!); - Roles newRole = Roles(role: getRole, icon: iconData); - roles[2].roles.add(newRole); + roles.add(getRole!); + } + for (var role in roles) { + for (var module in role.modules!) { + for (var object in module!.objects!) { + DisplayCard newCard = DisplayCard( + moduleName: module.name!.toLowerCase(), + object: object!, + roleName: role.name!.toLowerCase()); + cards.add(newCard); } } } + return Scaffold( appBar: AppBar( - backgroundColor: primary, - leading: IconButton( - onPressed: () { - ZoomDrawer.of(context)!.toggle(); - }, - icon: const Icon( - Icons.menu, - color: Colors.white, - ), - ), - centerTitle: true, - title: const Text( - unit2ModuleScreen, - style: TextStyle( - fontSize: 18.0, - color: Colors.white, - ), - ), + backgroundColor: primary, + leading: IconButton( + onPressed: () { + ZoomDrawer.of(context)!.toggle(); + }, + icon: const Icon( + Icons.menu, + color: Colors.white, + ), + ), + centerTitle: true, + title: const Text( + unit2ModuleScreen, + style: TextStyle( + fontSize: 18.0, + color: Colors.white, + ), + ), ), body: state.userData!.user!.login!.user!.roles!.isNotEmpty - ? DashBoard( - userId: userId!, - roles: roles, - ) - : const NoModule(), + ? DashBoard( + userId: userId!, + cards: cards, + ) + : const NoModule(), ); } return Container(); }), ); } +} - IconData iconGenerator(String roleName) { - IconData? iconData; - switch (roleName.toLowerCase()) { - case 'qr code scanner': - iconData = FontAwesome.qrcode; - break; - case 'security guard': - iconData = FontAwesome5.user_shield; - break; - case 'establishment point-person': - iconData = FontAwesome.building_filled; - break; - case 'registration in-charge': - iconData = FontAwesome.user_plus; - break; - case 'process server': - iconData = Typicons.doc_text; - break; - case 'field surveyor': - iconData = RpgAwesome.telescope; - } - return iconData!; - } +class DisplayCard { + final String roleName; + final String moduleName; + final ModuleObject object; + const DisplayCard( + {required this.moduleName, required this.object, required this.roleName}); } class Module { @@ -123,7 +101,7 @@ class Module { } class Roles { - final IconData icon; + final IconData? icon; final Role role; Roles({required this.role, required this.icon}); } diff --git a/lib/screens/unit2/login/login.dart b/lib/screens/unit2/login/login.dart index c0a0c03..ecd188a 100644 --- a/lib/screens/unit2/login/login.dart +++ b/lib/screens/unit2/login/login.dart @@ -1,4 +1,3 @@ -import 'package:barcode_scan2/barcode_scan2.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; @@ -66,7 +65,9 @@ class _UniT2LoginState extends State { gravity: ToastGravity.BOTTOM, backgroundColor: Colors.black, ); - Navigator.pushReplacementNamed(context, '/module-screen'); + Navigator.pushReplacementNamed( + NavigationService.navigatorKey.currentContext!, + '/module-screen'); }, () => Navigator.pushReplacementNamed( context, '/module-screen'), @@ -85,8 +86,10 @@ class _UniT2LoginState extends State { Navigator.of(context).pop(); }); } - }if(state is UuidLoaded){ - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context){ + } + if (state is UuidLoaded) { + Navigator.push(context, + MaterialPageRoute(builder: (BuildContext context) { return const QRLogin(); })); } @@ -367,15 +370,18 @@ class _UniT2LoginState extends State { if (state is SplashScreen) { return const UniTSplashScreen(); } - if(state is LoginErrorState){ - return SomethingWentWrong(message: state.message, onpressed: () { + if (state is LoginErrorState) { + return SomethingWentWrong( + message: state.message, + onpressed: () { BlocProvider.of( NavigationService.navigatorKey.currentContext!) .add(GetApkVersion()); return MaterialPageRoute(builder: (_) { return const UniT2Login(); }); - },); + }, + ); } return Container(); }), diff --git a/lib/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart b/lib/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart index 19df2b5..c150df6 100644 --- a/lib/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart +++ b/lib/screens/unit2/roles/qr_code_scanner.dart/settings_screen.dart @@ -9,7 +9,7 @@ import 'package:fluttericon/modern_pictograms_icons.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:unit2/bloc/role/pass_check/pass_check_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; -import 'package:unit2/screens/unit2/homepage.dart/components/dashboard.dart'; +import 'package:unit2/screens/unit2/homepage.dart/components/dashboard/dashboard.dart'; import 'package:unit2/screens/unit2/roles/qr_code_scanner.dart/components/custom_switch.dart'; import 'package:unit2/screens/unit2/roles/qr_code_scanner.dart/scan.dart'; import 'package:unit2/utils/text_container.dart'; @@ -40,593 +40,591 @@ class _QRCodeScannerSettingsState extends State { final _formKey = GlobalKey(); @override Widget build(BuildContext context) { - return SafeArea( - child: Scaffold( - appBar: AppBar( - title: const Text(qrScannerTitle), - centerTitle: true, - backgroundColor: primary, + return Scaffold( + appBar: AppBar( + title: const Text(qrScannerTitle), + centerTitle: true, + backgroundColor: primary, + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + indicatorWidget: const SpinKitFadingCircle( + color: Colors.white, ), - body: ProgressHUD( - padding: const EdgeInsets.all(24), - indicatorWidget: const SpinKitFadingCircle( - color: Colors.white, - ), - backgroundColor: Colors.black87, - child: BlocBuilder( - builder: (context, state) { - if (state is UserLoggedIn) { - token = state.userData!.user!.login!.token; - checkerId = state.userData!.user!.login!.user!.id; - return BlocConsumer( - listener: (context, state) { - if (state is PassCheckLoadingState) { - final progress = ProgressHUD.of(context); - progress!.showWithText("Please wait..."); - } - if (state is AssignAreaLoaded || - state is PassCheckErrorState) { - final progress = ProgressHUD.of(context); - progress!.dismiss(); - } - }, - builder: (context, state) { - if (state is AssignAreaLoaded) { - return Container( - height: screenHeight * .90, - padding: const EdgeInsets.symmetric( - horizontal: 42, vertical: 10), - child: FormBuilder( - key: _formKey, - child: Column( - children: [ - Flexible( - child: ListView( - children: [ - const SizedBox( - height: 32, - ), - SvgPicture.asset( - 'assets/svgs/switch.svg', - height: blockSizeVertical * 14, - allowDrawingOutsideViewBox: true, - ), - ListTile( - title: Text( - setQRScannerSettings, - style: Theme.of(context) - .textTheme - .titleLarge! - .copyWith(color: third), - textAlign: TextAlign.center, - ), - ), - Text(includeOtherInputs, - style: Theme.of(context) - .textTheme - .titleMedium), - Text( - includeOtherInputsSubTitle, + backgroundColor: Colors.black87, + child: BlocBuilder( + builder: (context, state) { + if (state is UserLoggedIn) { + token = state.userData!.user!.login!.token; + checkerId = state.userData!.user!.login!.user!.id; + return BlocConsumer( + listener: (context, state) { + if (state is PassCheckLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is AssignAreaLoaded || + state is PassCheckErrorState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + }, + builder: (context, state) { + if (state is AssignAreaLoaded) { + return Container( + height: screenHeight * .90, + padding: const EdgeInsets.symmetric( + horizontal: 42, vertical: 10), + child: FormBuilder( + key: _formKey, + child: Column( + children: [ + Flexible( + child: ListView( + children: [ + const SizedBox( + height: 32, + ), + SvgPicture.asset( + 'assets/svgs/switch.svg', + height: blockSizeVertical * 14, + allowDrawingOutsideViewBox: true, + ), + ListTile( + title: Text( + setQRScannerSettings, style: Theme.of(context) .textTheme - .bodySmall, + .titleLarge! + .copyWith(color: third), + textAlign: TextAlign.center, ), - SizedBox( - child: FittedBox( - child: CostumToggleSwitch( - activeBGColors: [ - Colors.green[800]!, - Colors.red[800]! - ], - initialLabelIndex: - _includeOtherInputs ? 0 : 1, - icons: const [ - Entypo.check, - ModernPictograms.cancel - ], - labels: const ['YES', 'NO'], - onToggle: (value) { - value == 0 - ? _includeOtherInputs = true - : _includeOtherInputs = false; - }, - ), - ), - ), - // Incoming or outgoing - Text(incomingORoutgoing, - style: Theme.of(context) - .textTheme - .titleMedium), - Text( - incomingORoutgoingSubTitle, + ), + Text(includeOtherInputs, style: Theme.of(context) .textTheme - .bodySmall, - ), - FittedBox( + .titleMedium), + Text( + includeOtherInputsSubTitle, + style: Theme.of(context) + .textTheme + .bodySmall, + ), + SizedBox( + child: FittedBox( child: CostumToggleSwitch( activeBGColors: [ - Colors.red[800]!, - Colors.green[800]! + Colors.green[800]!, + Colors.red[800]! ], initialLabelIndex: - scanMode == 'INCOMING' ? 0 : 1, + _includeOtherInputs ? 0 : 1, icons: const [ - Entypo.down_bold, - Entypo.up_bold, - ], - labels: const [ - 'INCOMING', - 'OUTGOING' + Entypo.check, + ModernPictograms.cancel ], + labels: const ['YES', 'NO'], onToggle: (value) { value == 0 - ? scanMode = 'INCOMING' - : scanMode = 'OUTGOING'; + ? _includeOtherInputs = true + : _includeOtherInputs = false; }, ), ), - const SizedBox( - height: 24, - ), - - ////STATION - Container( - child: state.roleId == 41 - ? DropdownButtonFormField( - isExpanded: true, - validator: FormBuilderValidators - .required( - errorText: - fieldIsRequired), - decoration: - normalTextFieldStyle( - "station", "station"), - items: state.assignedArea - .map((station) { - if (station.motherStation) { - return DropdownMenuItem< - dynamic>( - enabled: false, - value: station, - child: Text( - station.stationName - .toUpperCase(), - style: - const TextStyle( - color: Colors - .grey), - ), - ); - } else { - return DropdownMenuItem< - dynamic>( - value: station, - child: Padding( - padding: - const EdgeInsets - .only( - left: 10), - child: Text(station - .stationName), - ), - ); - } - }).toList(), - // value: selectedLevel, - onChanged: (value) async { - assignedArea = value; - }, - ////BARANGAY - ) - : state.roleId == 7 - ? Column( - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - Text( - "Select Barangay", - textAlign: - TextAlign.start, - style: - Theme.of(context) - .textTheme - .titleMedium, - ), - const SizedBox( - height: 12, - ), - DropdownButtonFormField( - isExpanded: true, - decoration: - normalTextFieldStyle( - "Barangay", - "Barangay"), - items: state - .assignedArea - .map((barangay) { - return DropdownMenuItem< - dynamic>( - value: barangay, - child: Padding( - padding: - const EdgeInsets - .only( - left: - 5), - child: Text( - barangay - .brgydesc), - ), - ); - }).toList(), - onChanged: (value) { - assignedArea = - value; - }, - ), - ], - ) - : - ////PUROK - state.roleId == 10 - ? Column( - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - Text( - "Select Purok", - textAlign: - TextAlign - .start, - style: Theme.of( - context) - .textTheme - .titleMedium, - ), - const SizedBox( - height: 12, - ), - DropdownButtonFormField( - isExpanded: true, - decoration: - normalTextFieldStyle( - "Purok", - "Purok"), - items: state - .assignedArea - .map((purok) { - return DropdownMenuItem< - dynamic>( - value: purok, - child: - Padding( - padding: const EdgeInsets - .only( - left: - 5), - child: Text( - purok - .purokdesc), - ), - ); - }).toList(), - onChanged: - (value) { - assignedArea = - value; - }, - ), - ], - ) - : - ////Registration InCharge - state.roleId == 22 - ? Column( - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - Text( - "Select Station", - textAlign: - TextAlign - .start, - style: Theme.of( - context) - .textTheme - .titleMedium, - ), - const SizedBox( - height: 12, - ), - DropdownButtonFormField( - isExpanded: - true, - validator: FormBuilderValidators - .required( - errorText: - fieldIsRequired), - decoration: normalTextFieldStyle( - "station", - "station"), - items: state - .assignedArea - .map( - (station) { - if (station - .motherStation) { - return DropdownMenuItem< - dynamic>( - enabled: - false, - value: - station, - child: - Text( - station - .stationName - .toUpperCase(), - style: - const TextStyle(color: Colors.grey), - ), - ); - } else { - return DropdownMenuItem< - dynamic>( - value: - station, - child: - Padding( - padding: - const EdgeInsets.only(left: 5), - child: - Text(station.stationName), - ), - ); - } - }).toList(), - // value: selectedLevel, - onChanged: - (value) async { - assignedArea = - value; - }, - ), - ], - ) - : ////QR Code Scanner - state.roleId == 13 - ? Column( - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - Text( - "Select Station", - textAlign: - TextAlign - .start, - style: Theme.of( - context) - .textTheme - .titleMedium, - ), - const SizedBox( - height: - 12, - ), - DropdownButtonFormField( - isExpanded: - true, - validator: - FormBuilderValidators.required( - errorText: fieldIsRequired), - decoration: normalTextFieldStyle( - "station", - "station"), - items: state - .assignedArea - .map( - (station) { - if (station - .motherStation) { - return DropdownMenuItem< - dynamic>( - enabled: - false, - value: - station, - child: - Text( - station.stationName.toUpperCase(), - style: const TextStyle(color: Colors.grey), - ), - ); - } else { - return DropdownMenuItem< - dynamic>( - value: - station, - child: - Padding( - padding: const EdgeInsets.only(left: 5), - child: Text(station.stationName), - ), - ); - } - }).toList(), - // value: selectedLevel, - onChanged: - (value) async { - assignedArea = - value; - }, - ), - ], - ) - : - ////Establishment Point-Person - state.roleId == 16 - ? Column( - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - Text( - "Select Agency", - textAlign: - TextAlign.start, - style: Theme.of(context) - .textTheme - .titleMedium, - ), - const SizedBox( - height: - 12, - ), - DropdownButtonFormField( - isExpanded: - true, - validator: - FormBuilderValidators.required(errorText: fieldIsRequired), - decoration: normalTextFieldStyle( - "Agency", - "Agency"), - items: state - .assignedArea - .map((agency) { - return DropdownMenuItem( - value: agency, - child: Padding( - padding: const EdgeInsets.only(left: 5), - child: Text(agency.area.name), - ), - ); - }).toList(), - // value: selectedLevel, - onChanged: - (value) async { - assignedArea = - value; - }, - ), - ], - ) - : ////Office Branch Chief - state.roleId == - 17 - ? Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - "Select Station", - textAlign: TextAlign.start, - style: Theme.of(context).textTheme.titleMedium, - ), - const SizedBox( - height: 12, - ), - DropdownButtonFormField( - isExpanded: true, - validator: FormBuilderValidators.required(errorText: fieldIsRequired), - decoration: normalTextFieldStyle("station", "station"), - items: state.assignedArea.map((station) { - if (station.motherStation) { - return DropdownMenuItem( - enabled: false, - value: station, - child: Text( - station.stationName.toUpperCase(), - style: const TextStyle(color: Colors.grey), - ), - ); - } else { - return DropdownMenuItem( - value: station, - child: Padding( - padding: const EdgeInsets.only(left: 5), - child: Text(station.stationName), - ), - ); - } - }).toList(), - // value: selectedLevel, - onChanged: (value) async { - assignedArea = value; - }, - ), - ], - ) - : Container()) - ], - ), - ), - SizedBox( - width: double.infinity, - height: 60, - child: ElevatedButton( - style: mainBtnStyle(primary, - Colors.transparent, Colors.white54), - child: const Text( - submit, - style: TextStyle(color: Colors.white), ), - onPressed: () { - if (_formKey.currentState! - .saveAndValidate()) { - print(scanMode); - print(_includeOtherInputs); - print(checkerId); - print(assignedArea); - Navigator.push(context, - MaterialPageRoute(builder: - (BuildContext context) { - return BlocProvider< - PassCheckBloc>.value( - value: PassCheckBloc() - ..add(SetScannerSettings( - token: token!, - assignedArea: assignedArea, - checkerId: checkerId!, - entranceExit: scanMode, - includeOtherInputs: - _includeOtherInputs, - roleId: state.roleId)), - child: const QRCodeScanner(), - ); - })); - } - }, + // Incoming or outgoing + Text(incomingORoutgoing, + style: Theme.of(context) + .textTheme + .titleMedium), + Text( + incomingORoutgoingSubTitle, + style: Theme.of(context) + .textTheme + .bodySmall, + ), + FittedBox( + child: CostumToggleSwitch( + activeBGColors: [ + Colors.red[800]!, + Colors.green[800]! + ], + initialLabelIndex: + scanMode == 'INCOMING' ? 0 : 1, + icons: const [ + Entypo.down_bold, + Entypo.up_bold, + ], + labels: const [ + 'INCOMING', + 'OUTGOING' + ], + onToggle: (value) { + value == 0 + ? scanMode = 'INCOMING' + : scanMode = 'OUTGOING'; + }, + ), + ), + const SizedBox( + height: 24, + ), + + ////STATION + Container( + child: state.roleId == 41 + ? DropdownButtonFormField( + isExpanded: true, + validator: FormBuilderValidators + .required( + errorText: + fieldIsRequired), + decoration: + normalTextFieldStyle( + "station", "station"), + items: state.assignedArea + .map((station) { + if (station.motherStation) { + return DropdownMenuItem< + dynamic>( + enabled: false, + value: station, + child: Text( + station.stationName + .toUpperCase(), + style: + const TextStyle( + color: Colors + .grey), + ), + ); + } else { + return DropdownMenuItem< + dynamic>( + value: station, + child: Padding( + padding: + const EdgeInsets + .only( + left: 10), + child: Text(station + .stationName), + ), + ); + } + }).toList(), + // value: selectedLevel, + onChanged: (value) async { + assignedArea = value; + }, + ////BARANGAY + ) + : state.roleId == 7 + ? Column( + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + Text( + "Select Barangay", + textAlign: + TextAlign.start, + style: + Theme.of(context) + .textTheme + .titleMedium, + ), + const SizedBox( + height: 12, + ), + DropdownButtonFormField( + isExpanded: true, + decoration: + normalTextFieldStyle( + "Barangay", + "Barangay"), + items: state + .assignedArea + .map((barangay) { + return DropdownMenuItem< + dynamic>( + value: barangay, + child: Padding( + padding: + const EdgeInsets + .only( + left: + 5), + child: Text( + barangay + .brgydesc), + ), + ); + }).toList(), + onChanged: (value) { + assignedArea = + value; + }, + ), + ], + ) + : + ////PUROK + state.roleId == 10 + ? Column( + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + Text( + "Select Purok", + textAlign: + TextAlign + .start, + style: Theme.of( + context) + .textTheme + .titleMedium, + ), + const SizedBox( + height: 12, + ), + DropdownButtonFormField( + isExpanded: true, + decoration: + normalTextFieldStyle( + "Purok", + "Purok"), + items: state + .assignedArea + .map((purok) { + return DropdownMenuItem< + dynamic>( + value: purok, + child: + Padding( + padding: const EdgeInsets + .only( + left: + 5), + child: Text( + purok + .purokdesc), + ), + ); + }).toList(), + onChanged: + (value) { + assignedArea = + value; + }, + ), + ], + ) + : + ////Registration InCharge + state.roleId == 22 + ? Column( + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + Text( + "Select Station", + textAlign: + TextAlign + .start, + style: Theme.of( + context) + .textTheme + .titleMedium, + ), + const SizedBox( + height: 12, + ), + DropdownButtonFormField( + isExpanded: + true, + validator: FormBuilderValidators + .required( + errorText: + fieldIsRequired), + decoration: normalTextFieldStyle( + "station", + "station"), + items: state + .assignedArea + .map( + (station) { + if (station + .motherStation) { + return DropdownMenuItem< + dynamic>( + enabled: + false, + value: + station, + child: + Text( + station + .stationName + .toUpperCase(), + style: + const TextStyle(color: Colors.grey), + ), + ); + } else { + return DropdownMenuItem< + dynamic>( + value: + station, + child: + Padding( + padding: + const EdgeInsets.only(left: 5), + child: + Text(station.stationName), + ), + ); + } + }).toList(), + // value: selectedLevel, + onChanged: + (value) async { + assignedArea = + value; + }, + ), + ], + ) + : ////QR Code Scanner + state.roleId == 13 + ? Column( + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + Text( + "Select Station", + textAlign: + TextAlign + .start, + style: Theme.of( + context) + .textTheme + .titleMedium, + ), + const SizedBox( + height: + 12, + ), + DropdownButtonFormField( + isExpanded: + true, + validator: + FormBuilderValidators.required( + errorText: fieldIsRequired), + decoration: normalTextFieldStyle( + "station", + "station"), + items: state + .assignedArea + .map( + (station) { + if (station + .motherStation) { + return DropdownMenuItem< + dynamic>( + enabled: + false, + value: + station, + child: + Text( + station.stationName.toUpperCase(), + style: const TextStyle(color: Colors.grey), + ), + ); + } else { + return DropdownMenuItem< + dynamic>( + value: + station, + child: + Padding( + padding: const EdgeInsets.only(left: 5), + child: Text(station.stationName), + ), + ); + } + }).toList(), + // value: selectedLevel, + onChanged: + (value) async { + assignedArea = + value; + }, + ), + ], + ) + : + ////Establishment Point-Person + state.roleId == 16 + ? Column( + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + Text( + "Select Agency", + textAlign: + TextAlign.start, + style: Theme.of(context) + .textTheme + .titleMedium, + ), + const SizedBox( + height: + 12, + ), + DropdownButtonFormField( + isExpanded: + true, + validator: + FormBuilderValidators.required(errorText: fieldIsRequired), + decoration: normalTextFieldStyle( + "Agency", + "Agency"), + items: state + .assignedArea + .map((agency) { + return DropdownMenuItem( + value: agency, + child: Padding( + padding: const EdgeInsets.only(left: 5), + child: Text(agency.area.name), + ), + ); + }).toList(), + // value: selectedLevel, + onChanged: + (value) async { + assignedArea = + value; + }, + ), + ], + ) + : ////Office Branch Chief + state.roleId == + 17 + ? Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + "Select Station", + textAlign: TextAlign.start, + style: Theme.of(context).textTheme.titleMedium, + ), + const SizedBox( + height: 12, + ), + DropdownButtonFormField( + isExpanded: true, + validator: FormBuilderValidators.required(errorText: fieldIsRequired), + decoration: normalTextFieldStyle("station", "station"), + items: state.assignedArea.map((station) { + if (station.motherStation) { + return DropdownMenuItem( + enabled: false, + value: station, + child: Text( + station.stationName.toUpperCase(), + style: const TextStyle(color: Colors.grey), + ), + ); + } else { + return DropdownMenuItem( + value: station, + child: Padding( + padding: const EdgeInsets.only(left: 5), + child: Text(station.stationName), + ), + ); + } + }).toList(), + // value: selectedLevel, + onChanged: (value) async { + assignedArea = value; + }, + ), + ], + ) + : Container()) + ], + ), + ), + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: mainBtnStyle(primary, + Colors.transparent, Colors.white54), + child: const Text( + submit, + style: TextStyle(color: Colors.white), ), + onPressed: () { + if (_formKey.currentState! + .saveAndValidate()) { + print(scanMode); + print(_includeOtherInputs); + print(checkerId); + print(assignedArea); + Navigator.push(context, + MaterialPageRoute(builder: + (BuildContext context) { + return BlocProvider< + PassCheckBloc>.value( + value: PassCheckBloc() + ..add(SetScannerSettings( + token: token!, + assignedArea: assignedArea, + checkerId: checkerId!, + entranceExit: scanMode, + includeOtherInputs: + _includeOtherInputs, + roleId: state.roleId)), + child: const QRCodeScanner(), + ); + })); + } + }, ), - const SizedBox( - height: 52, - ), - ], - ), + ), + const SizedBox( + height: 52, + ), + ], ), - ); - } - if (state is PassCheckErrorState) { - return SomethingWentWrong( - message: state.message, onpressed: () { - - context.read().add(GetPassCheckAreas(roleId: widget.roleId, userId: widget.userId)); - }); - } - return Container(); - }, - ); - } - return Container(); - }, - ), - )), - ); + ), + ); + } + if (state is PassCheckErrorState) { + return SomethingWentWrong( + message: state.message, onpressed: () { + + context.read().add(GetPassCheckAreas(roleId: widget.roleId, userId: widget.userId)); + }); + } + return Container(); + }, + ); + } + return Container(); + }, + ), + )); } } diff --git a/lib/screens/unit2/roles/rbac/add_rbac.dart b/lib/screens/unit2/roles/rbac/add_rbac.dart new file mode 100644 index 0000000..bf7fd3c --- /dev/null +++ b/lib/screens/unit2/roles/rbac/add_rbac.dart @@ -0,0 +1,82 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:unit2/theme-data.dart/box_shadow.dart'; + +import '../../../../theme-data.dart/btn-style.dart'; +import '../../../../theme-data.dart/colors.dart'; +import '../../../../theme-data.dart/form-style.dart'; + +class AddRbac extends StatefulWidget { + +final Function() onpressed; +final GlobalKey formKey; +final String title; + const AddRbac({super.key,required this.title,required this.onpressed, required this.formKey}); + + @override + State createState() => _AddRbacState(); +} + +class _AddRbacState extends State { + final formKey = GlobalKey(); + @override + Widget build(BuildContext context) { + return Container( + decoration: box1(), + child: Column(mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(height: 24,), + const Text("No result found"), + const SizedBox(height: 12,), + TextButton(onPressed: (){ + showDialog(context: context,builder: (BuildContext context) { + return AlertDialog( + title: Text(widget.title), + content: FormBuilder( + key: widget.formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + FormBuilderTextField( + name: "object_name", + decoration: normalTextFieldStyle("Object name *", "Object name "), + validator: FormBuilderValidators.required(errorText: "This field is required"), + ), + const SizedBox( + height: 8, + ), + FormBuilderTextField( + name: "slug", + decoration: normalTextFieldStyle("Slug ", "Slug"), + ), + const SizedBox( + height: 8, + ), + FormBuilderTextField( + name: "shorthand", + decoration: normalTextFieldStyle("Shorthand ", "Shorthand"), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + style: mainBtnStyle(primary, Colors.transparent, second), + onPressed: widget.onpressed, + child: const Text("Add"))), + ], + ), + ), + ); + }); + }, child: Text(widget.title)) + ], + ), + ); + } +} diff --git a/lib/screens/unit2/roles/rbac/rbac.dart b/lib/screens/unit2/roles/rbac/rbac.dart new file mode 100644 index 0000000..12fe372 --- /dev/null +++ b/lib/screens/unit2/roles/rbac/rbac.dart @@ -0,0 +1,746 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:multi_dropdown/multiselect_dropdown.dart'; +import 'package:searchable_paginated_dropdown/searchable_paginated_dropdown.dart'; +import 'package:searchfield/searchfield.dart'; +import 'package:unit2/bloc/rbac/rbac_bloc.dart'; +import 'package:unit2/bloc/user/user_bloc.dart'; +import 'package:unit2/model/rbac/new_permission.dart'; +import 'package:unit2/model/rbac/permission.dart'; +import 'package:unit2/model/rbac/rbac.dart'; +import 'package:unit2/theme-data.dart/btn-style.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/widgets/error_state.dart'; +import '../../../../model/profile/basic_information/primary-information.dart'; +import '../../../../sevices/roles/rbac_services.dart'; +import '../../../../theme-data.dart/box_shadow.dart'; +import '../../../../theme-data.dart/form-style.dart'; +import '../../../../utils/alerts.dart'; +import 'add_rbac.dart'; + +class RBACScreen extends StatefulWidget { + const RBACScreen({super.key}); + + @override + State createState() => _RBACScreenState(); +} + +class _RBACScreenState extends State { + ////roles + final roleFocusNode = FocusNode(); + final roleController = TextEditingController(); + RBAC? selectedRole; + + ////modules + final moduleFocusNode = FocusNode(); + final moduleController = TextEditingController(); + RBAC? selectedModule; + +////permissions + final permissionFocusNode = FocusNode(); + final permissionController = TextEditingController(); + List valueItemSelectedPermissions = []; + List valueItemPermission = []; + +////Object + RBAC? selectedObject; + final objectFocusNode = FocusNode(); + final objectController = TextEditingController(); + + ////operations + List operationsId = []; + List newOperations = []; + List valueItemOperation = []; + List selectedValueItemOperation = []; + + String? token; + + ////new permission + List newPermissions = []; + + final formKey = GlobalKey(); + final addRbacFormKey = GlobalKey(); + final newOperationKey = GlobalKey(); + int? selectedWebUserId; + bool showAddOperations = false; + @override + void dispose() { + moduleFocusNode.dispose(); + moduleController.dispose(); + roleFocusNode.dispose(); + roleController.dispose(); + permissionFocusNode.dispose(); + permissionController.dispose(); + objectFocusNode.dispose(); + objectController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + resizeToAvoidBottomInset: false, + appBar: AppBar( + title: const FittedBox( + child: Text("Role Based Access Control"), + ), + backgroundColor: primary, + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocBuilder( + builder: (context, state) { + if (state is UserLoggedIn) { + token = state.userData!.user!.login!.token; + return BlocConsumer( + listener: (context, state) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + if (state is RbacScreenSetted || state is RbacErrorState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + if (state is RbacAssignedState) { + if (state.responseStatus['success']) { + successAlert(context, "Assigning Successfull!", + state.responseStatus['message'], () { + Navigator.of(context).pop(); + context.read().add(LoadRbac()); + }); + } else { + errorAlert(context, "Assigning Failed!", + state.responseStatus['message'], () { + Navigator.of(context).pop(); + context.read().add(LoadRbac()); + }); + } + } + }, + builder: (context, state) { + if (state is RbacScreenSetted) { + //// permission value item + valueItemPermission = + state.permission.map((RBACPermission permission) { + return ValueItem( + label: + "${permission.operation?.name} - ${permission.object?.name!}", + value: permission.id.toString()); + }).toList(); + ////value item operation + valueItemOperation = + state.operations.map((RBAC operation) { + return ValueItem( + label: operation.name!, + value: operation.id.toString()); + }).toList(); + return Container( + padding: const EdgeInsets.symmetric( + vertical: 32, horizontal: 34), + child: FormBuilder( + key: formKey, + child: Column( + children: [ + const SizedBox( + height: 38, + ), + Flexible( + child: Column( + children: [ + ////users + SearchableDropdownFormField.paginated( + margin: const EdgeInsets.all(0), + trailingIcon: const Padding( + padding: EdgeInsets.only(right: 8), + child: Icon( + Icons.arrow_drop_down, + color: Colors.grey, + )), + hintText: const Padding( + padding: EdgeInsets.only(left: 8), + child: Text( + "Search User", + style: TextStyle( + color: Colors.grey, + fontSize: 16), + )), + searchHintText: "Search User", + backgroundDecoration: (child) { + return SizedBox( + width: double.infinity, + child: Container( + width: double.infinity, + height: 50, + decoration: BoxDecoration( + border: Border.all( + color: Colors.grey), + borderRadius: + const BorderRadius.all( + Radius.circular(5))), + child: child, + )); + }, + paginatedRequest: + (int page, String? searchKey) async { + List users = await RbacServices + .instance + .searchUser( + page: page, + name: searchKey ??= "", + token: token!); + return users.map((e) { + String fullname = + "${e.firstName} ${e.lastName}"; + return SearchableDropdownMenuItem< + Profile>( + label: fullname, + child: ListTile( + title: Text(fullname), + subtitle: + Text(e.birthdate.toString()), + ), + onTap: () { + setState(() { + selectedWebUserId = e.webuserId; + }); + }, + ); + }).toList(); + }, + ), + + const SizedBox( + height: 12, + ), + ////Role + StatefulBuilder( + builder: (context, setState) { + return SearchField( + itemHeight: 40, + suggestionsDecoration: box1(), + suggestions: state.role + .map((RBAC role) => + SearchFieldListItem( + role.name!, + item: role, + child: Padding( + padding: + const EdgeInsets + .symmetric( + horizontal: 10), + child: ListTile( + title: Text( + role.name!, + overflow: TextOverflow + .visible, + )), + ))) + .toList(), + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + focusNode: roleFocusNode, + searchInputDecoration: + normalTextFieldStyle("Role *", "") + .copyWith( + suffixIcon: IconButton( + icon: const Icon( + Icons.arrow_drop_down), + onPressed: () { + roleFocusNode.unfocus(); + }, + )), + onSuggestionTap: (role) { + setState(() { + selectedRole = role.item; + roleFocusNode.unfocus(); + }); + }, + ////Add new role + emptyWidget: AddRbac( + formKey: addRbacFormKey, + title: "Add Role", + onpressed: () { + RBAC? newRole; + if (addRbacFormKey.currentState! + .saveAndValidate()) { + newRole = RBAC( + id: null, + name: addRbacFormKey + .currentState + ?.value['object_name'], + slug: addRbacFormKey + .currentState + ?.value['slug'], + shorthand: addRbacFormKey + .currentState + ?.value['shorthand'], + fontawesomeIcon: null, + createdAt: null, + updatedAt: null, + createdBy: null, + updatedBy: null); + } + setState(() { + state.role.insert(0, newRole!); + }); + roleFocusNode.unfocus(); + Navigator.pop(context); + }, + )); + }), + const SizedBox( + height: 12, + ), + // //// Modules + StatefulBuilder( + builder: (context, setState) { + return SearchField( + itemHeight: 40, + suggestionsDecoration: box1(), + suggestions: state.modules + .map((RBAC module) => + SearchFieldListItem( + module.name!, + item: module, + child: Padding( + padding: + const EdgeInsets + .symmetric( + horizontal: 10), + child: ListTile( + title: Text( + module.name!, + overflow: TextOverflow + .visible, + )), + ))) + .toList(), + validator: (module) { + if (module!.isEmpty) { + return "This field is required"; + } + return null; + }, + focusNode: moduleFocusNode, + searchInputDecoration: + normalTextFieldStyle( + "Module *", "") + .copyWith( + suffixIcon: IconButton( + icon: const Icon( + Icons.arrow_drop_down), + onPressed: () { + moduleFocusNode.unfocus(); + }, + )), + onSuggestionTap: (module) { + setState(() { + selectedModule = module.item; + moduleFocusNode.unfocus(); + }); + }, + // //// Add new module + + emptyWidget: AddRbac( + formKey: addRbacFormKey, + title: "Add Module", + onpressed: () { + RBAC? newModule; + if (addRbacFormKey.currentState! + .saveAndValidate()) { + newModule = RBAC( + id: null, + name: addRbacFormKey + .currentState + ?.value['object_name'], + slug: addRbacFormKey + .currentState + ?.value['slug'], + shorthand: addRbacFormKey + .currentState + ?.value['shorthand'], + fontawesomeIcon: null, + createdAt: null, + updatedAt: null, + createdBy: null, + updatedBy: null); + } + setState(() { + state.modules + .insert(0, newModule!); + }); + moduleFocusNode.unfocus(); + Navigator.pop(context); + }, + )); + }), + const SizedBox( + height: 12, + ), + //// Permission + + StatefulBuilder( + builder: (context, setState) { + return SizedBox( + width: double.infinity, + child: Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular(5), + border: Border.all( + color: Colors.grey)), + child: Row( + children: [ + Expanded( + child: MultiSelectDropDown( + onOptionSelected: + (List + selectedOptions) { + setState(() { + valueItemSelectedPermissions = + selectedOptions; + }); + }, + hint: "Permissions", + hintStyle: const TextStyle( + fontSize: 16, + color: Colors.grey), + padding: + const EdgeInsets.all(8), + options: valueItemPermission, + selectionType: + SelectionType.multi, + chipConfig: const ChipConfig( + wrapType: + WrapType.scroll), + dropdownHeight: 300, + optionTextStyle: + const TextStyle( + fontSize: 16), + selectedOptionIcon: + const Icon( + Icons.check_circle), + ), + ), + const SizedBox( + width: 6, + ), + IconButton( + ////Add Permission not the dialog add button + onPressed: () { + final addPermissionFormKey = + GlobalKey(); + showDialog( + context: context, + builder: (BuildContext + context) { + String? objectname; + String? slug; + String? shorthand; + return AlertDialog( + title: Row( + children: [ + Expanded( + child: + Container( + child: !showAddOperations + ? const Text( + "Add new Permission") + : const Text( + "Add new Operation"), + ), + ), + ////close button + IconButton( + onPressed: + () { + setState( + () { + showAddOperations = + false; + }); + Navigator.pop( + context); + }, + icon: const Icon( + Icons + .close)) + ], + ), + content: StatefulBuilder( + builder: (context, + stateSetter) { + return showAddOperations + ////add permission content if choice is in the choices + ? FormBuilder( + key: + newOperationKey, + child: + Column( + mainAxisSize: + MainAxisSize.min, + children: [ + FormBuilderTextField( + onChanged: (value) { + objectname = value!; + }, + autovalidateMode: AutovalidateMode.always, + validator: FormBuilderValidators.required(errorText: "This field is required"), + name: "object_name", + decoration: normalTextFieldStyle("Object name *", "Object name "), + ), + const SizedBox( + height: 8, + ), + FormBuilderTextField( + onChanged: (value) { + slug = value!; + }, + name: "slug", + decoration: normalTextFieldStyle("Slug *", "Slug"), + ), + const SizedBox( + height: 8, + ), + FormBuilderTextField( + onChanged: (value) { + shorthand = value!; + }, + name: "shorthand", + decoration: normalTextFieldStyle("Shorthand *", "Shorthand"), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: double.infinity, + height: 50, + /////Add new Operation submit button + child: ElevatedButton( + style: mainBtnStyle(primary, Colors.transparent, second), + onPressed: () async { + if (newOperationKey.currentState!.saveAndValidate()) { + RBAC newOperation = RBAC(id: null, name: objectname, slug: slug, shorthand: shorthand, fontawesomeIcon: null, createdAt: null, updatedAt: null, createdBy: null, updatedBy: null); + stateSetter(() { + newOperations.add(newOperation); + valueItemOperation.insert(0, ValueItem(label: newOperation.name!, value: newOperation.name)); + showAddOperations = false; + }); + } + }, + child: const Text("Add"))), + ], + ), + ) + ////add permission content if choice is in the choices + : Form( + key: + addPermissionFormKey, + child: + Column( + mainAxisSize: + MainAxisSize.min, + children: [ + ////Object + SizedBox( + width: double.infinity, + child: + ////Row ofr operation and add operation + Row( + children: [ + ////Operations + Expanded( + child: MultiSelectDropDown( + onOptionSelected: (List selectedOptions) { + stateSetter(() { + ////get operation ids + selectedValueItemOperation = selectedOptions; + }); + }, + borderColor: Colors.grey, + borderWidth: 1, + borderRadius: 5, + hint: "Operations", + hintStyle: const TextStyle(fontSize: 16, color: Colors.grey), + padding: const EdgeInsets.all(8), + options: valueItemOperation, + selectionType: SelectionType.multi, + chipConfig: const ChipConfig(wrapType: WrapType.wrap), + dropdownHeight: 300, + optionTextStyle: const TextStyle(fontSize: 16), + selectedOptionIcon: const Icon(Icons.check_circle), + ), + ), + const SizedBox( + width: 5, + ), + Container( + decoration: BoxDecoration(border: Border.all(color: Colors.grey), borderRadius: BorderRadius.circular(5)), + child: IconButton( + ////Add Operation beside row button + onPressed: () { + stateSetter(() { + showAddOperations = true; + }); + }, + icon: const Icon(Icons.add)), + ) + ], + )), + const SizedBox( + height: 12, + ), + SearchField( + itemHeight: 40, + suggestionsDecoration: box1(), + suggestions: state.objects + .map((RBAC object) => SearchFieldListItem(object.name!, + item: object, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: ListTile( + title: Text( + object.name!, + overflow: TextOverflow.visible, + )), + ))) + .toList(), + validator: (module) { + if (module!.isEmpty) { + return "This field is required"; + } + return null; + }, + focusNode: objectFocusNode, + searchInputDecoration: normalTextFieldStyle("Object *", "").copyWith( + suffixIcon: IconButton( + icon: const Icon(Icons.arrow_drop_down), + onPressed: () { + objectFocusNode.unfocus(); + }, + )), + onSuggestionTap: (object) { + stateSetter(() { + selectedObject = object.item; + objectFocusNode.unfocus(); + }); + }, + ////Add new Object + emptyWidget: AddRbac( + formKey: addRbacFormKey, + title: "Add Add Object", + onpressed: () { + if (addRbacFormKey.currentState!.saveAndValidate()) { + RBAC? newObject; + + if (addRbacFormKey.currentState!.saveAndValidate()) { + newObject = RBAC(id: null, name: addRbacFormKey.currentState?.value['object_name'], slug: addRbacFormKey.currentState?.value['slug'], shorthand: addRbacFormKey.currentState?.value['shorthand'], fontawesomeIcon: null, createdAt: null, updatedAt: null, createdBy: null, updatedBy: null); + } + stateSetter(() { + state.objects.insert(0, newObject!); + }); + objectFocusNode.unfocus(); + Navigator.pop(context); + } + }, + )), + const SizedBox( + height: 20, + ), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + onPressed: () { + ////Add Operation + if (addPermissionFormKey.currentState!.validate()) { + selectedValueItemOperation.forEach((e) { + setState(() { + // state.permission.insert(0, Permission(id: null, object: selectedObject!, operation: RBAC(id: null, name: e.label, slug: null, shorthand: null, fontawesomeIcon: null, createdAt: null, updatedAt: null, createdBy: null, updatedBy: null), createdAt: null, updatedAt: null, createdBy: null, updatedBy: null)); + valueItemPermission.insert(0, ValueItem(label: "${selectedObject!.name} - ${e.label}")); + valueItemPermission = valueItemPermission; + }); + }); + Navigator.pop(context); + setState(() {}); + } + }, + style: mainBtnStyle(primary, Colors.transparent, second), + child: const Text("Submit"), + )) + ], + )); + })); + }); + }, + icon: const Icon(Icons.add)), + ], + ), + ), + ); + }), + const SizedBox( + height: 12, + ), + ], + )), + SizedBox( + height: 50, + width: double.infinity, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState! + .saveAndValidate()) { + ////existing permission + List permissions = + valueItemSelectedPermissions + .map((e) => + int.parse(e.value!)) + .toList(); + + context.read().add( + AssignedRbac( + assigneeId: + selectedWebUserId!, + assignerId: 63, + newPermissions: [], + permissionId: permissions, + selectedModule: + selectedModule, + selectedRole: selectedRole)); + } + print(valueItemSelectedPermissions + .length); + }, + child: const Text("submit")), + ) + ], + )), + ); + } + if (state is RbacErrorState) { + return SomethingWentWrong( + message: state.message, onpressed: () {}); + } + return Container(); + }, + ); + } + return Container(); + }, + ), + )); + } +} diff --git a/lib/screens/utils/formatters.dart b/lib/screens/utils/formatters.dart new file mode 100644 index 0000000..b9f78bb --- /dev/null +++ b/lib/screens/utils/formatters.dart @@ -0,0 +1 @@ +// TODO Implement this library. \ No newline at end of file diff --git a/lib/sevices/roles/pass_check_services.dart b/lib/sevices/roles/pass_check_services.dart index 3b076a6..8ff5c22 100644 --- a/lib/sevices/roles/pass_check_services.dart +++ b/lib/sevices/roles/pass_check_services.dart @@ -32,7 +32,7 @@ class PassCheckServices { 'X-Client-Key': xClientKey, 'X-Client-Secret': xClientSecret }; - try { + // try { http.Response response = await Request.instance .getRequest(param: params, headers: headers, path: path); if (response.statusCode == 200) { @@ -133,9 +133,9 @@ class PassCheckServices { statusResponse = assignedArea; } } - } catch (e) { - throw e.toString(); - } + // } catch (e) { + // throw e.toString(); + // } return statusResponse!; } diff --git a/lib/sevices/roles/rbac_operations/agency_services.dart b/lib/sevices/roles/rbac_operations/agency_services.dart new file mode 100644 index 0000000..3f8d713 --- /dev/null +++ b/lib/sevices/roles/rbac_operations/agency_services.dart @@ -0,0 +1,73 @@ +import 'dart:convert'; + +import 'package:unit2/screens/profile/components/other_information/org_membership/add_modal.dart'; +import 'package:unit2/utils/request.dart'; +import 'package:unit2/utils/urls.dart'; + +import '../../../model/utils/agency.dart'; +import 'package:http/http.dart' as http; + +class AgencyServices { + static final AgencyServices _instance = AgencyServices(); + static AgencyServices get instance => _instance; + String xClientKey = "unitK3CQaXiWlPReDsBzmmwBZPd9Re1z"; + String xClientKeySecret = "unitcYqAN7GGalyz"; + + Future> getAgencies() async { + List agencies = []; + String path = Url.instance.agencies(); + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + try { + http.Response response = await Request.instance + .getRequest(path: path, headers: headers, param: {}); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + for (var element in data['data']) { + Agency newAgency = Agency.fromJson(element); + agencies.add(newAgency); + } + } + } + } catch (e) { + throw e.toString(); + } + return agencies; + } + Future>add({required Agency agency})async{ + Map statusResponse = {}; + String path = Url.instance.postAgencies(); + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + Map body = { + "name":agency.name, + "category_id":agency.category!.id, + "private_entity":agency.privateEntity, + "contact_info":null, + }; + try{ + http.Response response = await Request.instance.postRequest(param: {},path: path, body: body,headers: headers); + if(response.statusCode == 201){ + Map data = jsonDecode(response.body); + statusResponse = data; + }else{ + Map data = jsonDecode(response.body); + String message = data['message']; + statusResponse.addAll({'message': message}); + statusResponse.addAll( + {'success': false}, + ); + } + }catch(e){ + throw e.toString(); + } + return statusResponse; + } +} diff --git a/lib/sevices/roles/rbac_operations/module_objects_services.dart b/lib/sevices/roles/rbac_operations/module_objects_services.dart new file mode 100644 index 0000000..87e9890 --- /dev/null +++ b/lib/sevices/roles/rbac_operations/module_objects_services.dart @@ -0,0 +1,98 @@ +import 'dart:convert'; + +import 'package:unit2/model/login_data/user_info/module.dart'; +import 'package:http/http.dart' as http; +import '../../../model/rbac/rbac_rbac.dart'; +import '../../../utils/request.dart'; +import '../../../utils/urls.dart'; + +class RbacModuleObjectsServices { + static final RbacModuleObjectsServices _instance = + RbacModuleObjectsServices(); + static RbacModuleObjectsServices get instance => _instance; + String xClientKey = "unitK3CQaXiWlPReDsBzmmwBZPd9Re1z"; + String xClientKeySecret = "unitcYqAN7GGalyz"; + Future> getModuleObjects() async { + List moduleObjects = []; + String path = Url.instance.getModuleObjects(); + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + try { + http.Response response = await Request.instance + .getRequest(param: {}, path: path, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + for (var modObj in data['data']) { + ModuleObjects newModObj = ModuleObjects.fromJson(modObj); + moduleObjects.add(newModObj); + } + } + } + } catch (e) { + throw e.toString(); + } + return moduleObjects; + } + + ////Add + Future> add({ + required int assignerId, + required int? moduleId, + required List objectsId, + }) async { + String path = Url.instance.getModuleObjects(); + Map statusResponse = {}; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + Map body = { + "module_id": moduleId, + "objects": objectsId, + "assigner_user_id": assignerId + }; + try { + http.Response response = await Request.instance + .postRequest(path: path, body: body, headers: headers, param: {}); + if (response.statusCode == 201) { + Map data = jsonDecode(response.body); + statusResponse = data; + } else { + Map data = jsonDecode(response.body); + String message = data['message']; + statusResponse.addAll({'message': message}); + statusResponse.addAll( + {'success': false}, + ); + } + } catch (e) { + throw e.toString(); + } + return statusResponse; + } + + Future deleteRbacModuleObject({required int moduleObjectId}) async { + bool success = false; + String path = "${Url.instance.getModuleObjects()}$moduleObjectId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + try { + http.Response response = await Request.instance + .deleteRequest(path: path, headers: headers, body: {}, param: {}); + if (response.statusCode == 200) { + success = true; + } + } catch (e) { + throw e.toString(); + } + return success; + } +} diff --git a/lib/sevices/roles/rbac_operations/module_services.dart b/lib/sevices/roles/rbac_operations/module_services.dart new file mode 100644 index 0000000..76ea4a1 --- /dev/null +++ b/lib/sevices/roles/rbac_operations/module_services.dart @@ -0,0 +1,146 @@ +import 'dart:convert'; + +import 'package:unit2/model/rbac/rbac.dart'; +import 'package:http/http.dart' as http; +import 'package:unit2/utils/request.dart'; +import '../../../utils/urls.dart'; + +class RbacModuleServices { + static final RbacModuleServices _instance = RbacModuleServices(); + static RbacModuleServices get instance => _instance; + String xClientKey = "unitK3CQaXiWlPReDsBzmmwBZPd9Re1z"; + String xClientKeySecret = "unitcYqAN7GGalyz"; + + Future> getRbacModule() async { + List modules = []; + String path = Url.instance.getModules(); + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + try { + http.Response response = await Request.instance + .getRequest(param: {}, path: path, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + for (var rbac in data['data']) { + RBAC newModule = RBAC.fromJson(rbac); + modules.add(newModule); + } + } + } + } catch (e) { + throw e.toString(); + } + + return modules; + } + ////Add + Future> add( + {required String name, + required String? slug, + required String? short, + required int id}) async { + String path = Url.instance.getModules(); + Map statusResponse = {}; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + String? newSlug = slug?.replaceAll(" ", "-"); + Map body = { + "name": name, + "slug": newSlug?.toLowerCase(), + "shorthand": short, + "fontawesome_icon":"mobile", + "created_by_id": id, + "updated_by_id": id + }; + try { + http.Response response = await Request.instance + .postRequest(path: path, body: body, headers: headers); + if (response.statusCode == 201) { + Map data = jsonDecode(response.body); + statusResponse = data; + } else { + Map data = jsonDecode(response.body); + String message = data['message']; + statusResponse.addAll({'message': message}); + statusResponse.addAll( + {'success': false}, + ); + } + } catch (e) { + throw e.toString(); + } + return statusResponse; + } + + ////Update + Future> update({ + required int moduleId, + required String name, + required String? slug, + required String? short, + required int? createdBy, + required int updatedBy, + }) async { + String path = "${Url.instance.getModules()}$moduleId/"; + Map statusResponse = {}; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + String? newSlug = slug?.replaceAll(" ", "-"); + Map body = { + "name": name, + "slug": newSlug?.toLowerCase(), + "shorthand": short, + "created_by_id": createdBy, + "updated_by_id": updatedBy, + "fontawesome_icon":"mobile", + }; + try { + http.Response response = await Request.instance + .putRequest(path: path, body: body, headers: headers, param: {}); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + statusResponse = data; + } else { + Map data = jsonDecode(response.body); + String message = data['message']; + statusResponse.addAll({'message': message}); + statusResponse.addAll( + {'success': false}, + ); + } + } catch (e) { + throw e.toString(); + } + return statusResponse; + } + + Future deleteRbacModule({required int moduleId}) async { + bool success = false; + String path = "${Url.instance.getModules()}$moduleId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + try { + http.Response response = await Request.instance + .deleteRequest(path: path, headers: headers, body: {}, param: {}); + if (response.statusCode == 200) { + success = true; + } + } catch (e) { + throw e.toString(); + } + return success; + } +} diff --git a/lib/sevices/roles/rbac_operations/object_services.dart b/lib/sevices/roles/rbac_operations/object_services.dart new file mode 100644 index 0000000..402b2ac --- /dev/null +++ b/lib/sevices/roles/rbac_operations/object_services.dart @@ -0,0 +1,144 @@ +import 'dart:convert'; + +import 'package:unit2/model/rbac/rbac.dart'; +import 'package:http/http.dart' as http; +import 'package:unit2/utils/request.dart'; +import '../../../utils/urls.dart'; + +class RbacObjectServices { + static final RbacObjectServices _instance = RbacObjectServices(); + static RbacObjectServices get instance => _instance; + String xClientKey = "unitK3CQaXiWlPReDsBzmmwBZPd9Re1z"; + String xClientKeySecret = "unitcYqAN7GGalyz"; + + Future> getRbacObjects() async { + List objects = []; + String path = Url.instance.getObject(); + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + try { + http.Response response = await Request.instance + .getRequest(param: {}, path: path, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + for (var rbac in data['data']) { + RBAC newRbac = RBAC.fromJson(rbac); + objects.add(newRbac); + } + } + } + } catch (e) { + throw e.toString(); + } + + return objects; + } + ////Add + Future> add( + {required String name, + required String? slug, + required String? short, + required int id}) async { + String path = Url.instance.getObject(); + Map statusResponse = {}; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + String? newSlug = slug?.replaceAll(" ", "-"); + Map body = { + "name": name, + "slug": newSlug?.toLowerCase(), + "shorthand": short, + "created_by_id": id, + "updated_by_id": id + }; + try { + http.Response response = await Request.instance + .postRequest(path: path, body: body, headers: headers); + if (response.statusCode == 201) { + Map data = jsonDecode(response.body); + statusResponse = data; + } else { + Map data = jsonDecode(response.body); + String message = data['message']; + statusResponse.addAll({'message': message}); + statusResponse.addAll( + {'success': false}, + ); + } + } catch (e) { + throw e.toString(); + } + return statusResponse; + } + + ////Update + Future> update({ + required int objectId, + required String name, + required String? slug, + required String? short, + required int? createdBy, + required int updatedBy, + }) async { + String path = "${Url.instance.getObject()}$objectId/"; + Map statusResponse = {}; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + String? newSlug = slug?.replaceAll(" ", "-"); + Map body = { + "name": name, + "slug": newSlug?.toLowerCase(), + "shorthand": short, + "created_by_id": createdBy, + "updated_by_id": updatedBy + }; + try { + http.Response response = await Request.instance + .putRequest(path: path, body: body, headers: headers, param: {}); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + statusResponse = data; + } else { + Map data = jsonDecode(response.body); + String message = data['message']; + statusResponse.addAll({'message': message}); + statusResponse.addAll( + {'success': false}, + ); + } + } catch (e) { + throw e.toString(); + } + return statusResponse; + } + + Future deleteRbacRole({required int objectId}) async { + bool success = false; + String path = "${Url.instance.getObject()}$objectId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + try { + http.Response response = await Request.instance + .deleteRequest(path: path, headers: headers, body: {}, param: {}); + if (response.statusCode == 200) { + success = true; + } + } catch (e) { + throw e.toString(); + } + return success; + } +} diff --git a/lib/sevices/roles/rbac_operations/operation_services.dart b/lib/sevices/roles/rbac_operations/operation_services.dart new file mode 100644 index 0000000..f737dc7 --- /dev/null +++ b/lib/sevices/roles/rbac_operations/operation_services.dart @@ -0,0 +1,144 @@ +import 'dart:convert'; + +import 'package:unit2/model/rbac/rbac.dart'; +import 'package:http/http.dart' as http; +import 'package:unit2/utils/request.dart'; +import '../../../utils/urls.dart'; + +class RbacOperationServices { + static final RbacOperationServices _instance = RbacOperationServices(); + static RbacOperationServices get instance => _instance; + String xClientKey = "unitK3CQaXiWlPReDsBzmmwBZPd9Re1z"; + String xClientKeySecret = "unitcYqAN7GGalyz"; + + Future> getRbacOperations() async { + List roles = []; + String path = Url.instance.getOperations(); + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + try { + http.Response response = await Request.instance + .getRequest(param: {}, path: path, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + for (var role in data['data']) { + RBAC newRole = RBAC.fromJson(role); + roles.add(newRole); + } + } + } + } catch (e) { + throw e.toString(); + } + + return roles; + } + ////Add + Future> add( + {required String name, + required String? slug, + required String? short, + required int id}) async { + String path = Url.instance.getOperations(); + Map statusResponse = {}; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + String? newSlug = slug?.replaceAll(" ", "-"); + Map body = { + "name": name, + "slug": newSlug?.toLowerCase(), + "shorthand": short, + "created_by_id": id, + "updated_by_id": id + }; + try { + http.Response response = await Request.instance + .postRequest(path: path, body: body, headers: headers); + if (response.statusCode == 201) { + Map data = jsonDecode(response.body); + statusResponse = data; + } else { + Map data = jsonDecode(response.body); + String message = data['message']; + statusResponse.addAll({'message': message}); + statusResponse.addAll( + {'success': false}, + ); + } + } catch (e) { + throw e.toString(); + } + return statusResponse; + } + + ////Update + Future> update({ + required int operationId, + required String name, + required String? slug, + required String? short, + required int? createdBy, + required int updatedBy, + }) async { + String path = "${Url.instance.getRbacOperations()}$operationId/"; + Map statusResponse = {}; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + String? newSlug = slug?.replaceAll(" ", "-"); + Map body = { + "name": name, + "slug": newSlug?.toLowerCase(), + "shorthand": short, + "created_by_id": createdBy, + "updated_by_id": updatedBy + }; + try { + http.Response response = await Request.instance + .putRequest(path: path, body: body, headers: headers, param: {}); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + statusResponse = data; + } else { + Map data = jsonDecode(response.body); + String message = data['message']; + statusResponse.addAll({'message': message}); + statusResponse.addAll( + {'success': false}, + ); + } + } catch (e) { + throw e.toString(); + } + return statusResponse; + } + + Future delete({required int operation}) async { + bool success = false; + String path = "${Url.instance.getRbacOperations()}$operation/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + try { + http.Response response = await Request.instance + .deleteRequest(path: path, headers: headers, body: {}, param: {}); + if (response.statusCode == 200) { + success = true; + } + } catch (e) { + throw e.toString(); + } + return success; + } +} diff --git a/lib/sevices/roles/rbac_operations/permission_service.dart b/lib/sevices/roles/rbac_operations/permission_service.dart new file mode 100644 index 0000000..aa49ba5 --- /dev/null +++ b/lib/sevices/roles/rbac_operations/permission_service.dart @@ -0,0 +1,100 @@ +import 'dart:convert'; + +import 'package:unit2/model/rbac/permission.dart'; +import 'package:unit2/model/rbac/rbac.dart'; +import 'package:http/http.dart' as http; +import 'package:unit2/utils/request.dart'; +import '../../../utils/urls.dart'; + +class RbacPermissionServices { + static final RbacPermissionServices _instance = RbacPermissionServices(); + static RbacPermissionServices get instance => _instance; + String xClientKey = "unitK3CQaXiWlPReDsBzmmwBZPd9Re1z"; + String xClientKeySecret = "unitcYqAN7GGalyz"; + + Future> getRbacPermission() async { + List permissions = []; + String path = Url.instance.getPersmissions(); + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + try { + http.Response response = await Request.instance + .getRequest(param: {}, path: path, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + for (var rbac in data['data']) { + RBACPermission newRbac = RBACPermission.fromJson(rbac); + permissions.add(newRbac); + } + } + } + } catch (e) { + throw e.toString(); + } + + return permissions; + } + + + ////Add + Future> add({ + required int assignerId, + required int? objectId, + required List operationsId, + }) async { + String path = Url.instance.getPersmissions(); + Map statusResponse = {}; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + Map body = { + "object_id": objectId, + "operations": operationsId, + "assigner_user_id": assignerId + }; + try { + http.Response response = await Request.instance + .postRequest(path: path, body: body, headers: headers, param: {}); + if (response.statusCode == 201) { + Map data = jsonDecode(response.body); + statusResponse = data; + } else { + Map data = jsonDecode(response.body); + String message = data['message']; + statusResponse.addAll({'message': message}); + statusResponse.addAll( + {'success': false}, + ); + } + } catch (e) { + throw e.toString(); + } + return statusResponse; + } + + Future deletePermission ({required int permissionId}) async { + bool success = false; + String path = "${Url.instance.getPersmissions()}$permissionId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + try { + http.Response response = await Request.instance + .deleteRequest(path: path, headers: headers, body: {}, param: {}); + if (response.statusCode == 200) { + success = true; + } + } catch (e) { + throw e.toString(); + } + return success; + } +} diff --git a/lib/sevices/roles/rbac_operations/role_assignment_services.dart b/lib/sevices/roles/rbac_operations/role_assignment_services.dart new file mode 100644 index 0000000..b9d6eae --- /dev/null +++ b/lib/sevices/roles/rbac_operations/role_assignment_services.dart @@ -0,0 +1,137 @@ +import 'dart:convert'; + +import 'package:unit2/model/rbac/assigned_role.dart'; +import 'package:unit2/model/rbac/rbac.dart'; +import 'package:unit2/utils/request.dart'; +import 'package:unit2/utils/urls.dart'; + +import 'package:http/http.dart' as http; + +import '../../../model/profile/basic_information/primary-information.dart'; + +class RbacRoleAssignmentServices { + static final RbacRoleAssignmentServices _instance = + RbacRoleAssignmentServices(); + static RbacRoleAssignmentServices get instance => _instance; + String xClientKey = "unitK3CQaXiWlPReDsBzmmwBZPd9Re1z"; + String xClientKeySecret = "unitcYqAN7GGalyz"; + + Future> getAssignedRoles( + {required String firstname, required String lastname}) async { + List assignedRoles = []; + String path = Url.instance.getRoleAssignment(); + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + Map param = { + "user__first_name__icontains": firstname, + "user__last_name__icontains": lastname + }; + try { + http.Response response = await Request.instance + .getRequest(param: param, path: path, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + for (var role in data['data']) { + AssignedRole newRole = AssignedRole.fromJson(role); + assignedRoles.add(newRole); + } + } + } + } catch (e) { + throw e.toString(); + } + return assignedRoles; + } + + Future deleteAssignedRole({required int roleId}) async { + bool success = false; + String path = "${Url.instance.getRoleAssignment()}$roleId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + try { + http.Response response = await Request.instance + .deleteRequest(path: path, headers: headers, body: {}, param: {}); + if (response.statusCode == 200) { + success = true; + } + } catch (e) { + throw e.toString(); + } + return success; + } + ////Add + Future> add({ + required int userId, + required int? assignerId, + required List roles, + }) async { + String path = Url.instance.getRoleAssignment(); + Map statusResponse = {}; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + Map body = { + "user_id": userId, + "roles": roles, + "assigner_user_id": assignerId, + }; + try { + http.Response response = await Request.instance + .postRequest(path: path, body: body, headers: headers, param: {}); + if (response.statusCode == 201) { + Map data = jsonDecode(response.body); + statusResponse = data; + } else { + Map data = jsonDecode(response.body); + String message = data['message']; + statusResponse.addAll({'message': message}); + statusResponse.addAll( + {'success': false}, + ); + } + } catch (e) { + throw e.toString(); + } + return statusResponse; + } + + Future searchUser( + {required int page, required String name, required String lastname}) async { + String path = Url.instance.searchUsers(); + Profile? user; + Map params = { + "profile__last_name__icontains": lastname, + "profile__first_name__icontains": name, + "page": page.toString(), + "is_paginated": "true", + }; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + + http.Response response = await Request.instance + .getRequest(param: params, path: path, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + for(var profile in data['data']) { + int websuerId = profile['webuserid']; + Profile newUser = Profile.fromJson(profile['profile']); + newUser.webuserId = websuerId; + user= newUser; + break; + } + } + + return user; + } + +} diff --git a/lib/sevices/roles/rbac_operations/role_extend_services.dart b/lib/sevices/roles/rbac_operations/role_extend_services.dart new file mode 100644 index 0000000..b5c0fc9 --- /dev/null +++ b/lib/sevices/roles/rbac_operations/role_extend_services.dart @@ -0,0 +1,96 @@ +import 'dart:convert'; + +import 'package:unit2/model/rbac/role_extend.dart'; +import 'package:unit2/model/rbac/role_under.dart'; +import 'package:http/http.dart' as http; +import '../../../utils/request.dart'; +import '../../../utils/urls.dart'; + +class RbacRoleExtendServices { + static final RbacRoleExtendServices _instance = RbacRoleExtendServices(); + static RbacRoleExtendServices get instance => _instance; + String xClientKey = "unitK3CQaXiWlPReDsBzmmwBZPd9Re1z"; + String xClientKeySecret = "unitcYqAN7GGalyz"; + + Future> getRolesExtend() async { + List rolesextend = []; + String path = Url.instance.getRoleExtend(); + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + // try { + http.Response response = await Request.instance + .getRequest(param: {}, path: path, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + for (var roleExt in data['data']) { + RolesExtend newRoleExtend = RolesExtend.fromJson(roleExt); + rolesextend.add(newRoleExtend); + } + } + } + // } catch (e) { + // throw e.toString(); + // } + return rolesextend; + } + + ////Add + Future> add({ + required int? roleId, + required List rolesExtendsId, + }) async { + String path = Url.instance.getRoleExtend(); + Map statusResponse = {}; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + Map body = { + "role_main_id": roleId, + "roles_extend": rolesExtendsId, + }; + try { + http.Response response = await Request.instance + .postRequest(path: path, body: body, headers: headers, param: {}); + if (response.statusCode == 201) { + Map data = jsonDecode(response.body); + statusResponse = data; + } else { + Map data = jsonDecode(response.body); + String message = data['message']; + statusResponse.addAll({'message': message}); + statusResponse.addAll( + {'success': false}, + ); + } + } catch (e) { + throw e.toString(); + } + return statusResponse; + } + + Future delete({required int roleExtendId}) async { + bool success = false; + String path = "${Url.instance.getRoleExtend()}$roleExtendId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + try { + http.Response response = await Request.instance + .deleteRequest(path: path, headers: headers, body: {}, param: {}); + if (response.statusCode == 200) { + success = true; + } + } catch (e) { + throw e.toString(); + } + return success; + } +} diff --git a/lib/sevices/roles/rbac_operations/role_module_services.dart b/lib/sevices/roles/rbac_operations/role_module_services.dart new file mode 100644 index 0000000..27a0779 --- /dev/null +++ b/lib/sevices/roles/rbac_operations/role_module_services.dart @@ -0,0 +1,100 @@ +import 'dart:convert'; + +import 'package:unit2/model/login_data/user_info/module.dart'; +import 'package:http/http.dart' as http; +import 'package:unit2/model/rbac/role_module.dart'; +import '../../../model/rbac/rbac_rbac.dart'; +import '../../../utils/request.dart'; +import '../../../utils/urls.dart'; + +class RbacRoleModuleServices { + static final RbacRoleModuleServices _instance = + RbacRoleModuleServices(); + static RbacRoleModuleServices get instance => _instance; + String xClientKey = "unitK3CQaXiWlPReDsBzmmwBZPd9Re1z"; + String xClientKeySecret = "unitcYqAN7GGalyz"; + + Future> getRoleModules() async { + List roleModules = []; + String path = Url.instance.getRoleModules(); + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + try { + http.Response response = await Request.instance + .getRequest(param: {}, path: path, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + for (var roleMod in data['data']) { + RoleModules newRoleMod = RoleModules.fromJson(roleMod); + roleModules.add(newRoleMod); + } + } + } + } catch (e) { + throw e.toString(); + } + return roleModules; + } + + ////Add + Future> add({ + required int assignerId, + required int? roleId, + required List moduleIds, + }) async { + String path = Url.instance.getRoleModules(); + Map statusResponse = {}; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + Map body = { + "role_id": roleId, + "modules": moduleIds, + "assigner_user_id": assignerId + }; + try { + http.Response response = await Request.instance + .postRequest(path: path, body: body, headers: headers, param: {}); + if (response.statusCode == 201) { + Map data = jsonDecode(response.body); + statusResponse = data; + } else { + Map data = jsonDecode(response.body); + String message = data['message']; + statusResponse.addAll({'message': message}); + statusResponse.addAll( + {'success': false}, + ); + } + } catch (e) { + throw e.toString(); + } + return statusResponse; + } + + Future deleteRbacRoleModule({required int moduleObjectId}) async { + bool success = false; + String path = "${Url.instance.getRoleModules()}$moduleObjectId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + try { + http.Response response = await Request.instance + .deleteRequest(path: path, headers: headers, body: {}, param: {}); + if (response.statusCode == 200) { + success = true; + } + } catch (e) { + throw e.toString(); + } + return success; + } +} diff --git a/lib/sevices/roles/rbac_operations/role_services.dart b/lib/sevices/roles/rbac_operations/role_services.dart new file mode 100644 index 0000000..08a689c --- /dev/null +++ b/lib/sevices/roles/rbac_operations/role_services.dart @@ -0,0 +1,146 @@ +import 'dart:convert'; + +import 'package:unit2/model/rbac/rbac.dart'; +import 'package:unit2/sevices/profile/education_services.dart'; +import 'package:http/http.dart' as http; +import 'package:unit2/utils/request.dart'; +import '../../../utils/urls.dart'; + +class RbacRoleServices { + static final RbacRoleServices _instance = RbacRoleServices(); + static RbacRoleServices get instance => _instance; + String xClientKey = "unitK3CQaXiWlPReDsBzmmwBZPd9Re1z"; + String xClientKeySecret = "unitcYqAN7GGalyz"; + + Future> getRbacRoles() async { + List roles = []; + String path = Url.instance.getRbacRoles(); + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + try { + http.Response response = await Request.instance + .getRequest(param: {}, path: path, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + for (var role in data['data']) { + RBAC newRole = RBAC.fromJson(role); + roles.add(newRole); + } + } + } + } catch (e) { + throw e.toString(); + } + + return roles; + } + +////Add + Future> add( + {required String name, + required String? slug, + required String? short, + required int id}) async { + String path = Url.instance.getRbacRoles(); + Map statusResponse = {}; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + String? newSlug = slug?.replaceAll(" ", "-"); + Map body = { + "name": name, + "slug": newSlug?.toLowerCase(), + "shorthand": short, + "created_by_id": id, + "updated_by_id": id + }; + try { + http.Response response = await Request.instance + .postRequest(path: path, body: body, headers: headers); + if (response.statusCode == 201) { + Map data = jsonDecode(response.body); + statusResponse = data; + } else { + Map data = jsonDecode(response.body); + String message = data['message']; + statusResponse.addAll({'message': message}); + statusResponse.addAll( + {'success': false}, + ); + } + } catch (e) { + throw e.toString(); + } + return statusResponse; + } + + ////Update + Future> update({ + required int roleId, + required String name, + required String? slug, + required String? short, + required int? createdBy, + required int updatedBy, + }) async { + String path = "${Url.instance.getRbacRoles()}$roleId/"; + Map statusResponse = {}; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + String? newSlug = slug?.replaceAll(" ", "-"); + Map body = { + "name": name, + "slug": newSlug?.toLowerCase(), + "shorthand": short, + "created_by_id": createdBy, + "updated_by_id": updatedBy + }; + try { + http.Response response = await Request.instance + .putRequest(path: path, body: body, headers: headers, param: {}); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + statusResponse = data; + } else { + Map data = jsonDecode(response.body); + String message = data['message']; + statusResponse.addAll({'message': message}); + statusResponse.addAll( + {'success': false}, + ); + } + } catch (e) { + throw e.toString(); + } + return statusResponse; + } + + Future deleteRbacRole({required int roleId}) async { + bool success = false; + String path = "${Url.instance.getRbacRoles()}$roleId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + try { + http.Response response = await Request.instance + .deleteRequest(path: path, headers: headers, body: {}, param: {}); + if (response.statusCode == 200) { + success = true; + } + } catch (e) { + throw e.toString(); + } + return success; + } +} diff --git a/lib/sevices/roles/rbac_operations/roles_under_services.dart b/lib/sevices/roles/rbac_operations/roles_under_services.dart new file mode 100644 index 0000000..719dab4 --- /dev/null +++ b/lib/sevices/roles/rbac_operations/roles_under_services.dart @@ -0,0 +1,96 @@ +import 'dart:convert'; + +import 'package:unit2/model/rbac/role_under.dart'; +import 'package:http/http.dart' as http; +import '../../../utils/request.dart'; +import '../../../utils/urls.dart'; + +class RbacRoleUnderServices { + static final RbacRoleUnderServices _instance = RbacRoleUnderServices(); + static RbacRoleUnderServices get instance => _instance; + String xClientKey = "unitK3CQaXiWlPReDsBzmmwBZPd9Re1z"; + String xClientKeySecret = "unitcYqAN7GGalyz"; + + Future> getRolesUnder() async { + List rolesUnder = []; + String path = Url.instance.getRolesUnder(); + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + try { + http.Response response = await Request.instance + .getRequest(param: {}, path: path, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + for (var roleUnd in data['data']) { + RolesUnder newRoleUnder = RolesUnder.fromJson(roleUnd); + rolesUnder.add(newRoleUnder); + } + } + } + } catch (e) { + throw e.toString(); + } + return rolesUnder; + } + + ////Add + Future> add({ + required int? roleId, + required List rolesId, + }) async { + String path = Url.instance.getRolesUnder(); + Map statusResponse = {}; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + Map body = { + "role_main_id": roleId, + "roles_under": rolesId, + }; + try { + http.Response response = await Request.instance + .postRequest(path: path, body: body, headers: headers, param: {}); + if (response.statusCode == 201) { + Map data = jsonDecode(response.body); + statusResponse = data; + } else { + Map data = jsonDecode(response.body); + String message = data['message']; + statusResponse.addAll({'message': message}); + statusResponse.addAll( + {'success': false}, + ); + } + } catch (e) { + throw e.toString(); + } + return statusResponse; + } + + + Future deleteRbacRoleUnder({required int roleUnderId}) async { + bool success = false; + String path = "${Url.instance.getRolesUnder ()}$roleUnderId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + try { + http.Response response = await Request.instance + .deleteRequest(path: path, headers: headers, body: {}, param: {}); + if (response.statusCode == 200) { + success = true; + } + } catch (e) { + throw e.toString(); + } + return success; + } +} diff --git a/lib/sevices/roles/rbac_operations/station_services.dart b/lib/sevices/roles/rbac_operations/station_services.dart new file mode 100644 index 0000000..9170583 --- /dev/null +++ b/lib/sevices/roles/rbac_operations/station_services.dart @@ -0,0 +1,39 @@ +import 'dart:convert'; +import 'package:unit2/utils/request.dart'; +import 'package:unit2/utils/urls.dart'; +import 'package:http/http.dart' as http; + +import '../../../model/rbac/rbac_station.dart'; +import '../../../model/roles/pass_check/station_assign_area.dart'; +class RbacStationServices{ + static final RbacStationServices _instance = RbacStationServices(); + static RbacStationServices get instance => _instance; + String xClientKey = "unitK3CQaXiWlPReDsBzmmwBZPd9Re1z"; + String xClientKeySecret = "unitcYqAN7GGalyz"; + + Future> getStations({required int agencyId})async{ + List stations = []; + String path = Url.instance.getStation(); + Map param = {"government_agency_id":agencyId.toString()}; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + try{ + http.Response response = await Request.instance.getRequest(param: param,path: path,headers: headers); + if(response.statusCode == 200){ + Map data = jsonDecode(response.body); + if(data['data'] != null){ + for(var station in data['data']){ + RbacStation area = RbacStation.fromJson(station); + stations.add(area); + } + } + } + }catch(e){ + throw e.toString(); + } + return stations; + } +} \ No newline at end of file diff --git a/lib/sevices/roles/rbac_services.dart b/lib/sevices/roles/rbac_services.dart new file mode 100644 index 0000000..da172e4 --- /dev/null +++ b/lib/sevices/roles/rbac_services.dart @@ -0,0 +1,218 @@ +import 'dart:convert'; + +import 'package:dio/dio.dart'; +import 'package:unit2/model/rbac/permission.dart'; +import 'package:unit2/model/rbac/rbac.dart'; +import 'package:unit2/utils/request.dart'; + +import '../../model/profile/basic_information/primary-information.dart'; +import '../../model/rbac/new_permission.dart'; +import '../../utils/global.dart'; +import '../../utils/urls.dart'; +import 'package:http/http.dart' as http; + +class RbacServices { + static final RbacServices _instance = RbacServices(); + static RbacServices get instance => _instance; + Future> getModules() async { + List modules = []; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientSecret + }; + String path = Url.instance.getModules(); + try { + http.Response response = await Request.instance + .getRequest(path: path, headers: headers, param: {}); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + data['data'].forEach((var module) { + print(module); + RBAC newModule = RBAC.fromJson(module); + modules.add(newModule); + }); + } + } catch (e) { + throw e.toString(); + } + return modules; + } + + Future> getObjects() async { + List objects = []; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientSecret + }; + String path = Url.instance.getObject(); + try { + http.Response response = await Request.instance + .getRequest(path: path, headers: headers, param: {}); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + data['data'].forEach((var object) { + RBAC newObject = RBAC.fromJson(object); + objects.add(newObject); + }); + } + } catch (e) { + throw e.toString(); + } + return objects; + } + + Future> getRole() async { + List roles = []; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientSecret + }; + String path = Url.instance.getRoles(); + try { + http.Response response = await Request.instance + .getRequest(path: path, headers: headers, param: {}); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + data['data'].forEach((var role) { + RBAC newRole = RBAC.fromJson(role); + roles.add(newRole); + }); + } + } catch (e) { + throw e.toString(); + } + return roles; + } + + Future> getPermission() async { + List permissions = []; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientSecret + }; + String path = Url.instance.getPersmissions(); + try { + http.Response response = await Request.instance + .getRequest(path: path, headers: headers, param: {}); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + data['data'].forEach((var permission) { + RBACPermission newPermission = RBACPermission.fromJson(permission); + permissions.add(newPermission); + }); + } + } catch (e) { + throw e.toString(); + } + return permissions; + } + + Future> getOperations() async { + List operations = []; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientSecret + }; + String path = Url.instance.getOperations(); + try { + http.Response response = await Request.instance + .getRequest(path: path, headers: headers, param: {}); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + data['data'].forEach((var operation) { + RBAC newOperation = RBAC.fromJson(operation); + operations.add(newOperation); + }); + } + } catch (e) { + throw e.toString(); + } + return operations; + } + + Future> searchUser( + {required int page, required String name, required String token}) async { + List users = []; + String path = Url.instance.searchUsers(); + String authtoken = "Token $token"; + Map params = { + "profile__last_name__icontains": name, + "page": page.toString(), + "is_paginated": "true", + }; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authtoken + }; + + http.Response response = await Request.instance + .getRequest(param: params, path: path, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + data['data'].forEach((profile) { + int websuerId = profile['webuserid']; + Profile newUsers = Profile.fromJson(profile['profile']); + newUsers.webuserId = websuerId; + users.add(newUsers); + }); + } + + return users; + } + + Future> assignRBAC( + {required int assigneeId, + required int assignerId, + required RBAC? selectedRole, + required RBAC? selectedModule, + required List permissionId, + required List newPermissions}) async { + bool success = false; + String path = Url.instance.assignRbac(); + Map responseStatus = {}; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientSecret + }; + Map body = { + "assignee_user_id": assigneeId, + "assigner_user_id": assignerId, + "role_id": selectedRole?.id, + "_new_role_name": selectedRole?.name, + "_new_role_slug": selectedRole?.slug, + "_new_role_shorthand": selectedRole?.shorthand, + "module_id": selectedModule?.id, + "_new_module_name": selectedModule?.name, + "_new_module_slug": selectedModule?.slug, + "_new_module_shorthand": selectedModule?.shorthand, + "_new_module_icon": null, + "permission_ids": permissionId, + "_new_permissions": newPermissions, + "assigned_areas": [] + }; + try { + http.Response response = await Request.instance + .postRequest(path: path, param: {}, body: body, headers: headers); + Map data = jsonDecode(response.body); + if (response.statusCode == 201) { + success = true; + String message = data['message']; + responseStatus.addAll({"success": success, "message": message}); + } else { + success = false; + String message = data['message']; + responseStatus.addAll({"success": success, "message": message}); + } + } catch (e) { + throw e.toString(); + } + + return responseStatus; + } +} diff --git a/lib/utils/app_router.dart b/lib/utils/app_router.dart index 3bf9c9b..610fd3f 100644 --- a/lib/utils/app_router.dart +++ b/lib/utils/app_router.dart @@ -4,10 +4,12 @@ import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/role/pass_check/pass_check_bloc.dart'; import 'package:unit2/bloc/sos/sos_bloc.dart'; import 'package:unit2/screens/sos/index.dart'; -import 'package:unit2/screens/unit2/homepage.dart/components/dashboard.dart'; +import 'package:unit2/screens/unit2/homepage.dart/components/dashboard/dashboard.dart'; import 'package:unit2/screens/unit2/homepage.dart/components/menu.dart'; import 'package:unit2/screens/unit2/login/login.dart'; +import 'package:unit2/screens/unit2/roles/rbac/rbac.dart'; import 'package:unit2/utils/global_context.dart'; +import '../bloc/rbac/rbac_bloc.dart'; import '../bloc/user/user_bloc.dart'; import '../screens/profile/profile.dart'; import '../screens/unit2/basic-info/basic-info.dart'; @@ -62,6 +64,12 @@ class AppRouter { child: QRCodeScannerSettings(roleId: arguments.roleId, userId: arguments.userId,), ); }); + case '/rbac': + return MaterialPageRoute(builder: (BuildContext context){ + return BlocProvider( + create: (_) => RbacBloc()..add(SetRbacScreen()), + child:const RBACScreen(), + );}); default: return MaterialPageRoute(builder: (context) { return Container(); diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index 5dea2f3..9669a61 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -4,11 +4,11 @@ class Url { static Url get instance => _instance; String host() { - // return '192.168.10.183:3000'; - return 'agusandelnorte.gov.ph'; + // return '192.168.10.183:3000'; + // return 'agusandelnorte.gov.ph'; // return "192.168.10.219:3000"; // return "192.168.10.241"; - // return "192.168.10.221:3004"; + return "192.168.10.221:3004"; // return "playweb.agusandelnorte.gov.ph"; // return 'devapi.agusandelnorte.gov.ph:3004'; } @@ -17,211 +17,306 @@ class Url { return '/api/account/auth/login/'; } - String profileInformation(){ + String profileInformation() { return 'api/jobnet_app/profile/pds/'; } - String latestApk(){ + String latestApk() { return "/api/system_app/apk_version/latest"; } ////SOS paths - String sosRequest(){ + String sosRequest() { return "/api/sos_app/sos_request/"; } //// DOCSMS paths - String getDocument(){ + String getDocument() { return "/api/web_app/public/document_viewer/"; } ////ELIGIBILITIES PATHS -String eligibilities(){ - return "/api/jobnet_app/eligibilities/"; -} + String eligibilities() { + return "/api/jobnet_app/eligibilities/"; + } -String getEligibilities(){ - return "/api/jobnet_app/profile/pds/eligibility/"; -} + String getEligibilities() { + return "/api/jobnet_app/profile/pds/eligibility/"; + } -String addEligibility(){ - return "/api/jobnet_app/profile/pds/eligibility/"; -} -String deleteEligibility(){ - return "/api/jobnet_app/profile/pds/eligibility/"; -} + String addEligibility() { + return "/api/jobnet_app/profile/pds/eligibility/"; + } + + String deleteEligibility() { + return "/api/jobnet_app/profile/pds/eligibility/"; + } + + String updateEligibility() { + return "/api/jobnet_app/profile/pds/eligibility/"; + } -String updateEligibility(){ - return "/api/jobnet_app/profile/pds/eligibility/"; -} //// work history paths -String workhistory(){ - return "/api/jobnet_app/profile/pds/work/"; -} -String getPositions(){ - return "/api/jobnet_app/positions/"; -} -String getAgencies(){ + String workhistory() { + return "/api/jobnet_app/profile/pds/work/"; + } + + String getPositions() { + return "/api/jobnet_app/positions/"; + } + + String getAgencies() { return "/api/jobnet_app/agencies/"; -} + } -String getAgencyCategory(){ - return "api/jobnet_app/agency_categories/"; -} - -String identifications(){ - return "/api/jobnet_app/profile/pds/basic/identification/"; -} + String getAgencyCategory() { + return "api/jobnet_app/agency_categories/"; + } + String identifications() { + return "/api/jobnet_app/profile/pds/basic/identification/"; + } ////educational background paths -String educationalBackground(){ - return "/api/jobnet_app/profile/pds/education/"; -} -String getSchools(){ - return "/api/jobnet_app/schools/"; -} -String getPrograms(){ - return "api/jobnet_app/education_programs/"; -} -String getHonors(){ - return "/api/jobnet_app/honors"; -} + String educationalBackground() { + return "/api/jobnet_app/profile/pds/education/"; + } + + String getSchools() { + return "/api/jobnet_app/schools/"; + } + + String getPrograms() { + return "api/jobnet_app/education_programs/"; + } + + String getHonors() { + return "/api/jobnet_app/honors"; + } //// learning and development paths -String learningAndDevelopments(){ - return "api/jobnet_app/profile/pds/learning_development/"; -} -String conductedTrainings(){ - return "api/jobnet_app/conducted_trainings/"; -} -String learningAndDevelopmentType(){ - return "api/jobnet_app/learning_development/"; -} -String learningAndDevelopmentTopics(){ - return "api/jobnet_app/training_topics/"; -} + String learningAndDevelopments() { + return "api/jobnet_app/profile/pds/learning_development/"; + } + + String conductedTrainings() { + return "api/jobnet_app/conducted_trainings/"; + } + + String learningAndDevelopmentType() { + return "api/jobnet_app/learning_development/"; + } + + String learningAndDevelopmentTopics() { + return "api/jobnet_app/training_topics/"; + } //// references paths -String reference(){ - return "/api/jobnet_app/profile/pds/personal_reference/"; -} - + String reference() { + return "/api/jobnet_app/profile/pds/personal_reference/"; + } ////voluntary works -String getVoluntaryWorks(){ - return "/api/jobnet_app/profile/pds/voluntary_work/"; -} + String getVoluntaryWorks() { + return "/api/jobnet_app/profile/pds/voluntary_work/"; + } //// skills hobbies -String skillsHobbies(){ - return "/api/jobnet_app/profile/pds/other/skill_hobby/"; -} -String getAllSkillsHobbies(){ - return "/api/jobnet_app/skill_hobby/"; -} + String skillsHobbies() { + return "/api/jobnet_app/profile/pds/other/skill_hobby/"; + } + + String getAllSkillsHobbies() { + return "/api/jobnet_app/skill_hobby/"; + } + //// orgmemberships -String getOrgMemberShips(){ - return "/api/jobnet_app/profile/pds/other/org_membership/"; -} + String getOrgMemberShips() { + return "/api/jobnet_app/profile/pds/other/org_membership/"; + } ////non academic recognition -String getNonAcademicRecognition(){ - return "/api/jobnet_app/profile/pds/other/non_acad_recognition/"; -} + String getNonAcademicRecognition() { + return "/api/jobnet_app/profile/pds/other/non_acad_recognition/"; + } ////citizenship -String citizenship(){ - return "/api/jobnet_app/profile/pds/basic/citizenship/"; -} + String citizenship() { + return "/api/jobnet_app/profile/pds/basic/citizenship/"; + } ////family paths -String getFamilies(){ - return "/api/jobnet_app/profile/pds/family/"; -} -String addEmergency(){ - return "/api/profile_app/person_emergency/"; -} -String getRelationshipTypes(){ - return "/api/jobnet_app/relationship_types"; -} -String updatePersonalInfor(){ - return "/api/jobnet_app/profile/pds/basic/personal/"; -} + String getFamilies() { + return "/api/jobnet_app/profile/pds/family/"; + } + String addEmergency() { + return "/api/profile_app/person_emergency/"; + } + + String getRelationshipTypes() { + return "/api/jobnet_app/relationship_types"; + } + + String updatePersonalInfor() { + return "/api/jobnet_app/profile/pds/basic/personal/"; + } //// contacts path -String getServiceTypes(){ - return "/api/jobnet_app/comm_service_type/"; + String getServiceTypes() { + return "/api/jobnet_app/comm_service_type/"; + } -} //// address path -String addressPath(){ - return "/api/jobnet_app/profile/pds/basic/address/"; -} + String addressPath() { + return "/api/jobnet_app/profile/pds/basic/address/"; + } + + String contactPath() { + return "/api/jobnet_app/profile/pds/basic/contact/"; + } + + String getCommunicationProvider() { + return "/api/jobnet_app/comm_services/"; + } + + String deleteContact() { + return "/api/jobnet_app/profile/pds/basic/contact/"; + } -String contactPath(){ - return "/api/jobnet_app/profile/pds/basic/contact/"; -} -String getCommunicationProvider(){ - return "/api/jobnet_app/comm_services/"; -} -String deleteContact (){ - return "/api/jobnet_app/profile/pds/basic/contact/"; -} ////profile other info -String getReligions(){ - return "/api/profile_app/religion/"; -} -String getEthnicity(){ - return "/api/profile_app/ethnicity/"; -} + String getReligions() { + return "/api/profile_app/religion/"; + } -String getDisability(){ - return "api/profile_app/disability/"; -} + String getEthnicity() { + return "/api/profile_app/ethnicity/"; + } -String getIndigency(){ - return "/api/profile_app/indigenous/"; -} + String getDisability() { + return "api/profile_app/disability/"; + } -String getGenders(){ - return "/api/profile_app/gender/"; -} + String getIndigency() { + return "/api/profile_app/indigenous/"; + } + + String getGenders() { + return "/api/profile_app/gender/"; + } /////ROLES // pass check -String getAssignAreas(){ - return "/api/account/auth/assigned_role_area/"; -} + String getAssignAreas() { + return "/api/account/auth/assigned_role_area/"; + } -String getPasserInfo(){ - return "/api/profile_app/person_basicinfo/"; -} + String getPasserInfo() { + return "/api/profile_app/person_basicinfo/"; + } -String postLogs(){ - return "/api/unit2_app/monitoring/pass_check/"; -} + String postLogs() { + return "/api/unit2_app/monitoring/pass_check/"; + } + String passCheck() { + return "/api/unit2_app/monitoring/pass_check"; + } +////rbac + + String getRbacRoles() { + return "/api/account/auth/roles/"; + } + + String searchUsers() { + return "/api/hrms_app/employee_details/"; + } + + String assignRbac() { + return "/api/account/auth/rbac/"; + } + +////rbac operations + + String getRbacOperations() { + return "/api/account/auth/operations/"; + } + + String getPersmissions() { + return "/api/account/auth/permissionrbac/"; + } + + String getRoles() { + return "/api/account/auth/roles/"; + } + + String getOperations() { + return "/api/account/auth/operations/"; + } + + String getModules() { + return "/api/account/auth/modules/"; + } + + String getObject() { + return "/api/account/auth/objects/"; + } + + String getModuleObjects() { + return "/api/account/auth/moduleobject/"; + } + + String agencies() { + return "/api/jobnet_app/agencies/"; + } + + String postAgencies() { + return "/api/profile_app/agencies/"; + } + + String getRoleModules() { + return "/api/account/auth/rolemodules/"; + } + + String getRolesUnder() { + return "/api/account/auth/rolesunder/"; + } + + String getRoleExtend() { + return "/api/account/auth/rolesextend/"; + } + + String getStation() { + return "/api/hrms_app/station/"; + } + + String getRoleAssignment(){ + return "api/account/auth/role_assignment/"; + } //// location utils path - String getCounties(){ + String getCounties() { return "/api/jobnet_app/countries/"; } - String getRegions(){ + + String getRegions() { return "/api/web_app/location/region/"; } - String getProvinces(){ + + String getProvinces() { return "api/web_app/location/province/"; } - String getCities(){ + + String getCities() { return "/api/web_app/location/citymun/"; } - String getBarangays(){ + + String getBarangays() { return "/api/web_app/location/barangay/"; } - String getAddressCategory(){ + + String getAddressCategory() { return "/api/jobnet_app/address_categories/"; } -} \ No newline at end of file +} diff --git a/macos/Podfile.lock b/macos/Podfile.lock index d473e2f..9a31980 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -1,4 +1,10 @@ PODS: + - assets_audio_player (0.0.1): + - FlutterMacOS + - assets_audio_player_web (0.0.1): + - FlutterMacOS + - audioplayers_darwin (0.0.1): + - FlutterMacOS - FlutterMacOS (1.0.0) - FMDB (2.7.5): - FMDB/standard (= 2.7.5) @@ -16,6 +22,8 @@ PODS: - FlutterMacOS - platform_device_id_macos (0.0.1): - FlutterMacOS + - rive_common (0.0.1): + - FlutterMacOS - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS @@ -24,6 +32,9 @@ PODS: - FMDB (>= 2.7.5) DEPENDENCIES: + - assets_audio_player (from `Flutter/ephemeral/.symlinks/plugins/assets_audio_player/macos`) + - assets_audio_player_web (from `Flutter/ephemeral/.symlinks/plugins/assets_audio_player_web/macos`) + - audioplayers_darwin (from `Flutter/ephemeral/.symlinks/plugins/audioplayers_darwin/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - location (from `Flutter/ephemeral/.symlinks/plugins/location/macos`) - modal_progress_hud_nsn (from `Flutter/ephemeral/.symlinks/plugins/modal_progress_hud_nsn/macos`) @@ -31,6 +42,7 @@ DEPENDENCIES: - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/macos`) - platform_device_id (from `Flutter/ephemeral/.symlinks/plugins/platform_device_id/macos`) - platform_device_id_macos (from `Flutter/ephemeral/.symlinks/plugins/platform_device_id_macos/macos`) + - rive_common (from `Flutter/ephemeral/.symlinks/plugins/rive_common/macos`) - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos`) - sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`) @@ -39,6 +51,12 @@ SPEC REPOS: - FMDB EXTERNAL SOURCES: + assets_audio_player: + :path: Flutter/ephemeral/.symlinks/plugins/assets_audio_player/macos + assets_audio_player_web: + :path: Flutter/ephemeral/.symlinks/plugins/assets_audio_player_web/macos + audioplayers_darwin: + :path: Flutter/ephemeral/.symlinks/plugins/audioplayers_darwin/macos FlutterMacOS: :path: Flutter/ephemeral location: @@ -53,21 +71,27 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/platform_device_id/macos platform_device_id_macos: :path: Flutter/ephemeral/.symlinks/plugins/platform_device_id_macos/macos + rive_common: + :path: Flutter/ephemeral/.symlinks/plugins/rive_common/macos shared_preferences_foundation: :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos sqflite: :path: Flutter/ephemeral/.symlinks/plugins/sqflite/macos SPEC CHECKSUMS: + assets_audio_player: be2578e6f11dd4d183412e97143673c3c4cb2e8a + assets_audio_player_web: 917101123b6db8f73156835c0fa266c11340ff15 + audioplayers_darwin: dcad41de4fbd0099cb3749f7ab3b0cb8f70b810c FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a location: 7cdb0665bd6577d382b0a343acdadbcb7f964775 modal_progress_hud_nsn: 8099d46c2cf9de7af8fe0a3f8f5d2aa32cf956c3 package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce - path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9 + path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8 platform_device_id: 3e414428f45df149bbbfb623e2c0ca27c545b763 platform_device_id_macos: f763bb55f088be804d61b96eb4710b8ab6598e94 - shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472 + rive_common: fab8476ce8352bf54152a913f393a8696d3dc98c + shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7 diff --git a/pubspec.lock b/pubspec.lock index 43d40ba..9004766 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -518,6 +518,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" + flutter_custom_selector: + dependency: "direct main" + description: + name: flutter_custom_selector + sha256: "4c42dcd6cc2de1574454f0fc86b324303d1bd30e4bc236d01071525342d51427" + url: "https://pub.dev" + source: hosted + version: "0.0.3" flutter_form_builder: dependency: "direct main" description: @@ -563,6 +571,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.2.0" + flutter_staggered_animations: + dependency: "direct main" + description: + name: flutter_staggered_animations + sha256: "81d3c816c9bb0dca9e8a5d5454610e21ffb068aedb2bde49d2f8d04f75538351" + url: "https://pub.dev" + source: hosted + version: "1.1.1" flutter_svg: dependency: "direct main" description: @@ -653,6 +669,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.1" + group_list_view: + dependency: "direct main" + description: + name: group_list_view + sha256: "58bfc7f4b818abff531c2b202fb18b08abfb503f1621b0e86137a4fa4b6d91dd" + url: "https://pub.dev" + source: hosted + version: "1.1.1" hive: dependency: "direct main" description: @@ -1181,6 +1205,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.3" + search_page: + dependency: "direct main" + description: + name: search_page + sha256: "675239c1ac17f999c37aea7f4c969dc2fc21b58eb61d78180ff0c16112aab49b" + url: "https://pub.dev" + source: hosted + version: "2.3.0" searchable_paginated_dropdown: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 8bdae44..fcaa5d5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -82,6 +82,10 @@ dependencies: searchable_paginated_dropdown: ^1.2.0 audioplayers: ^4.1.0 assets_audio_player: ^3.0.6 + flutter_custom_selector: ^0.0.3 + flutter_staggered_animations: ^1.1.1 + group_list_view: ^1.1.1 + search_page: ^2.3.0 dev_dependencies: From 5fc0f672bf4020ee78f63a1ca0bf61c200a8ad1d Mon Sep 17 00:00:00 2001 From: cyzoox Date: Fri, 28 Jul 2023 10:35:36 +0800 Subject: [PATCH 74/86] First commit PASSO App --- android/app/build.gradle | 2 +- .../additional_item/additional_item_bloc.dart | 60 + .../additional_item_event.dart | 37 + .../additional_item_state.dart | 35 + .../class_components_bloc.dart | 22 + .../class_components_event.dart | 17 + .../class_components_state.dart | 26 + lib/bloc/passo/location/location_bloc.dart | 13 + lib/bloc/passo/location/location_event.dart | 8 + lib/bloc/passo/location/location_state.dart | 10 + .../property_appraisal_bloc.dart | 45 + .../property_appraisal_event.dart | 26 + .../property_appraisal_state.dart | 28 + .../property_assessment_bloc.dart | 60 + .../property_assessment_event.dart | 38 + .../property_assessment_state.dart | 28 + .../property_info/property_info_bloc.dart | 59 + .../property_info/property_info_event.dart | 55 + .../property_info/property_info_state.dart | 26 + .../passo/signatories/signatories_bloc.dart | 21 + .../passo/signatories/signatories_event.dart | 17 + .../passo/signatories/signatories_state.dart | 28 + .../unit_construct/unit_construct_bloc.dart | 21 + .../unit_construct/unit_construct_event.dart | 17 + .../unit_construct/unit_construct_state.dart | 26 + lib/main.dart | 6 +- lib/model/passo/additional_items.dart | 93 ++ lib/model/passo/bldg_loc.dart | 45 + lib/model/passo/class_components.dart | 93 ++ lib/model/passo/land_ref.dart | 57 + lib/model/passo/property_appraisal.dart | 68 + lib/model/passo/property_assessment.dart | 109 ++ lib/model/passo/property_info.dart | 90 ++ lib/model/passo/signatories.dart | 42 + lib/model/passo/unit_construct.dart | 38 + lib/screens/passo/Building/add_building.dart | 237 ++++ .../AddExtraItems.dart | 568 ++++++++ .../add_building_components/ExtraItems.dart | 13 + .../additional_items.dart | 233 ++++ .../bldg_location_landref.dart | 130 ++ .../general_description.dart | 179 +++ .../property_appraisal.dart | 330 +++++ .../property_assessment.dart | 1206 +++++++++++++++++ .../property_info.dart | 113 ++ .../structural_materials.dart | 228 ++++ .../passo/Test Envi/multi_dropdown.dart | 94 ++ lib/screens/passo/Test Envi/speed_dial.dart | 529 ++++++++ lib/screens/passo/passo_home.dart | 235 ++++ .../homepage.dart/components/dashboard.dart | 61 +- .../building/additional_items_services.dart | 53 + .../building/location_landref_services.dart | 27 + .../building/property_appraisal_services.dart | 43 + .../property_assessment_services.dart | 61 + .../building/property_info_services.dart | 74 + .../passo/class_components_services.dart | 21 + lib/sevices/passo/signatories_service.dart | 24 + .../passo/unit_construct_services.dart | 21 + lib/utils/app_router.dart | 20 +- lib/utils/urls.dart | 304 +++-- lib/widgets/passo/custom_button.dart | 22 + .../passo/custom_formBuilder_fields.dart | 47 + pubspec.lock | 50 +- pubspec.yaml | 6 +- 63 files changed, 6118 insertions(+), 177 deletions(-) create mode 100644 lib/bloc/passo/additional_item/additional_item_bloc.dart create mode 100644 lib/bloc/passo/additional_item/additional_item_event.dart create mode 100644 lib/bloc/passo/additional_item/additional_item_state.dart create mode 100644 lib/bloc/passo/class_components/class_components_bloc.dart create mode 100644 lib/bloc/passo/class_components/class_components_event.dart create mode 100644 lib/bloc/passo/class_components/class_components_state.dart create mode 100644 lib/bloc/passo/location/location_bloc.dart create mode 100644 lib/bloc/passo/location/location_event.dart create mode 100644 lib/bloc/passo/location/location_state.dart create mode 100644 lib/bloc/passo/property_appraisal/property_appraisal_bloc.dart create mode 100644 lib/bloc/passo/property_appraisal/property_appraisal_event.dart create mode 100644 lib/bloc/passo/property_appraisal/property_appraisal_state.dart create mode 100644 lib/bloc/passo/property_assessment/property_assessment_bloc.dart create mode 100644 lib/bloc/passo/property_assessment/property_assessment_event.dart create mode 100644 lib/bloc/passo/property_assessment/property_assessment_state.dart create mode 100644 lib/bloc/passo/property_info/property_info_bloc.dart create mode 100644 lib/bloc/passo/property_info/property_info_event.dart create mode 100644 lib/bloc/passo/property_info/property_info_state.dart create mode 100644 lib/bloc/passo/signatories/signatories_bloc.dart create mode 100644 lib/bloc/passo/signatories/signatories_event.dart create mode 100644 lib/bloc/passo/signatories/signatories_state.dart create mode 100644 lib/bloc/passo/unit_construct/unit_construct_bloc.dart create mode 100644 lib/bloc/passo/unit_construct/unit_construct_event.dart create mode 100644 lib/bloc/passo/unit_construct/unit_construct_state.dart create mode 100644 lib/model/passo/additional_items.dart create mode 100644 lib/model/passo/bldg_loc.dart create mode 100644 lib/model/passo/class_components.dart create mode 100644 lib/model/passo/land_ref.dart create mode 100644 lib/model/passo/property_appraisal.dart create mode 100644 lib/model/passo/property_assessment.dart create mode 100644 lib/model/passo/property_info.dart create mode 100644 lib/model/passo/signatories.dart create mode 100644 lib/model/passo/unit_construct.dart create mode 100644 lib/screens/passo/Building/add_building.dart create mode 100644 lib/screens/passo/Building/add_building_components/AddExtraItems.dart create mode 100644 lib/screens/passo/Building/add_building_components/ExtraItems.dart create mode 100644 lib/screens/passo/Building/add_building_components/additional_items.dart create mode 100644 lib/screens/passo/Building/add_building_components/bldg_location_landref.dart create mode 100644 lib/screens/passo/Building/add_building_components/general_description.dart create mode 100644 lib/screens/passo/Building/add_building_components/property_appraisal.dart create mode 100644 lib/screens/passo/Building/add_building_components/property_assessment.dart create mode 100644 lib/screens/passo/Building/add_building_components/property_info.dart create mode 100644 lib/screens/passo/Building/add_building_components/structural_materials.dart create mode 100644 lib/screens/passo/Test Envi/multi_dropdown.dart create mode 100644 lib/screens/passo/Test Envi/speed_dial.dart create mode 100644 lib/screens/passo/passo_home.dart create mode 100644 lib/sevices/passo/building/additional_items_services.dart create mode 100644 lib/sevices/passo/building/location_landref_services.dart create mode 100644 lib/sevices/passo/building/property_appraisal_services.dart create mode 100644 lib/sevices/passo/building/property_assessment_services.dart create mode 100644 lib/sevices/passo/building/property_info_services.dart create mode 100644 lib/sevices/passo/class_components_services.dart create mode 100644 lib/sevices/passo/signatories_service.dart create mode 100644 lib/sevices/passo/unit_construct_services.dart create mode 100644 lib/widgets/passo/custom_button.dart create mode 100644 lib/widgets/passo/custom_formBuilder_fields.dart diff --git a/android/app/build.gradle b/android/app/build.gradle index e896139..be52da5 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -27,7 +27,7 @@ apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { compileSdkVersion flutter.compileSdkVersion - ndkVersion flutter.ndkVersion + ndkVersion "25.1.8937393" compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 diff --git a/lib/bloc/passo/additional_item/additional_item_bloc.dart b/lib/bloc/passo/additional_item/additional_item_bloc.dart new file mode 100644 index 0000000..ad9c4ca --- /dev/null +++ b/lib/bloc/passo/additional_item/additional_item_bloc.dart @@ -0,0 +1,60 @@ +import 'dart:convert'; + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/model/passo/additional_items.dart'; +import 'package:http/http.dart' as http; +import 'package:unit2/sevices/passo/building/additional_items_services.dart'; + +part 'additional_item_event.dart'; +part 'additional_item_state.dart'; + +class AdditionalItemBloc + extends Bloc { + AdditionalItemBloc() : super(AdditionalItemsLoading()) { + List globalAdditionalItems = []; + on((event, emit) async { + emit(AdditionalItemsLoading()); + try { + // final tempID = await SharedPreferences.getInstance(); + // print(tempID.getInt('tempid')); + // final additionalItem = await GetAdditionalItems.getAdditionalItems( + // tempID.getInt('tempid')); + + emit(AdditionalItemsLoaded(globalAdditionalItems)); + } catch (e) { + emit(AdditionalItemsErrorState(e.toString())); + } + }); + on((event, emit) async { + http.Response response = (await AdditionalItemsServices.instance + .postAdditionalItems(event.items))!; + print(response.body); + + if (response.statusCode == 201) { + var jsonResponse = jsonDecode(response.body); + AdditionalItems newAdditional = + AdditionalItems.fromJson(jsonResponse['data']); + print(jsonResponse['data']); + globalAdditionalItems.add(newAdditional); + + emit(AdditionalItemsLoaded(globalAdditionalItems)); + } + }); + on((event, emit) async { + print(event.id); + http.Response response = (await AdditionalItemsServices.instance + .removeAdditionalItems(event.id)); + print(response.statusCode); + if (response.statusCode == 200) { + globalAdditionalItems + .removeWhere(((AdditionalItems element) => element.id == event.id)); + emit(AdditionalItemsDeletedState(success: true)); + } + }); + + on((event, emit) async { + emit(ShowAddItemsScreen()); + }); + } +} diff --git a/lib/bloc/passo/additional_item/additional_item_event.dart b/lib/bloc/passo/additional_item/additional_item_event.dart new file mode 100644 index 0000000..3922048 --- /dev/null +++ b/lib/bloc/passo/additional_item/additional_item_event.dart @@ -0,0 +1,37 @@ +part of 'additional_item_bloc.dart'; + +abstract class AdditionalItemEvent extends Equatable { + const AdditionalItemEvent(); + + @override + List get props => []; +} + +class LoadAdditionalItems extends AdditionalItemEvent { + final List items; + + const LoadAdditionalItems({this.items = const []}); + + @override + List get props => [items]; +} + +class AddAdditionalItems extends AdditionalItemEvent { + final AdditionalItems items; + + const AddAdditionalItems({required this.items}); + + @override + List get props => [items]; +} + +class DeleteAdditionalItems extends AdditionalItemEvent { + final int id; + + const DeleteAdditionalItems({required this.id}); + + @override + List get props => [id]; +} + +class ShowAdditionalItems extends AdditionalItemEvent {} diff --git a/lib/bloc/passo/additional_item/additional_item_state.dart b/lib/bloc/passo/additional_item/additional_item_state.dart new file mode 100644 index 0000000..d39fc65 --- /dev/null +++ b/lib/bloc/passo/additional_item/additional_item_state.dart @@ -0,0 +1,35 @@ +part of 'additional_item_bloc.dart'; + +abstract class AdditionalItemState extends Equatable { + const AdditionalItemState(); + + @override + List get props => []; +} + +class AdditionalItemsLoading extends AdditionalItemState {} + +class AdditionalItemsLoaded extends AdditionalItemState { + const AdditionalItemsLoaded(this.items); + final List items; + + @override + List get props => [items]; +} + +class ShowAddItemsScreen extends AdditionalItemState {} + +class AdditionalItemsErrorState extends AdditionalItemState { + const AdditionalItemsErrorState(this.error); + final String error; + + @override + List get props => [error]; +} + +class AdditionalItemsDeletedState extends AdditionalItemState { + final bool success; + const AdditionalItemsDeletedState({required this.success}); + @override + List get props => [success]; +} diff --git a/lib/bloc/passo/class_components/class_components_bloc.dart b/lib/bloc/passo/class_components/class_components_bloc.dart new file mode 100644 index 0000000..b3d9c52 --- /dev/null +++ b/lib/bloc/passo/class_components/class_components_bloc.dart @@ -0,0 +1,22 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/model/passo/class_components.dart'; +import 'package:unit2/sevices/passo/class_components_services.dart'; + +part 'class_components_event.dart'; +part 'class_components_state.dart'; + +class ClassComponentsBloc + extends Bloc { + ClassComponentsBloc() : super(ClassComponentLoading()) { + on((event, emit) async { + emit(ClassComponentLoading()); + try { + final classs = await ClassComponentService.instance.getClassComponent(); + emit(ClassComponentLoaded(classs)); + } catch (e) { + emit(ClassComponentErrorState(e.toString())); + } + }); + } +} diff --git a/lib/bloc/passo/class_components/class_components_event.dart b/lib/bloc/passo/class_components/class_components_event.dart new file mode 100644 index 0000000..5efdac8 --- /dev/null +++ b/lib/bloc/passo/class_components/class_components_event.dart @@ -0,0 +1,17 @@ +part of 'class_components_bloc.dart'; + +abstract class ClassComponentsEvent extends Equatable { + const ClassComponentsEvent(); + + @override + List get props => []; +} + +class LoadClassComponents extends ClassComponentsEvent { + final List classes; + + const LoadClassComponents({this.classes = const []}); + + @override + List get props => [classes]; +} diff --git a/lib/bloc/passo/class_components/class_components_state.dart b/lib/bloc/passo/class_components/class_components_state.dart new file mode 100644 index 0000000..99beb14 --- /dev/null +++ b/lib/bloc/passo/class_components/class_components_state.dart @@ -0,0 +1,26 @@ +part of 'class_components_bloc.dart'; + +abstract class ClassComponentsState extends Equatable { + const ClassComponentsState(); + + @override + List get props => []; +} + +class ClassComponentLoading extends ClassComponentsState {} + +class ClassComponentLoaded extends ClassComponentsState { + ClassComponentLoaded(this.classes); + final List classes; + + @override + List get props => [classes]; +} + +class ClassComponentErrorState extends ClassComponentsState { + ClassComponentErrorState(this.error); + final String error; + + @override + List get props => [error]; +} diff --git a/lib/bloc/passo/location/location_bloc.dart b/lib/bloc/passo/location/location_bloc.dart new file mode 100644 index 0000000..7bac921 --- /dev/null +++ b/lib/bloc/passo/location/location_bloc.dart @@ -0,0 +1,13 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; + +part 'location_event.dart'; +part 'location_state.dart'; + +class LocationBloc extends Bloc { + LocationBloc() : super(LocationInitial()) { + on((event, emit) { + // TODO: implement event handler + }); + } +} diff --git a/lib/bloc/passo/location/location_event.dart b/lib/bloc/passo/location/location_event.dart new file mode 100644 index 0000000..44dfbee --- /dev/null +++ b/lib/bloc/passo/location/location_event.dart @@ -0,0 +1,8 @@ +part of 'location_bloc.dart'; + +abstract class LocationEvent extends Equatable { + const LocationEvent(); + + @override + List get props => []; +} diff --git a/lib/bloc/passo/location/location_state.dart b/lib/bloc/passo/location/location_state.dart new file mode 100644 index 0000000..64ea7b3 --- /dev/null +++ b/lib/bloc/passo/location/location_state.dart @@ -0,0 +1,10 @@ +part of 'location_bloc.dart'; + +abstract class LocationState extends Equatable { + const LocationState(); + + @override + List get props => []; +} + +class LocationInitial extends LocationState {} diff --git a/lib/bloc/passo/property_appraisal/property_appraisal_bloc.dart b/lib/bloc/passo/property_appraisal/property_appraisal_bloc.dart new file mode 100644 index 0000000..55c2d3d --- /dev/null +++ b/lib/bloc/passo/property_appraisal/property_appraisal_bloc.dart @@ -0,0 +1,45 @@ +import 'dart:convert'; + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:unit2/model/passo/property_appraisal.dart'; +import 'package:http/http.dart' as http; +import 'package:unit2/sevices/passo/building/property_appraisal_services.dart'; + +part 'property_appraisal_event.dart'; +part 'property_appraisal_state.dart'; + +class PropertyAppraisalBloc + extends Bloc { + PropertyAppraisalBloc() : super(PropertyAppraisalInitial()) { + List globalPropertyAppraisal = []; + on((event, emit) async { + emit(PropertyAppraisalLoading()); + try { + final tempID = await SharedPreferences.getInstance(); + + final appraisal = await PropertyAppraisalServices.instance + .getPropertyAppraisal(tempID.getInt('tempid')); + + emit(PropertyAppraisalLoaded(appraisal)); + } catch (e) { + emit(PropertyAppraisalErrorState(e.toString())); + } + }); + on((event, emit) async { + http.Response response = (await PropertyAppraisalServices.instance + .postPropertyAppraisal(event.appraisal))!; + + if (response.statusCode == 201) { + var jsonResponse = jsonDecode(response.body); + PropertyAppraisal newAppraisal = + PropertyAppraisal.fromJson(jsonResponse['data']); + print(jsonResponse['data']); + globalPropertyAppraisal.add(newAppraisal); + + emit(PropertyAppraisalLoaded(globalPropertyAppraisal)); + } + }); + } +} diff --git a/lib/bloc/passo/property_appraisal/property_appraisal_event.dart b/lib/bloc/passo/property_appraisal/property_appraisal_event.dart new file mode 100644 index 0000000..c37835d --- /dev/null +++ b/lib/bloc/passo/property_appraisal/property_appraisal_event.dart @@ -0,0 +1,26 @@ +part of 'property_appraisal_bloc.dart'; + +abstract class PropertyAppraisalEvent extends Equatable { + const PropertyAppraisalEvent(); + + @override + List get props => []; +} + +class LoadPropertyAppraisal extends PropertyAppraisalEvent { + final List appraisal; + + const LoadPropertyAppraisal({this.appraisal = const []}); + + @override + List get props => [appraisal]; +} + +class AddPropertyAppraisal extends PropertyAppraisalEvent { + final PropertyAppraisal appraisal; + + const AddPropertyAppraisal({required this.appraisal}); + + @override + List get props => [appraisal]; +} diff --git a/lib/bloc/passo/property_appraisal/property_appraisal_state.dart b/lib/bloc/passo/property_appraisal/property_appraisal_state.dart new file mode 100644 index 0000000..a2e3a77 --- /dev/null +++ b/lib/bloc/passo/property_appraisal/property_appraisal_state.dart @@ -0,0 +1,28 @@ +part of 'property_appraisal_bloc.dart'; + +abstract class PropertyAppraisalState extends Equatable { + const PropertyAppraisalState(); + + @override + List get props => []; +} + +class PropertyAppraisalInitial extends PropertyAppraisalState {} + +class PropertyAppraisalLoading extends PropertyAppraisalState {} + +class PropertyAppraisalLoaded extends PropertyAppraisalState { + PropertyAppraisalLoaded(this.appraisal); + final List appraisal; + + @override + List get props => [appraisal]; +} + +class PropertyAppraisalErrorState extends PropertyAppraisalState { + PropertyAppraisalErrorState(this.error); + final String error; + + @override + List get props => [error]; +} diff --git a/lib/bloc/passo/property_assessment/property_assessment_bloc.dart b/lib/bloc/passo/property_assessment/property_assessment_bloc.dart new file mode 100644 index 0000000..ee73086 --- /dev/null +++ b/lib/bloc/passo/property_assessment/property_assessment_bloc.dart @@ -0,0 +1,60 @@ +import 'dart:convert'; + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:unit2/model/passo/property_assessment.dart'; +import 'package:http/http.dart' as http; +import 'package:unit2/sevices/passo/building/property_assessment_services.dart'; + +part 'property_assessment_event.dart'; +part 'property_assessment_state.dart'; + +class PropertyAssessmentBloc + extends Bloc { + PropertyAssessmentBloc() : super(PropertyAssessmentInitial()) { + List globalPropertyAssessment = []; + on((event, emit) async { + emit(PropertyAssessmentLoading()); + try { + final tempID = await SharedPreferences.getInstance(); + + final assessments = await PropertyAssessmentServices.instance + .getPropertyAssessment(tempID.getInt('tempid')! + 1); + + emit(PropertyAssessmentLoaded(assessments)); + } catch (e) { + emit(PropertyAssessmentErrorState(e.toString())); + } + }); + on((event, emit) async { + http.Response response = (await PropertyAssessmentServices.instance + .postPropertyAssessment(event.assessments))!; + print('Assessment'); + print(response.statusCode); + print(response.body); + if (response.statusCode == 201) { + var jsonResponse = jsonDecode(response.body); + PropertyAssessment newAssessment = + PropertyAssessment.fromJson(jsonResponse['data']); + + globalPropertyAssessment.add(newAssessment); + + emit(PropertyAssessmentLoaded(globalPropertyAssessment)); + } + }); + on((event, emit) async { + final tempID = await SharedPreferences.getInstance(); + final tempID2 = tempID.getInt('tempid')! - 1; + http.Response response = (await PropertyAssessmentServices.instance + .propertyAssessmentPutInfo(event.assessment, tempID2))!; + print('assessment'); + print(response.statusCode); + print(response.body); + // if (response.statusCode == 201) { + // final faas = await PropertyInfoRepository.getUsers(); + // emit(FaasLoaded(faas)); + // } + }); + } +} diff --git a/lib/bloc/passo/property_assessment/property_assessment_event.dart b/lib/bloc/passo/property_assessment/property_assessment_event.dart new file mode 100644 index 0000000..3c7621c --- /dev/null +++ b/lib/bloc/passo/property_assessment/property_assessment_event.dart @@ -0,0 +1,38 @@ +part of 'property_assessment_bloc.dart'; + +abstract class PropertyAssessmentEvent extends Equatable { + const PropertyAssessmentEvent(); + + @override + List get props => []; +} + +class LoadPropertyAssessment extends PropertyAssessmentEvent { + final List assessments; + + const LoadPropertyAssessment( + {this.assessments = const []}); + + @override + List get props => [assessments]; +} + +class AddPropertyAssessment extends PropertyAssessmentEvent { + final PropertyAssessment assessments; + + const AddPropertyAssessment({required this.assessments}); + + @override + List get props => [assessments]; +} + +class UpdatePropertyAssessment extends PropertyAssessmentEvent { + // ignore: non_constant_identifier_names + final PropertyAssessment assessment; + + // ignore: non_constant_identifier_names + const UpdatePropertyAssessment({required this.assessment}); + + @override + List get props => [assessment]; +} diff --git a/lib/bloc/passo/property_assessment/property_assessment_state.dart b/lib/bloc/passo/property_assessment/property_assessment_state.dart new file mode 100644 index 0000000..6c88b75 --- /dev/null +++ b/lib/bloc/passo/property_assessment/property_assessment_state.dart @@ -0,0 +1,28 @@ +part of 'property_assessment_bloc.dart'; + +abstract class PropertyAssessmentState extends Equatable { + const PropertyAssessmentState(); + + @override + List get props => []; +} + +class PropertyAssessmentInitial extends PropertyAssessmentState {} + +class PropertyAssessmentLoading extends PropertyAssessmentState {} + +class PropertyAssessmentLoaded extends PropertyAssessmentState { + PropertyAssessmentLoaded(this.assessments); + final List assessments; + + @override + List get props => [assessments]; +} + +class PropertyAssessmentErrorState extends PropertyAssessmentState { + PropertyAssessmentErrorState(this.error); + final String error; + + @override + List get props => [error]; +} diff --git a/lib/bloc/passo/property_info/property_info_bloc.dart b/lib/bloc/passo/property_info/property_info_bloc.dart new file mode 100644 index 0000000..a808842 --- /dev/null +++ b/lib/bloc/passo/property_info/property_info_bloc.dart @@ -0,0 +1,59 @@ +import 'dart:convert'; + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:unit2/model/passo/bldg_loc.dart'; +import 'package:unit2/model/passo/land_ref.dart'; +import 'package:unit2/model/passo/property_info.dart'; +import 'package:unit2/sevices/passo/building/property_info_services.dart'; +import 'package:http/http.dart' as http; + +part 'property_info_event.dart'; +part 'property_info_state.dart'; + +class PropertyInfoBloc extends Bloc { + PropertyInfoBloc() : super(PropertyInfoLoading()) { + on((event, emit) async { + emit(PropertyInfoLoading()); + try { + final property_info = + await PropertyInfoService.instance.getpropertyInfo(); + emit(PropertyInfoLoaded(property_info)); + } catch (e) { + emit(PropertyInfoErrorState(e.toString())); + } + }); + on((event, emit) async { + final state = this.state; + http.Response response = (await PropertyInfoService.instance + .postPropertyInfo(event.property_info))!; + print(response.body); + + if (response.statusCode == 201) { + var jsonResponse = jsonDecode(response.body); + final tempID = await SharedPreferences.getInstance(); + print(jsonResponse['data']); + await tempID.setInt('tempid', jsonResponse['data']['id'] + 1); + final faas = await PropertyInfoService.instance.getpropertyInfo(); + emit(PropertyInfoLoaded(faas)); + } + }); + on(((event, emit) async { + final state = this.state; + http.Response response = (await PropertyInfoService.instance + .bldgLocPutInfo(event.bldg_loc, event.bldg_loc.id))!; + print('bldgLoc'); + print(response.statusCode); + })); + on( + (event, emit) async { + final state = this.state; + http.Response response = (await PropertyInfoService.instance + .landRefPutInfo(event.land_ref, event.land_ref.id))!; + print('landref'); + print(response.statusCode); + }, + ); + } +} diff --git a/lib/bloc/passo/property_info/property_info_event.dart b/lib/bloc/passo/property_info/property_info_event.dart new file mode 100644 index 0000000..4356438 --- /dev/null +++ b/lib/bloc/passo/property_info/property_info_event.dart @@ -0,0 +1,55 @@ +part of 'property_info_bloc.dart'; + +abstract class PropertyInfoEvent extends Equatable { + const PropertyInfoEvent(); + + @override + List get props => []; +} + +class LoadPropertyInfo extends PropertyInfoEvent { + final List property_info; + + const LoadPropertyInfo({this.property_info = const []}); + + @override + List get props => [property_info]; +} + +class AddPropertyInfo extends PropertyInfoEvent { + final PropertyInfo property_info; + + const AddPropertyInfo({required this.property_info}); + + @override + List get props => [property_info]; +} + +class UpdateBldgLoc extends PropertyInfoEvent { + // ignore: non_constant_identifier_names + final BldgLoc bldg_loc; + + // ignore: non_constant_identifier_names + const UpdateBldgLoc({required this.bldg_loc}); + + @override + List get props => [bldg_loc]; +} + +class UpdateLandRef extends PropertyInfoEvent { + final LandRef land_ref; + + const UpdateLandRef({required this.land_ref}); + + @override + List get props => [land_ref]; +} + +// class UpdateGeneralDesc extends PropertyInfoEvent { +// final GeneralDesc gen_desc; + +// const UpdateGeneralDesc({required this.gen_desc}); + +// @override +// List get props => [gen_desc]; +// } \ No newline at end of file diff --git a/lib/bloc/passo/property_info/property_info_state.dart b/lib/bloc/passo/property_info/property_info_state.dart new file mode 100644 index 0000000..b325da5 --- /dev/null +++ b/lib/bloc/passo/property_info/property_info_state.dart @@ -0,0 +1,26 @@ +part of 'property_info_bloc.dart'; + +abstract class PropertyInfoState extends Equatable { + const PropertyInfoState(); + + @override + List get props => []; +} + +class PropertyInfoLoading extends PropertyInfoState {} + +class PropertyInfoLoaded extends PropertyInfoState { + const PropertyInfoLoaded(this.property_info); + final List property_info; + + @override + List get props => [property_info]; +} + +class PropertyInfoErrorState extends PropertyInfoState { + const PropertyInfoErrorState(this.error); + final String error; + + @override + List get props => [error]; +} diff --git a/lib/bloc/passo/signatories/signatories_bloc.dart b/lib/bloc/passo/signatories/signatories_bloc.dart new file mode 100644 index 0000000..9d6581c --- /dev/null +++ b/lib/bloc/passo/signatories/signatories_bloc.dart @@ -0,0 +1,21 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/model/passo/signatories.dart'; +import 'package:unit2/sevices/passo/signatories_service.dart'; + +part 'signatories_event.dart'; +part 'signatories_state.dart'; + +class SignatoriesBloc extends Bloc { + SignatoriesBloc() : super(SignatoriesInitial()) { + on((event, emit) async { + emit(SignatoriesLoading()); + try { + final signatories = await SignatoriesServices.instance.getSignatories(); + emit(SignatoriesLoaded(signatories)); + } catch (e) { + emit(SignatoriesErrorState(e.toString())); + } + }); + } +} diff --git a/lib/bloc/passo/signatories/signatories_event.dart b/lib/bloc/passo/signatories/signatories_event.dart new file mode 100644 index 0000000..a1949cd --- /dev/null +++ b/lib/bloc/passo/signatories/signatories_event.dart @@ -0,0 +1,17 @@ +part of 'signatories_bloc.dart'; + +abstract class SignatoriesEvent extends Equatable { + const SignatoriesEvent(); + + @override + List get props => []; +} + +class LoadSignatories extends SignatoriesEvent { + final List signatories; + + const LoadSignatories({this.signatories = const []}); + + @override + List get props => [signatories]; +} diff --git a/lib/bloc/passo/signatories/signatories_state.dart b/lib/bloc/passo/signatories/signatories_state.dart new file mode 100644 index 0000000..a914bc9 --- /dev/null +++ b/lib/bloc/passo/signatories/signatories_state.dart @@ -0,0 +1,28 @@ +part of 'signatories_bloc.dart'; + +abstract class SignatoriesState extends Equatable { + const SignatoriesState(); + + @override + List get props => []; +} + +class SignatoriesInitial extends SignatoriesState {} + +class SignatoriesLoading extends SignatoriesState {} + +class SignatoriesLoaded extends SignatoriesState { + SignatoriesLoaded(this.signatories); + final List signatories; + + @override + List get props => [signatories]; +} + +class SignatoriesErrorState extends SignatoriesState { + SignatoriesErrorState(this.error); + final String error; + + @override + List get props => [error]; +} diff --git a/lib/bloc/passo/unit_construct/unit_construct_bloc.dart b/lib/bloc/passo/unit_construct/unit_construct_bloc.dart new file mode 100644 index 0000000..8112eb3 --- /dev/null +++ b/lib/bloc/passo/unit_construct/unit_construct_bloc.dart @@ -0,0 +1,21 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/model/passo/unit_construct.dart'; +import 'package:unit2/sevices/passo/unit_construct_services.dart'; + +part 'unit_construct_event.dart'; +part 'unit_construct_state.dart'; + +class UnitConstructBloc extends Bloc { + UnitConstructBloc() : super(UnitConstructLoading()) { + on((event, emit) async { + emit(UnitConstructLoading()); + try { + final unit = await UnitConstructService.instance.getUnitConstruct(); + emit(UnitConstructLoaded(unit)); + } catch (e) { + emit(UnitConstructErrorState(e.toString())); + } + }); + } +} diff --git a/lib/bloc/passo/unit_construct/unit_construct_event.dart b/lib/bloc/passo/unit_construct/unit_construct_event.dart new file mode 100644 index 0000000..dfd9e83 --- /dev/null +++ b/lib/bloc/passo/unit_construct/unit_construct_event.dart @@ -0,0 +1,17 @@ +part of 'unit_construct_bloc.dart'; + +abstract class UnitConstructEvent extends Equatable { + const UnitConstructEvent(); + + @override + List get props => []; +} + +class LoadUnitConstruct extends UnitConstructEvent { + final List unit; + + const LoadUnitConstruct({this.unit = const []}); + + @override + List get props => [unit]; +} diff --git a/lib/bloc/passo/unit_construct/unit_construct_state.dart b/lib/bloc/passo/unit_construct/unit_construct_state.dart new file mode 100644 index 0000000..51adea0 --- /dev/null +++ b/lib/bloc/passo/unit_construct/unit_construct_state.dart @@ -0,0 +1,26 @@ +part of 'unit_construct_bloc.dart'; + +abstract class UnitConstructState extends Equatable { + const UnitConstructState(); + + @override + List get props => []; +} + +class UnitConstructLoading extends UnitConstructState {} + +class UnitConstructLoaded extends UnitConstructState { + UnitConstructLoaded(this.unit); + final List unit; + + @override + List get props => [unit]; +} + +class UnitConstructErrorState extends UnitConstructState { + UnitConstructErrorState(this.error); + final String error; + + @override + List get props => [error]; +} diff --git a/lib/main.dart b/lib/main.dart index 5144a73..b7f782d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,6 +7,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:hive/hive.dart'; import 'package:hive_flutter/hive_flutter.dart'; +import 'package:unit2/bloc/passo/property_info/property_info_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/theme-data.dart/colors.dart'; @@ -17,8 +18,6 @@ import 'package:path_provider/path_provider.dart' as path_provider; import './utils/router.dart'; import './utils/global.dart'; - - Future main() async { WidgetsFlutterBinding.ensureInitialized(); var appDirectory = await path_provider.getApplicationDocumentsDirectory(); @@ -66,6 +65,8 @@ class MyApp extends StatelessWidget { BlocProvider( create: (_) => ProfileBloc(), ), + BlocProvider( + create: (context) => PropertyInfoBloc()..add(LoadPropertyInfo())), ], child: MaterialApp( navigatorKey: NavigationService.navigatorKey, @@ -92,4 +93,3 @@ class MyApp extends StatelessWidget { ); } } - diff --git a/lib/model/passo/additional_items.dart b/lib/model/passo/additional_items.dart new file mode 100644 index 0000000..9f89494 --- /dev/null +++ b/lib/model/passo/additional_items.dart @@ -0,0 +1,93 @@ +// To parse this JSON data, do +// +// final additionalItems = additionalItemsFromJson(jsonString); + +import 'package:meta/meta.dart'; +import 'dart:convert'; + +AdditionalItems additionalItemsFromJson(String str) => + AdditionalItems.fromJson(json.decode(str)); + +String additionalItemsToJson(AdditionalItems data) => + json.encode(data.toJson()); + +class AdditionalItems { + final int id; + final int bldgapprDetailsId; + final int classId; + final String className; + final String structType; + final dynamic unitValue; + final dynamic baseUnitValue; + final dynamic area; + final dynamic marketValue; + final dynamic depreciationRate; + final dynamic adjustedMarketVal; + final dynamic amtDepreciation; + final bool painted; + final bool secondhand; + final dynamic paintedUnitval; + final dynamic secondhandUnitval; + final String actualUse; + + AdditionalItems({ + required this.id, + required this.bldgapprDetailsId, + required this.classId, + required this.className, + required this.structType, + required this.unitValue, + required this.baseUnitValue, + required this.area, + required this.marketValue, + required this.depreciationRate, + required this.adjustedMarketVal, + required this.amtDepreciation, + required this.painted, + required this.secondhand, + required this.paintedUnitval, + required this.secondhandUnitval, + required this.actualUse, + }); + + factory AdditionalItems.fromJson(Map json) => + AdditionalItems( + id: json["id"], + bldgapprDetailsId: json["bldgappr_details_id"], + classId: json["class_id"], + className: json["class_name"], + structType: json["struct_type"], + unitValue: json["unit_value"], + baseUnitValue: json["base_unit_value"], + area: json["area"], + marketValue: json["market_value"], + depreciationRate: json["depreciation_rate"], + adjustedMarketVal: json["adjusted_market_val"], + amtDepreciation: json["amt_depreciation"], + painted: json["painted"], + secondhand: json["secondhand"], + paintedUnitval: json["painted_unitval"], + secondhandUnitval: json["secondhand_unitval"], + actualUse: json["actual_use"], + ); + + Map toJson() => { + "id": id, + "bldgappr_details_id": bldgapprDetailsId, + "class_id": classId, + "class_name": className, + "struct_type": structType, + "unit_value": unitValue, + "base_unit_value": baseUnitValue, + "area": area, + "market_value": marketValue, + "depreciation_rate": depreciationRate, + "adjusted_market_val": adjustedMarketVal, + "amt_depreciation": amtDepreciation, + "painted": painted, + "secondhand": secondhand, + "painted_unitval": paintedUnitval, + "secondhand_unitval": secondhandUnitval, + "actual_use": actualUse, + }; +} diff --git a/lib/model/passo/bldg_loc.dart b/lib/model/passo/bldg_loc.dart new file mode 100644 index 0000000..0e9e80d --- /dev/null +++ b/lib/model/passo/bldg_loc.dart @@ -0,0 +1,45 @@ +// To parse this JSON data, do +// +// final bldgLoc = bldgLocFromJson(jsonString); + +import 'dart:convert'; + +BldgLoc bldgLocFromJson(String str) => BldgLoc.fromJson(json.decode(str)); + +String bldgLocToJson(BldgLoc data) => json.encode(data.toJson()); + +class BldgLoc { + BldgLoc({ + this.id, + this.bldgapprDetailsId, + this.street, + this.barangay, + this.municipality, + this.province, + }); + + final int? id; + final int? bldgapprDetailsId; + final String? street; + final String? barangay; + final String? municipality; + final String? province; + + factory BldgLoc.fromJson(Map json) => BldgLoc( + id: json["id"], + bldgapprDetailsId: json["bldgappr_details_id"], + street: json["street"], + barangay: json["barangay"], + municipality: json["municipality"], + province: json["province"], + ); + + Map toJson() => { + "id": id, + "bldgappr_details_id": bldgapprDetailsId, + "street": street, + "barangay": barangay, + "municipality": municipality, + "province": province, + }; +} diff --git a/lib/model/passo/class_components.dart b/lib/model/passo/class_components.dart new file mode 100644 index 0000000..b9ce05b --- /dev/null +++ b/lib/model/passo/class_components.dart @@ -0,0 +1,93 @@ +// To parse this JSON data, do +// +// final classComponents = classComponentsFromJson(jsonString); + +import 'package:meta/meta.dart'; +import 'dart:convert'; + +ClassComponents classComponentsFromJson(String str) => + ClassComponents.fromJson(json.decode(str)); + +String classComponentsToJson(ClassComponents data) => + json.encode(data.toJson()); + +class ClassComponents { + final int id; + final String componentName; + final String minBaseUnitvalPercent; + final String maxBaseUnitvalPercent; + final String minUnitvalSqrmtr; + final String maxUnitvalSqrmtr; + final String minAddBaseunitval; + final String maxAddBaseunitval; + final String minDeductBaserate; + final String maxDeductBaserate; + final String minLinearMeter; + final String maxLinearMeter; + final String minSpacing; + final String maxSpacing; + final String roughFinish; + final String highFinish; + final bool withoutBucc; + + ClassComponents({ + required this.id, + required this.componentName, + required this.minBaseUnitvalPercent, + required this.maxBaseUnitvalPercent, + required this.minUnitvalSqrmtr, + required this.maxUnitvalSqrmtr, + required this.minAddBaseunitval, + required this.maxAddBaseunitval, + required this.minDeductBaserate, + required this.maxDeductBaserate, + required this.minLinearMeter, + required this.maxLinearMeter, + required this.minSpacing, + required this.maxSpacing, + required this.roughFinish, + required this.highFinish, + required this.withoutBucc, + }); + + factory ClassComponents.fromJson(Map json) => + ClassComponents( + id: json["id"], + componentName: json["component_name"], + minBaseUnitvalPercent: json["min_base_unitval_percent"], + maxBaseUnitvalPercent: json["max_base_unitval_percent"], + minUnitvalSqrmtr: json["min_unitval_sqrmtr"], + maxUnitvalSqrmtr: json["max_unitval_sqrmtr"], + minAddBaseunitval: json["min_add_baseunitval"], + maxAddBaseunitval: json["max_add_baseunitval"], + minDeductBaserate: json["min_deduct_baserate"], + maxDeductBaserate: json["max_deduct_baserate"], + minLinearMeter: json["min_linear_meter"], + maxLinearMeter: json["max_linear_meter"], + minSpacing: json["min_spacing"], + maxSpacing: json["max_spacing"], + roughFinish: json["rough_finish"], + highFinish: json["high_finish"], + withoutBucc: json["without_bucc"], + ); + + Map toJson() => { + "id": id, + "component_name": componentName, + "min_base_unitval_percent": minBaseUnitvalPercent, + "max_base_unitval_percent": maxBaseUnitvalPercent, + "min_unitval_sqrmtr": minUnitvalSqrmtr, + "max_unitval_sqrmtr": maxUnitvalSqrmtr, + "min_add_baseunitval": minAddBaseunitval, + "max_add_baseunitval": maxAddBaseunitval, + "min_deduct_baserate": minDeductBaserate, + "max_deduct_baserate": maxDeductBaserate, + "min_linear_meter": minLinearMeter, + "max_linear_meter": maxLinearMeter, + "min_spacing": minSpacing, + "max_spacing": maxSpacing, + "rough_finish": roughFinish, + "high_finish": highFinish, + "without_bucc": withoutBucc, + }; +} diff --git a/lib/model/passo/land_ref.dart b/lib/model/passo/land_ref.dart new file mode 100644 index 0000000..58de998 --- /dev/null +++ b/lib/model/passo/land_ref.dart @@ -0,0 +1,57 @@ +// To parse this JSON data, do +// +// final landRef = landRefFromJson(jsonString); + +import 'dart:convert'; + +LandRef landRefFromJson(String str) => LandRef.fromJson(json.decode(str)); + +String landRefToJson(LandRef data) => json.encode(data.toJson()); + +class LandRef { + LandRef({ + this.id, + this.bldgapprDetailsId, + this.owner, + this.cloaNo, + this.lotNo, + this.tdn, + this.area, + this.surveyNo, + this.blkNo, + }); + + final int? id; + final int? bldgapprDetailsId; + final String? owner; + final String? cloaNo; + final String? lotNo; + final String? tdn; + final String? area; + final String? surveyNo; + final String? blkNo; + + factory LandRef.fromJson(Map json) => LandRef( + id: json["id"], + bldgapprDetailsId: json["bldgappr_details_id"], + owner: json["owner"], + cloaNo: json["cloa_no"], + lotNo: json["lot_no"], + tdn: json["tdn"], + area: json["area"], + surveyNo: json["survey_no"], + blkNo: json["blk_no"], + ); + + Map toJson() => { + "id": id, + "bldgappr_details_id": bldgapprDetailsId, + "owner": owner, + "cloa_no": cloaNo, + "lot_no": lotNo, + "tdn": tdn, + "area": area, + "survey_no": surveyNo, + "blk_no": blkNo, + }; +} diff --git a/lib/model/passo/property_appraisal.dart b/lib/model/passo/property_appraisal.dart new file mode 100644 index 0000000..4244db8 --- /dev/null +++ b/lib/model/passo/property_appraisal.dart @@ -0,0 +1,68 @@ +// To parse this JSON data, do +// +// final propertyAppraisal = propertyAppraisalFromJson(jsonString); + +import 'dart:convert'; + +PropertyAppraisal propertyAppraisalFromJson(String str) => + PropertyAppraisal.fromJson(json.decode(str)); + +String propertyAppraisalToJson(PropertyAppraisal data) => + json.encode(data.toJson()); + +class PropertyAppraisal { + final int id; + final int bldgapprDetailsId; + final String unitconstructCost; + final String buildingCore; + final String unitconstructSubtotal; + final String depreciationRate; + final String depreciationCost; + final String costAddItems; + final String addItemsSubtotal; + final String totalpercentDepreciation; + final String marketValue; + + PropertyAppraisal({ + required this.id, + required this.bldgapprDetailsId, + required this.unitconstructCost, + required this.buildingCore, + required this.unitconstructSubtotal, + required this.depreciationRate, + required this.depreciationCost, + required this.costAddItems, + required this.addItemsSubtotal, + required this.totalpercentDepreciation, + required this.marketValue, + }); + + factory PropertyAppraisal.fromJson(Map json) => + PropertyAppraisal( + id: json["id"], + bldgapprDetailsId: json["bldgappr_details_id"], + unitconstructCost: json["unitconstruct_cost"], + buildingCore: json["building_core"], + unitconstructSubtotal: json["unitconstruct_subtotal"], + depreciationRate: json["depreciation_rate"], + depreciationCost: json["depreciation_cost"], + costAddItems: json["cost_add_items"], + addItemsSubtotal: json["add_items_subtotal"], + totalpercentDepreciation: json["totalpercent_depreciation"], + marketValue: json["market_value"], + ); + + Map toJson() => { + "id": id, + "bldgappr_details_id": bldgapprDetailsId, + "unitconstruct_cost": unitconstructCost, + "building_core": buildingCore, + "unitconstruct_subtotal": unitconstructSubtotal, + "depreciation_rate": depreciationRate, + "depreciation_cost": depreciationCost, + "cost_add_items": costAddItems, + "add_items_subtotal": addItemsSubtotal, + "totalpercent_depreciation": totalpercentDepreciation, + "market_value": marketValue, + }; +} diff --git a/lib/model/passo/property_assessment.dart b/lib/model/passo/property_assessment.dart new file mode 100644 index 0000000..fb91bff --- /dev/null +++ b/lib/model/passo/property_assessment.dart @@ -0,0 +1,109 @@ +// To parse this JSON data, do +// +// final propertyAssessment = propertyAssessmentFromJson(jsonString); + +import 'package:meta/meta.dart'; +import 'dart:convert'; + +PropertyAssessment propertyAssessmentFromJson(String str) => + PropertyAssessment.fromJson(json.decode(str)); + +String propertyAssessmentToJson(PropertyAssessment data) => + json.encode(data.toJson()); + +class PropertyAssessment { + final int id; + final int bldgapprDetailsId; + final String actualUse; + final String marketValue; + final String assessmentLevel; + final String assessedValue; + final bool taxable; + final bool exempt; + final int qtr; + final int yr; + final String appraisedbyName; + final DateTime appraisedbyDate; + final String recommendapprName; + final DateTime recommendapprDate; + final String approvedbyName; + final String memoranda; + final String swornstatementNo; + final DateTime dateReceived; + final DateTime entryDateAssessment; + final String entryDateBy; + + PropertyAssessment({ + required this.id, + required this.bldgapprDetailsId, + required this.actualUse, + required this.marketValue, + required this.assessmentLevel, + required this.assessedValue, + required this.taxable, + required this.exempt, + required this.qtr, + required this.yr, + required this.appraisedbyName, + required this.appraisedbyDate, + required this.recommendapprName, + required this.recommendapprDate, + required this.approvedbyName, + required this.memoranda, + required this.swornstatementNo, + required this.dateReceived, + required this.entryDateAssessment, + required this.entryDateBy, + }); + + factory PropertyAssessment.fromJson(Map json) => + PropertyAssessment( + id: json["id"], + bldgapprDetailsId: json["bldgappr_details_id"], + actualUse: json["actual_use"], + marketValue: json["market_value"], + assessmentLevel: json["assessment_level"], + assessedValue: json["assessed_value"], + taxable: json["taxable"], + exempt: json["exempt"], + qtr: json["qtr"], + yr: json["yr"], + appraisedbyName: json["appraisedby_name"], + appraisedbyDate: DateTime.parse(json["appraisedby_date"]), + recommendapprName: json["recommendappr_name"], + recommendapprDate: DateTime.parse(json["recommendappr_date"]), + approvedbyName: json["approvedby_name"], + memoranda: json["memoranda"], + swornstatementNo: json["swornstatement_no"], + dateReceived: DateTime.parse(json["date_received"]), + entryDateAssessment: DateTime.parse(json["entry_date_assessment"]), + entryDateBy: json["entry_date_by"], + ); + + Map toJson() => { + "id": id, + "bldgappr_details_id": bldgapprDetailsId, + "actual_use": actualUse, + "market_value": marketValue, + "assessment_level": assessmentLevel, + "assessed_value": assessedValue, + "taxable": taxable, + "exempt": exempt, + "qtr": qtr, + "yr": yr, + "appraisedby_name": appraisedbyName, + "appraisedby_date": + "${appraisedbyDate.year.toString().padLeft(4, '0')}-${appraisedbyDate.month.toString().padLeft(2, '0')}-${appraisedbyDate.day.toString().padLeft(2, '0')}", + "recommendappr_name": recommendapprName, + "recommendappr_date": + "${recommendapprDate.year.toString().padLeft(4, '0')}-${recommendapprDate.month.toString().padLeft(2, '0')}-${recommendapprDate.day.toString().padLeft(2, '0')}", + "approvedby_name": approvedbyName, + "memoranda": memoranda, + "swornstatement_no": swornstatementNo, + "date_received": + "${dateReceived.year.toString().padLeft(4, '0')}-${dateReceived.month.toString().padLeft(2, '0')}-${dateReceived.day.toString().padLeft(2, '0')}", + "entry_date_assessment": + "${entryDateAssessment.year.toString().padLeft(4, '0')}-${entryDateAssessment.month.toString().padLeft(2, '0')}-${entryDateAssessment.day.toString().padLeft(2, '0')}", + "entry_date_by": entryDateBy, + }; +} diff --git a/lib/model/passo/property_info.dart b/lib/model/passo/property_info.dart new file mode 100644 index 0000000..89cd807 --- /dev/null +++ b/lib/model/passo/property_info.dart @@ -0,0 +1,90 @@ +// To parse this JSON data, do +// +// final propertyInfo = propertyInfoFromJson(jsonString); + +import 'dart:convert'; + +PropertyInfo propertyInfoFromJson(String str) => + PropertyInfo.fromJson(json.decode(str)); + +String propertyInfoToJson(PropertyInfo data) => json.encode(data.toJson()); + +class PropertyInfo { + final int? id; + final String? assessedById; + final String? assessedByName; + final DateTime? dateCreated; + final DateTime? dateModified; + final String? transCode; + final String? tdn; + final String? pin; + final String? owner; + final String? address; + final String? telno; + final String? tin; + final String? adminUser; + final String? adminAddress; + final String? adminTelno; + final String? adminTin; + + PropertyInfo({ + this.id, + this.assessedById, + this.assessedByName, + this.dateCreated, + this.dateModified, + this.transCode, + this.tdn, + this.pin, + this.owner, + this.address, + this.telno, + this.tin, + this.adminUser, + this.adminAddress, + this.adminTelno, + this.adminTin, + }); + + factory PropertyInfo.fromJson(Map json) => PropertyInfo( + id: json["id"], + assessedById: json["assessed_by_id"], + assessedByName: json["assessed_by_name"], + dateCreated: json["date_created"] == null + ? null + : DateTime.parse(json["date_created"]), + dateModified: json["date_modified"] == null + ? null + : DateTime.parse(json["date_modified"]), + transCode: json["trans_code"], + tdn: json["tdn"], + pin: json["pin"], + owner: json["owner"], + address: json["address"], + telno: json["telno"], + tin: json["tin"], + adminUser: json["admin_user"], + adminAddress: json["admin_address"], + adminTelno: json["admin_telno"], + adminTin: json["admin_tin"], + ); + + Map toJson() => { + "id": id, + "assessed_by_id": assessedById, + "assessed_by_name": assessedByName, + "date_created": dateCreated?.toIso8601String(), + "date_modified": dateModified?.toIso8601String(), + "trans_code": transCode, + "tdn": tdn, + "pin": pin, + "owner": owner, + "address": address, + "telno": telno, + "tin": tin, + "admin_user": adminUser, + "admin_address": adminAddress, + "admin_telno": adminTelno, + "admin_tin": adminTin, + }; +} diff --git a/lib/model/passo/signatories.dart b/lib/model/passo/signatories.dart new file mode 100644 index 0000000..641f491 --- /dev/null +++ b/lib/model/passo/signatories.dart @@ -0,0 +1,42 @@ +// To parse this JSON data, do +// +// final signatories = signatoriesFromJson(jsonString); + +import 'dart:convert'; + +Signatories signatoriesFromJson(String str) => + Signatories.fromJson(json.decode(str)); + +String signatoriesToJson(Signatories data) => json.encode(data.toJson()); + +class Signatories { + final int? id; + final int signatoryId; + final String firstname; + final String middlename; + final String lastname; + + Signatories({ + this.id, + required this.signatoryId, + required this.firstname, + required this.middlename, + required this.lastname, + }); + + factory Signatories.fromJson(Map json) => Signatories( + id: json["id"], + signatoryId: json["signatory_id"], + firstname: json["firstname"], + middlename: json["middlename"], + lastname: json["lastname"], + ); + + Map toJson() => { + "id": id, + "signatory_id": signatoryId, + "firstname": firstname, + "middlename": middlename, + "lastname": lastname, + }; +} diff --git a/lib/model/passo/unit_construct.dart b/lib/model/passo/unit_construct.dart new file mode 100644 index 0000000..bc97fa3 --- /dev/null +++ b/lib/model/passo/unit_construct.dart @@ -0,0 +1,38 @@ +// To parse this JSON data, do +// +// final unitConstruct = unitConstructFromJson(jsonString); + +import 'dart:convert'; + +UnitConstruct unitConstructFromJson(String str) => + UnitConstruct.fromJson(json.decode(str)); + +String unitConstructToJson(UnitConstruct data) => json.encode(data.toJson()); + +class UnitConstruct { + final int id; + final String bldgType; + final String building; + final String unitValue; + + UnitConstruct({ + required this.id, + required this.bldgType, + required this.building, + required this.unitValue, + }); + + factory UnitConstruct.fromJson(Map json) => UnitConstruct( + id: json["id"], + bldgType: json["bldg_type"], + building: json["building"], + unitValue: json["unit_value"], + ); + + Map toJson() => { + "id": id, + "bldg_type": bldgType, + "building": building, + "unit_value": unitValue, + }; +} diff --git a/lib/screens/passo/Building/add_building.dart b/lib/screens/passo/Building/add_building.dart new file mode 100644 index 0000000..30922cc --- /dev/null +++ b/lib/screens/passo/Building/add_building.dart @@ -0,0 +1,237 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:unit2/bloc/passo/class_components/class_components_bloc.dart'; +import 'package:unit2/bloc/passo/property_info/property_info_bloc.dart'; +import 'package:unit2/bloc/passo/unit_construct/unit_construct_bloc.dart'; +import 'package:unit2/model/passo/bldg_loc.dart'; +import 'package:unit2/model/passo/class_components.dart'; +import 'package:unit2/model/passo/land_ref.dart'; +import 'package:unit2/model/passo/property_info.dart'; +import 'package:unit2/screens/passo/Building/add_building_components/additional_items.dart'; +import 'package:unit2/screens/passo/Building/add_building_components/bldg_location_landref.dart'; +import 'package:unit2/screens/passo/Building/add_building_components/general_description.dart'; +import 'package:unit2/screens/passo/Building/add_building_components/property_appraisal.dart'; +import 'package:unit2/screens/passo/Building/add_building_components/property_assessment.dart'; +import 'package:unit2/screens/passo/Building/add_building_components/property_info.dart'; +import 'package:unit2/screens/passo/Building/add_building_components/structural_materials.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:im_stepper/stepper.dart'; + +GlobalKey formKey = GlobalKey(); + +class AddBuilding extends StatefulWidget { + @override + _AddBuilding createState() => _AddBuilding(); +} + +class _AddBuilding extends State { + int activeStep = 0; // Initial step set to 5. + int upperBound = 6; + + bool saveStep1 = false; + bool saveStep2 = false; + bool saveStep3 = false; + bool saveStep4 = false; + bool saveStep5 = false; + bool saveStep6 = false; + bool saveStep7 = false; + int tempId = 0; + + void onPostPropertyInfo() { + formKey.currentState?.save(); + if (formKey.currentState!.value['transaction_code'].toString() != null && + formKey.currentState!.value['owner'] != null) { + if (activeStep < upperBound && saveStep1 == false) { + setState(() { + activeStep++; + saveStep1 = true; + }); + } else { + setState(() { + activeStep++; + }); + } + + var property_info = PropertyInfo( + id: 1, + transCode: formKey.currentState!.value['transaction_code'].toString(), + tdn: formKey.currentState!.value['arp_td'], + pin: formKey.currentState!.value['pin'], + owner: formKey.currentState!.value['owner'], + address: formKey.currentState!.value['address'], + telno: formKey.currentState!.value['tel_no'], + tin: formKey.currentState!.value['tin'], + adminUser: formKey.currentState!.value['benificiary'], + adminAddress: formKey.currentState!.value['benificiary_address'], + adminTin: formKey.currentState!.value['benificiary_tin'], + adminTelno: formKey.currentState!.value['benificiary_telno'], + assessedById: '1', + assessedByName: 'Cyril', + dateModified: DateTime.now(), + dateCreated: DateTime.now()); + + context + .read() + .add(AddPropertyInfo(property_info: property_info)); +// _loadTempId(); + } + } + + void onPutBldgLandref() { + // Increment activeStep, when the next button is tapped. However, check for upper bound. + if (activeStep < upperBound && saveStep2 == false) { + setState(() { + activeStep++; + saveStep2 = true; + }); + } + var bldgLocData = BldgLoc( + id: tempId, + street: formKey.currentState?.value['street'], + barangay: formKey.currentState?.value['brgy'], + municipality: formKey.currentState?.value['municipality'], + province: formKey.currentState?.value['province'], + ); + var landRefData = LandRef( + id: tempId, + owner: formKey.currentState?.value['l_owner'], + cloaNo: formKey.currentState?.value['oct_tct_cloa'], + lotNo: formKey.currentState?.value['lot_no'], + tdn: formKey.currentState?.value['l_td_arp'], + area: formKey.currentState?.value['area'], + surveyNo: formKey.currentState?.value['survey_no'], + blkNo: formKey.currentState?.value['blk_no'], + ); + context.read() + ..add(UpdateBldgLoc(bldg_loc: bldgLocData)) + ..add(UpdateLandRef(land_ref: landRefData)); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: primary, + centerTitle: true, + title: const Text("Building FAAS"), + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocConsumer(listener: ( + context, + state, + ) { + if (state is PropertyInfoLoading) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is PropertyInfoLoaded || + state is PropertyInfoErrorState) { + final progress = ProgressHUD.of(context); + progress?.dismiss(); + } + }, builder: (context, state) { + if (state is PropertyInfoLoaded) { + return BlocConsumer( + listener: (context, state) { + // TODO: implement listener + }, + builder: (context, state) { + if (state is UnitConstructLoaded) { + final unit = state.unit; + return BlocConsumer( + listener: (context, state) { + // TODO: implement listener + }, + builder: (context, state) { + if (state is ClassComponentLoaded) { + return Column( + children: [ + NumberStepper( + numbers: [1, 2, 3, 4, 5, 6, 7], + stepPadding: 5, + activeStepColor: Colors.red, + numberStyle: + const TextStyle(color: Colors.white), + activeStepBorderColor: Colors.white, + // activeStep property set to activeStep variable defined above. + activeStep: activeStep, + + // This ensures step-tapping updates the activeStep. + onStepReached: (index) { + setState(() { + activeStep = index; + }); + }, + ), + Expanded( + child: FormBuilder( + key: formKey, + + // enabled: false, + onChanged: () { + formKey.currentState?.save(); + + print(formKey.currentState?.value.toString()); + }, + autovalidateMode: AutovalidateMode.disabled, + skipDisabled: true, + child: Container( + child: content( + onPostPropertyInfo, unit, state.classes), + ), + )), + ], + ); + } + return Container(); + }, + ); + } + return Container(); + }, + ); + } + return Container(); + })), + ); + } + + Widget content(handleButtonPress, unit, List classes) { + switch (activeStep) { + case 0: + return PropertyInfoPage(onPostPropertyInfo); + + case 1: + return BldgLocationLandrefPage(); + + case 2: + return GeneralDescriptionPage(unit); + + case 3: + return StructuralMaterialsPage(); + + case 4: + return AdditionalItemPage(unit, classes); + + case 5: + return PropertyAppraisalPage(); + + case 6: + return PropertyAssessmentPage(onSAveAll); + + default: + return Text("Property Info"); + } + } + + void onSAveAll() { + return Navigator.of(context).pop(); + } +} diff --git a/lib/screens/passo/Building/add_building_components/AddExtraItems.dart b/lib/screens/passo/Building/add_building_components/AddExtraItems.dart new file mode 100644 index 0000000..8e36686 --- /dev/null +++ b/lib/screens/passo/Building/add_building_components/AddExtraItems.dart @@ -0,0 +1,568 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:intl/intl.dart'; +import 'package:searchfield/searchfield.dart'; +import 'package:unit2/bloc/passo/additional_item/additional_item_bloc.dart'; +import 'package:unit2/model/passo/additional_items.dart'; +import 'package:unit2/model/passo/class_components.dart'; +import 'package:unit2/model/passo/unit_construct.dart'; +import 'package:unit2/theme-data.dart/form-style.dart'; + +class AddExtraItems extends StatefulWidget { + final List unit; + final List options; + + AddExtraItems(this.unit, this.options); + + @override + _AddExtraItems createState() => _AddExtraItems(); +} + +class _AddExtraItems extends State { + GlobalKey formKey = GlobalKey(); + final focus = FocusNode(); + double _computedValue = 0; + bool isPainted = false; + bool isSecondHand = false; + TextEditingController textEditingController = TextEditingController(); + double _unitBase = 0; + int _areaValue = 0; + double _depValue = 0; + double _unitValue = 0; + double _marketValue = 0; + String _className = ""; + int _classId = 0; + String _structureType = ""; + bool _withoutBUCC = false; + double _notPaintedUnitVal = 0; + double _secondHandUnitVal = 0; + + BoxDecoration box1() { + return const BoxDecoration(boxShadow: [ + BoxShadow(color: Colors.black12, spreadRadius: 5, blurRadius: 5) + ], color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(3))); + } + + double _computeValue(double unitbase, double unitvalue, double area) { +// Compute some value based on the text here + return (unitbase * unitvalue) * area; + } + + double _amountofDepreciation(unitVal, unitBase, area, depreciation) { + return ((unitVal * unitBase) * area) * depreciation; + } + + double _adjustedMarketValue(unitVal, unitBase, area, depreciation) { + double depAmount = ((unitVal * unitBase) * area) * depreciation; + + return ((unitVal * unitBase) * area) - depAmount; + } + + double _totalMarketValue(unitVal, unitBase, area, depreciation, withBUCC, + className, painted, secondHand, paintedUnitVal, secondhandUntVal) { + if (withBUCC == false) { + if (painted == true || secondHand == true) { + final deductions = paintedUnitVal + secondhandUntVal; + print(deductions); + return (((unitVal - deductions) * unitBase) * area); + } else { + return ((unitVal * unitBase) * area); + } + } else { + return (unitVal * area); + } + } + + @override + Widget build(BuildContext context) { + return BlocBuilder( + buildWhen: (previous, current) { + return false; + }, builder: (context, state) { + if (state is ShowAddItemsScreen) { + return FormBuilder( + key: formKey, + onChanged: () { + formKey.currentState?.save(); + }, + autovalidateMode: AutovalidateMode.disabled, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + height: 1000, + child: SingleChildScrollView( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Container( + margin: const EdgeInsets.only( + left: 0, top: 10, right: 0, bottom: 0), + child: FormBuilderDropdown( + name: 'extra_item', + autofocus: false, + decoration: + normalTextFieldStyle("Additional Item", ""), + items: widget.options + .map((e) => DropdownMenuItem( + value: e, + child: Text(e.componentName), + )) + .toList(), + onChanged: (value) { + if (value!.minBaseUnitvalPercent != '0.00') { + setState(() { + _unitValue = + double.parse(value.minBaseUnitvalPercent); + _className = value.componentName; + _classId = value.id; + _withoutBUCC = value.withoutBucc; + }); + formKey.currentState!.patchValue( + {'unitValue': value.minBaseUnitvalPercent}); + } + if (value.maxBaseUnitvalPercent != '0.00') { + setState(() { + _unitValue = + double.parse(value.maxBaseUnitvalPercent); + _className = value.componentName; + _classId = value.id; + _withoutBUCC = value.withoutBucc; + }); + formKey.currentState!.patchValue( + {'unitValue': value.maxBaseUnitvalPercent}); + } + if (value.minUnitvalSqrmtr != '0.00') { + setState(() { + _unitValue = + double.parse(value.minUnitvalSqrmtr); + _className = value.componentName; + _classId = value.id; + _withoutBUCC = value.withoutBucc; + }); + formKey.currentState!.patchValue( + {'unitValue': value.minUnitvalSqrmtr}); + } + if (value.maxUnitvalSqrmtr != '0.00') { + setState(() { + _unitValue = + double.parse(value.maxUnitvalSqrmtr); + _className = value.componentName; + _classId = value.id; + _withoutBUCC = value.withoutBucc; + }); + formKey.currentState!.patchValue( + {'unitValue': value.maxUnitvalSqrmtr}); + } + if (value.minAddBaseunitval != '0.00') { + setState(() { + _unitValue = + double.parse(value.minAddBaseunitval); + _className = value.componentName; + _classId = value.id; + _withoutBUCC = value.withoutBucc; + }); + formKey.currentState!.patchValue( + {'unitValue': value.minAddBaseunitval}); + } + if (value.maxAddBaseunitval != '0.00') { + setState(() { + _unitValue = + double.parse(value.maxAddBaseunitval); + _className = value.componentName; + _classId = value.id; + _withoutBUCC = value.withoutBucc; + }); + formKey.currentState!.patchValue( + {'unitValue': value.maxAddBaseunitval}); + } + if (value.minDeductBaserate != '0.00') { + setState(() { + _unitValue = + double.parse(value.minDeductBaserate); + _className = value.componentName; + _classId = value.id; + _withoutBUCC = value.withoutBucc; + }); + formKey.currentState!.patchValue( + {'unitValue': value.minDeductBaserate}); + } + if (value.maxDeductBaserate != '0.00') { + setState(() { + _unitValue = + double.parse(value.maxDeductBaserate); + _className = value.componentName; + _classId = value.id; + _withoutBUCC = value.withoutBucc; + }); + formKey.currentState!.patchValue( + {'unitValue': value.maxDeductBaserate}); + } + }, + ), + ), + const SizedBox(height: 10), + Container( + margin: const EdgeInsets.only( + left: 0, top: 10, right: 0, bottom: 0), + child: SizedBox( + height: 45, + child: SearchField( + itemHeight: 70, + suggestions: widget.unit + .map((UnitConstruct unit) => + SearchFieldListItem( + unit.bldgType! + + ' - ' + + unit.building, + item: unit, + child: ListTile( + title: Text( + unit.bldgType + + ' - ' + + unit.building!.toUpperCase(), + overflow: TextOverflow.ellipsis, + ), + ))) + .toList(), + + validator: FormBuilderValidators.required( + errorText: "This field is required"), + + searchInputDecoration: normalTextFieldStyle( + "Structure Type", "") + .copyWith( + suffixIcon: + const Icon(Icons.arrow_drop_down)), + ////agency suggestion tap + focusNode: focus, + suggestionState: Suggestion.expand, + onSuggestionTap: (unit) { + setState(() { + _unitBase = + double.parse(unit.item!.unitValue); + _structureType = unit.item!.bldgType + + ' - ' + + unit.item!.building; + }); + focus.unfocus(); + }, + ), + ), + ), + // const SizedBox(height: 10), + // Container( + // margin: const EdgeInsets.only( + // left: 0, top: 10, right: 0, bottom: 0), + // child: FormBuilderDropdown( + // name: 'struc_type', + // autofocus: false, + // decoration: + // normalTextFieldStyle("Structure Type", ""), + // items: widget.unit + // .map((e) => DropdownMenuItem( + // value: e, + // child: + // Text(e.bldgType + " - " + e.building), + // )) + // .toList(), + // onChanged: (val) { + // setState(() { + // _unitBase = double.parse(val!.unitValue); + // _structureType = val.bldgType; + // }); + // }, + // ), + // ), + const SizedBox(height: 10), + Row( + children: [ + Expanded( + flex: 1, + child: FormBuilderTextField( + name: 'unitValue', + decoration: + normalTextFieldStyle("Unit Value", ""), + validator: FormBuilderValidators.compose([]), + ), + ), + const SizedBox(width: 10), + Expanded( + flex: 1, + child: FormBuilderTextField( + name: 'areaValue', + decoration: normalTextFieldStyle("Area", ""), + validator: FormBuilderValidators.compose([]), + onChanged: (value) { + setState(() { + _areaValue = int.parse(value!); + }); + }, + ), + ), + ], + ), + // const SizedBox(height: 10), + // FormBuilderTextField( + // name: 'depRate', + // decoration: + // normalTextFieldStyle("Depreciation Rate", ""), + // validator: FormBuilderValidators.compose([]), + // onChanged: (value) { + // setState(() { + // _depValue = double.parse(value!); + // }); + // }, + // ), + // const SizedBox(height: 10), + // FormBuilderTextField( + // name: 'marketValue', + // decoration: normalTextFieldStyle( + // NumberFormat.currency( + // locale: 'en-PH', symbol: "₱") + // .format(_totalMarketValue(_unitValue, + // _unitBase, _areaValue, _depValue)), + // ""), + // validator: FormBuilderValidators.compose([]), + // onChanged: (value) { + // setState(() { + // _marketValue = double.parse(value!); + // }); + // }, + // ), + // const SizedBox(height: 10), + // Text('Amount of Depreciation'), + // const SizedBox(height: 5), + // Container( + // height: 45.0, + // width: double.infinity, + // decoration: BoxDecoration( + // color: Colors.white, + // border: Border.all( + // color: Colors.grey, + // width: 1.0, + // ), + // borderRadius: BorderRadius.circular(5.0), + // ), + // child: Align( + // alignment: Alignment.center, + // child: Text(NumberFormat.currency( + // locale: 'en-PH', symbol: "₱") + // .format(_amountofDepreciation(_unitValue, + // _unitBase, _areaValue, _depValue)))), + // ), + + Visibility( + visible: !_withoutBUCC, + child: Column( + children: [ + const SizedBox(height: 10), + Text('Building is not painted?'), + const SizedBox(height: 5), + Container( + child: Row( + children: [ + Checkbox( + value: isPainted, + onChanged: (bool? value) { + setState(() { + isPainted = value!; + if (value == false) { + _notPaintedUnitVal = 0.00; + } else { + _notPaintedUnitVal = 0.10; + } + }); + }, + ), + const SizedBox(width: 10), + Container( + height: 40.0, + width: 100, + decoration: BoxDecoration( + color: Colors.white, + border: Border.all( + color: Colors.grey, + width: 1.0, + ), + borderRadius: + BorderRadius.circular(5.0), + ), + child: Align( + alignment: Alignment.center, + child: Text(' - ' + + _notPaintedUnitVal.toString())), + ), + ], + ), + ), + const SizedBox(height: 10), + Text('Uses second hand materials?'), + const SizedBox(height: 5), + Container( + child: Row( + children: [ + Checkbox( + value: isSecondHand, + onChanged: (bool? value) { + setState(() { + isSecondHand = value!; + if (isSecondHand == false) { + _secondHandUnitVal = 0; + formKey.currentState!.patchValue( + {'secondHandMat': '0'}); + } else { + _secondHandUnitVal = 0.05; + formKey.currentState!.patchValue( + {'secondHandMat': '0.05'}); + } + }); + }, + ), + const SizedBox(width: 10), + SizedBox( + height: 40, + width: 100, + child: FormBuilderTextField( + enabled: isSecondHand, + name: 'secondHandMat', + textAlign: TextAlign.center, + decoration: normalTextFieldStyle( + "Unit Value", ""), + validator: + FormBuilderValidators.compose([]), + onChanged: (value) { + // Check if the value is not null before parsing to double + if (value != null && + value.isNotEmpty) { + setState(() { + _secondHandUnitVal = + double.parse(value); + }); + } else { + // Handle the case when the value is empty or null + // For example, set _secondHandUnitVal to a default value or show an error message. + } + }, + ), + ), + ], + ), + ), + ], + ), + ), + + const SizedBox(height: 10), + Text('Market Value'), + const SizedBox(height: 5), + Container( + height: 45.0, + width: double.infinity, + decoration: BoxDecoration( + color: Colors.white, + border: Border.all( + color: Colors.grey, + width: 1.0, + ), + borderRadius: BorderRadius.circular(5.0), + ), + child: Align( + alignment: Alignment.center, + child: Text(NumberFormat.currency( + locale: 'en-PH', symbol: "₱") + .format(_totalMarketValue( + _unitValue, + _unitBase, + _areaValue, + _depValue, + _withoutBUCC, + _className, + isPainted, + isSecondHand, + _notPaintedUnitVal, + _secondHandUnitVal)))), + ), + const SizedBox(height: 10), + Row( + children: [ + Container( + width: 120, + height: 60, + padding: const EdgeInsets.all(8.0), + child: ElevatedButton( + onPressed: () { + var itemss = AdditionalItems( + id: 1, + bldgapprDetailsId: 528, + classId: _classId, + className: _className, + structType: _structureType, + unitValue: _unitValue, + baseUnitValue: _unitBase, + area: _areaValue, + marketValue: + (_unitValue * _unitBase) * _areaValue, + depreciationRate: _depValue, + adjustedMarketVal: _adjustedMarketValue( + _unitValue, + _unitBase, + _areaValue, + _depValue, + ), + actualUse: 'Test', + amtDepreciation: _amountofDepreciation( + _unitValue, + _unitBase, + _areaValue, + _depValue, + ), + painted: true, + secondhand: true, + paintedUnitval: '1', + secondhandUnitval: '1'); + + context + .read() + .add(AddAdditionalItems(items: itemss)); + }, + style: ElevatedButton.styleFrom( + primary: Colors.black, + ), + child: const Text("Submit"), + ), + ), + SizedBox( + width: + 5), // Use SizedBox for horizontal spacing in a Row + Container( + width: 120, + height: 60, + padding: const EdgeInsets.all(8.0), + child: ElevatedButton( + onPressed: () { + context + .read() + .add(LoadAdditionalItems()); + }, + style: ElevatedButton.styleFrom( + primary: Colors.black, + ), + child: const Text("Cancel"), + ), + ), + ], + ) + ], + ), + ), + ))); + } + return Container(); + }); + } +} diff --git a/lib/screens/passo/Building/add_building_components/ExtraItems.dart b/lib/screens/passo/Building/add_building_components/ExtraItems.dart new file mode 100644 index 0000000..90c6573 --- /dev/null +++ b/lib/screens/passo/Building/add_building_components/ExtraItems.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; + +class ExtraItemsPage extends StatefulWidget { + @override + _ExtraItemsPage createState() => _ExtraItemsPage(); +} + +class _ExtraItemsPage extends State { + @override + Widget build(BuildContext context) { + return Container(); + } +} diff --git a/lib/screens/passo/Building/add_building_components/additional_items.dart b/lib/screens/passo/Building/add_building_components/additional_items.dart new file mode 100644 index 0000000..d58bc5b --- /dev/null +++ b/lib/screens/passo/Building/add_building_components/additional_items.dart @@ -0,0 +1,233 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:unit2/bloc/passo/additional_item/additional_item_bloc.dart'; +import 'package:unit2/model/passo/class_components.dart'; +import 'package:unit2/model/passo/unit_construct.dart'; +import 'package:unit2/screens/passo/Building/add_building_components/AddExtraItems.dart'; +import 'package:unit2/utils/alerts.dart'; + +class AdditionalItemPage extends StatefulWidget { + final List unit; + final List options; + AdditionalItemPage(this.unit, this.options); + + @override + _AdditionalItemPage createState() => _AdditionalItemPage(); +} + +class _AdditionalItemPage extends State { + void deleteItem(int itemId) { + context.read().add(DeleteAdditionalItems(id: itemId)); + } + + @override + Widget build(BuildContext context) { + return BlocConsumer( + listener: (context, state) { + // TODO: implement listener + }, + builder: (context, state) { + final state = context.watch().state; + if (state is AdditionalItemsLoaded) { + return SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(15.0), + child: Column( + children: [ + Container( + margin: const EdgeInsets.only( + left: 0, top: 20, right: 0, bottom: 10), + child: const Text('ADDITIONAL ITEMS', + style: TextStyle( + fontWeight: FontWeight.bold, fontSize: 18), + textAlign: TextAlign.left), + ), + Align( + alignment: Alignment.topRight, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.red, + ), + onPressed: () { + context + .read() + .add(ShowAdditionalItems()); + }, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Text('ADD ITEM'), // <-- Text + const SizedBox( + width: 5, + ), + const Icon( + // <-- Icon + Icons.add, + size: 24.0, + ), + ], + ), + ), + ), + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: DataTable( + // ignore: prefer_const_literals_to_create_immutables + columns: [ + const DataColumn( + label: Text('Items'), + ), + const DataColumn( + label: Text('Unit Value'), + ), + const DataColumn( + label: Text('% of BUCC'), + ), + const DataColumn( + label: Text('Market Value'), + ), + const DataColumn( + label: Text('Action'), + ) + ], + rows: state.items.map((dataRow) { + return DataRow( + cells: [ + DataCell(Text(dataRow.className)), + DataCell(Text(dataRow.baseUnitValue)), + DataCell(Text(dataRow.unitValue)), + DataCell(Text(((double.parse(dataRow.unitValue) * + double.parse(dataRow.baseUnitValue) * + double.parse(dataRow.area))) + .toString())), + DataCell(Row( + children: [ + InkWell( + child: Container( + height: 30, + width: 30, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.red, + ), + child: Icon( + Icons.delete, + color: Colors.white, + size: 20.0, + ), + ), + onTap: () { + deleteItem(dataRow.id); + }, + ), + SizedBox( + width: 10, + ), + InkWell( + child: Container( + height: 30, + width: 30, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.red, + ), + child: Icon( + Icons.edit, + color: Colors.white, + size: 20.0, + ), + ), + onTap: () {}, + ), + ], + )) + ], + ); + }).toList(), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [Text('Total'), Text("0.00")], + ) + ], + ), + ), + ); + } + if (state is AdditionalItemsDeletedState) { + if (state.success) { + WidgetsBinding.instance.addPostFrameCallback((_) { + successAlert(context, "Deletion Successful", + "Extra item has been deleted successfully", () { + Navigator.of(context).pop(); + context + .read() + .add(const LoadAdditionalItems()); + }); + }); + } + } + if (state is ShowAddItemsScreen) { + return ConstrainedBox( + constraints: BoxConstraints(maxHeight: 1000.0), + child: AlertDialog( + insetPadding: EdgeInsets.symmetric( + horizontal: 20.0, + vertical: 10.0, + ), + title: Text( + 'ADD EXTRA ITEMS', + textAlign: TextAlign.center, + ), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded(child: AddExtraItems(widget.unit, widget.options)) + ], + ), + ), + ); + } + return Container( + child: Column( + children: [ + Container( + margin: const EdgeInsets.only( + left: 0, top: 20, right: 0, bottom: 10), + child: const Text('ADDITIONAL MATERIALS', + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18), + textAlign: TextAlign.left), + ), + Align( + alignment: Alignment.topRight, + child: ElevatedButton( + onPressed: () { + context + .read() + .add(ShowAdditionalItems()); + }, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Text('ADD ITEM'), // <-- Text + const SizedBox( + width: 5, + ), + const Icon( + // <-- Icon + Icons.add, + size: 24.0, + ), + ], + ), + ), + ), + ], + ), + ); + }, + ); + } +} diff --git a/lib/screens/passo/Building/add_building_components/bldg_location_landref.dart b/lib/screens/passo/Building/add_building_components/bldg_location_landref.dart new file mode 100644 index 0000000..0d92c22 --- /dev/null +++ b/lib/screens/passo/Building/add_building_components/bldg_location_landref.dart @@ -0,0 +1,130 @@ +import 'package:flutter/material.dart'; +import 'package:unit2/widgets/passo/custom_button.dart'; +import 'package:unit2/widgets/passo/custom_formBuilder_fields.dart'; + +class BldgLocationLandrefPage extends StatefulWidget { + BldgLocationLandrefPage(); + + @override + _BldgLocationLandrefPage createState() => _BldgLocationLandrefPage(); +} + +class _BldgLocationLandrefPage extends State { + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + children: [ + Container( + margin: + const EdgeInsets.only(left: 0, top: 20, right: 0, bottom: 10), + child: const Text('BUILDING LOCATION', + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18), + textAlign: TextAlign.left), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + flex: 1, + child: customTextField("No. / Street", "", 'street'), + ), + const SizedBox(width: 10.0), + Expanded( + // optional flex property if flex is 1 because the default flex is 1 + flex: 1, + child: customTextField("Brgy. / District", "", 'brgy')) + ]), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + flex: 1, + child: customTextField("Municipality", "", 'municipality'), + ), + const SizedBox(width: 10.0), + Expanded( + // optional flex property if flex is 1 because the default flex is 1 + flex: 1, + child: customTextField("Province / City", "", 'province')) + ]), + Container( + margin: + const EdgeInsets.only(left: 0, top: 20, right: 0, bottom: 10), + child: const Text('LAND REFERENCE', + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18), + textAlign: TextAlign.left), + ), + customTextField("Land Owner", "", 'l_owner'), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + flex: 1, + child: + customTextField("OCT/TCT/CLOA No.", "", 'oct_tct_cloa'), + ), + const SizedBox(width: 10.0), + Expanded( + // optional flex property if flex is 1 because the default flex is 1 + flex: 1, + child: customTextField("Survey No.", "", 'survey_no')) + ]), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + flex: 1, + child: customTextField("Lot No.", "", 'lot_no'), + ), + const SizedBox(width: 10.0), + Expanded( + // optional flex property if flex is 1 because the default flex is 1 + flex: 1, + child: customTextField("Blk No.", "", 'blk_no')) + ]), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + flex: 1, + child: customTextField("TD / ARP No.", "", 'l_td_arp'), + ), + const SizedBox(width: 10.0), + Expanded( + // optional flex property if flex is 1 because the default flex is 1 + flex: 1, + child: customTextField("Area", "", 'area')) + ]), + SizedBox( + height: 50, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + CustomButton( + icon: const Icon(Icons.chevron_left_rounded, + color: Colors.white), + onPressed: () { + {} + ; + }, + ), + CustomButton( + icon: const Icon(Icons.chevron_right_rounded, + color: Colors.white), + onPressed: () { + {} + ; + }, + ) + ], + ) + ], + ), + ), + ); + } +} diff --git a/lib/screens/passo/Building/add_building_components/general_description.dart b/lib/screens/passo/Building/add_building_components/general_description.dart new file mode 100644 index 0000000..7fc58f0 --- /dev/null +++ b/lib/screens/passo/Building/add_building_components/general_description.dart @@ -0,0 +1,179 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:unit2/model/passo/unit_construct.dart'; +import 'package:unit2/theme-data.dart/form-style.dart'; +import 'package:unit2/widgets/passo/custom_button.dart'; +import 'package:unit2/widgets/passo/custom_formBuilder_fields.dart'; + +class GeneralDescriptionPage extends StatefulWidget { + final List unit; + GeneralDescriptionPage(this.unit); + + @override + _GeneralDescriptionPage createState() => _GeneralDescriptionPage(); +} + +class _GeneralDescriptionPage extends State { + final actual_use = [ + "Residential", + "Agricultural", + "Commercial", + "Industrial", + "Mineral", + "Timberland", + ]; + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + children: [ + Container( + margin: + const EdgeInsets.only(left: 0, top: 20, right: 0, bottom: 10), + child: const Text('GENERAL DESCRIPTION', + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18), + textAlign: TextAlign.left), + ), + Container( + margin: + const EdgeInsets.only(left: 0, top: 10, right: 0, bottom: 0), + child: FormBuilderDropdown( + name: 'bldg_type', + autofocus: false, + decoration: normalTextFieldStyle("Kind of Building", ""), + items: widget.unit + .map((e) => DropdownMenuItem( + value: e, + child: Text(e.bldgType + '-' + e.building), + )) + .toList(), + ), + ), + customDropDownField("Actual Use", "", 'actual_use', actual_use), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + flex: 1, + child: + customTextField("Bldg. Permit No.", "", 'bldg_permit'), + ), + const SizedBox(width: 10.0), + Expanded( + // optional flex property if flex is 1 because the default flex is 1 + flex: 1, + child: customDatTimePicker( + "Certificate of Occupancy Issued ON", + "", + 'date_issued')) + ]), + customTextField( + "Condominium Certificate of Title (CCT)", "", 'cct'), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + flex: 1, + child: customDatTimePicker( + "Certificate of Completion Issued ON", + "", + 'coc_issued'), + ), + const SizedBox(width: 10.0), + Expanded( + // optional flex property if flex is 1 because the default flex is 1 + flex: 1, + child: customDatTimePicker( + "Certificate of Occupancy Issued ON", + "", + 'coo_issued')) + ]), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + flex: 1, + child: customDatTimePicker( + "Date Constructed /Completed", "", 'date_cnstructed'), + ), + const SizedBox(width: 10.0), + Expanded( + // optional flex property if flex is 1 because the default flex is 1 + flex: 1, + child: customDatTimePicker( + "Date Occupied", "", 'date_occupied')) + ]), + Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: < + Widget>[ + Expanded( + flex: 1, + child: customTextField("Bldg. Age", "", 'bldg_age'), + ), + const SizedBox(width: 10.0), + Expanded( + // optional flex property if flex is 1 because the default flex is 1 + flex: 1, + child: customTextField("No. of storeys", "", 'no_of_storeys')) + ]), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + flex: 1, + child: customTextField( + "Area of 1st Floor", "", 'area_of_1stFl'), + ), + const SizedBox(width: 10.0), + Expanded( + // optional flex property if flex is 1 because the default flex is 1 + flex: 1, + child: customTextField( + "Area of 2nd Floor", "", 'area_of_2ndFl')) + ]), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + flex: 1, + child: customTextField( + "Area of 3rd Floor", "", 'area_of_3rdFl')), + const SizedBox(width: 10.0), + Expanded( + // optional flex property if flex is 1 because the default flex is 1 + flex: 1, + child: customTextField( + "Area of 4th Floor", "", 'area_of_4thFl')) + ]), + customTextField("Total Area", "", 'total_area'), + SizedBox( + height: 50, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + CustomButton( + icon: const Icon(Icons.chevron_left_rounded, + color: Colors.white), + onPressed: () { + {} + ; + }, + ), + CustomButton( + icon: const Icon(Icons.chevron_right_rounded, + color: Colors.white), + onPressed: () { + {} + ; + }, + ) + ], + ) + ], + ), + ), + ); + } +} diff --git a/lib/screens/passo/Building/add_building_components/property_appraisal.dart b/lib/screens/passo/Building/add_building_components/property_appraisal.dart new file mode 100644 index 0000000..9b909e0 --- /dev/null +++ b/lib/screens/passo/Building/add_building_components/property_appraisal.dart @@ -0,0 +1,330 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:intl/intl.dart'; +import 'package:unit2/bloc/passo/additional_item/additional_item_bloc.dart'; +import 'package:unit2/model/passo/additional_items.dart'; +import 'package:unit2/screens/passo/Building/add_building.dart'; +import 'package:unit2/theme-data.dart/form-style.dart'; + +class PropertyAppraisalPage extends StatefulWidget { + PropertyAppraisalPage(); + + @override + _PropertyAppraisalPage createState() => _PropertyAppraisalPage(); +} + +class _PropertyAppraisalPage extends State { + double depRate = 0; + calculateAdditionalItems(List items) { + double sum = 0; + double product = 1; + + for (AdditionalItems value in items) { + sum += double.parse(value.adjustedMarketVal); + } + + return sum; + } + + calculateTotalConstructionCost(buildingCost, additionalItems) { + double sum = 0; + double product = 1; + + sum = buildingCost + calculateAdditionalItems(additionalItems); + + return sum; + } + + calculateMarketValue(buildingCost, additionalItems, dep) { + double sum = 0; + double depreciation = 0; + double total = 0; + + sum = buildingCost + calculateAdditionalItems(additionalItems); + + depreciation = sum * dep; + + total = sum - depreciation; + + return total; + } + + calculateDepCost(buildingCost, additionalItems, dep) { + double sum = 0; + double depreciation = 0; + double total = 0; + + sum = buildingCost + calculateAdditionalItems(additionalItems); + + depreciation = sum * dep; + + total = sum - depreciation; + + return depreciation; + } + + @override + Widget build(BuildContext context) { + return BlocConsumer( + listener: (context, state) { + // TODO: implement listener + }, + builder: (context, state) { + if (state is AdditionalItemsLoaded) { + return SingleChildScrollView( + child: Container( + margin: const EdgeInsets.only(left: 20.0, right: 20.0), + child: Column( + children: [ + Container( + margin: const EdgeInsets.only( + left: 0, top: 20, right: 0, bottom: 20), + child: const Text('PROPERTY APPRAISAL', + style: TextStyle( + fontWeight: FontWeight.bold, fontSize: 18), + textAlign: TextAlign.left), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + child: Text( + "Unit Construction Cost", + style: TextStyle( + fontWeight: FontWeight.bold, fontSize: 13), + textAlign: TextAlign.left, + ), + ), + Container( + child: Text( + formKey.currentState!.value['bldg_type'].unitValue + + ' sq.m', + textAlign: TextAlign.right, + ), + ) + ], + ), + const SizedBox(height: 15), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + child: Text( + "Building Core", + style: TextStyle( + fontWeight: FontWeight.bold, fontSize: 13), + textAlign: TextAlign.left, + ), + ), + Container( + child: Text( + '', + textAlign: TextAlign.right, + ), + ) + ], + ), + const SizedBox(height: 40), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + child: Text( + "Sub-total", + style: TextStyle( + fontWeight: FontWeight.bold, fontSize: 13), + textAlign: TextAlign.left, + ), + ), + Container( + child: Text( + (double.parse(formKey + .currentState!.value['total_area']) * + double.parse(formKey.currentState! + .value['bldg_type'].unitValue)) + .toString(), + textAlign: TextAlign.right, + ), + ) + ], + ), + const SizedBox(height: 40), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + child: Text( + "Cost of Additional Items", + style: TextStyle( + fontWeight: FontWeight.bold, fontSize: 13), + textAlign: TextAlign.left, + ), + ), + Container( + child: Text( + '', + textAlign: TextAlign.right, + ), + ) + ], + ), + const SizedBox(height: 15), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + child: Text( + "Sub-total", + style: TextStyle( + fontWeight: FontWeight.bold, fontSize: 13), + textAlign: TextAlign.left, + ), + ), + Container( + child: Text( + calculateAdditionalItems(state.items).toString(), + textAlign: TextAlign.right, + ), + ) + ], + ), + const SizedBox(height: 15), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + child: Text( + "Total Construction Cost", + style: TextStyle( + fontWeight: FontWeight.bold, fontSize: 13), + textAlign: TextAlign.left, + ), + ), + Container( + child: Text( + calculateTotalConstructionCost( + (double.parse(formKey + .currentState!.value['total_area']) * + double.parse(formKey.currentState! + .value['bldg_type'].unitValue)), + state.items) + .toString(), + textAlign: TextAlign.right, + ), + ) + ], + ), + const SizedBox(height: 40), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + child: Text( + "Depreciation Rate", + style: TextStyle( + fontWeight: FontWeight.bold, fontSize: 13), + textAlign: TextAlign.left, + ), + ), + SizedBox( + width: 90, + height: 25, + child: FormBuilderTextField( + name: 'depRate', + decoration: normalTextFieldStyle("0.00", ""), + validator: FormBuilderValidators.compose([]), + onChanged: (value) { + setState(() { + depRate = double.parse(value!); + }); + }, + ), + ), + ], + ), + const SizedBox(height: 15), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + child: Text( + "Depreciation Cost", + style: TextStyle( + fontWeight: FontWeight.bold, fontSize: 13), + textAlign: TextAlign.left, + ), + ), + Container( + child: Text( + calculateDepCost( + (double.parse(formKey + .currentState!.value['total_area']) * + double.parse(formKey.currentState! + .value['bldg_type'].unitValue)), + state.items, + depRate) + .toString(), + textAlign: TextAlign.right, + ), + ) + ], + ), + const SizedBox(height: 15), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + child: Text( + "Total % Depreciation", + style: TextStyle( + fontWeight: FontWeight.bold, fontSize: 13), + textAlign: TextAlign.left, + ), + ), + Container( + child: Text( + '0.00', + textAlign: TextAlign.right, + ), + ) + ], + ), + const SizedBox(height: 15), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + child: Text( + "Market Value", + style: TextStyle( + fontWeight: FontWeight.bold, fontSize: 13), + textAlign: TextAlign.left, + ), + ), + Container( + child: Text( + calculateMarketValue( + (double.parse(formKey + .currentState!.value['total_area']) * + double.parse(formKey.currentState! + .value['bldg_type'].unitValue)), + state.items, + depRate) + .toString(), + textAlign: TextAlign.right, + ), + ) + ], + ) + ], + ), + ), + ); + } + return Container(); + }, + ); + } +} diff --git a/lib/screens/passo/Building/add_building_components/property_assessment.dart b/lib/screens/passo/Building/add_building_components/property_assessment.dart new file mode 100644 index 0000000..e93a223 --- /dev/null +++ b/lib/screens/passo/Building/add_building_components/property_assessment.dart @@ -0,0 +1,1206 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:intl/intl.dart'; +import 'package:unit2/bloc/passo/property_appraisal/property_appraisal_bloc.dart'; +import 'package:unit2/bloc/passo/property_assessment/property_assessment_bloc.dart'; +import 'package:unit2/bloc/passo/signatories/signatories_bloc.dart'; +import 'package:unit2/model/passo/property_appraisal.dart'; +import 'package:unit2/model/passo/property_assessment.dart'; +import 'package:unit2/model/passo/signatories.dart'; +import 'package:unit2/screens/passo/Building/add_building.dart'; +import 'package:unit2/theme-data.dart/form-style.dart'; +import 'package:unit2/widgets/passo/custom_formBuilder_fields.dart'; + +class PropertyAssessmentPage extends StatefulWidget { + Function function; + + PropertyAssessmentPage(this.function); + + @override + _PropertyAssessmentPage createState() => _PropertyAssessmentPage(); +} + +class _PropertyAssessmentPage extends State { + double assessment_level = 0; + bool isTaxable = false; + bool isExempt = false; + + String assessmentLevel(marketValue, property_class) { + switch (property_class) { + case 'Residential': + if (marketValue < 175000) { + // setState(() { + // assessment_level = 0; + // }); + return '0 '; + } else if (marketValue < 300000 && marketValue > 175000) { + // setState(() { + // assessment_level = 0.10; + // }); + return '10 '; + } else if (marketValue < 500000 && marketValue > 300000) { + // setState(() { + // assessment_level = 0.20; + // }); + return '20 '; + } else if (marketValue < 750000 && marketValue > 500000) { + // setState(() { + // assessment_level = 0.25; + // }); + return '25 '; + } else if (marketValue < 1000000 && marketValue > 750000) { + // setState(() { + // assessment_level = 0.30; + // }); + return '30 '; + } else if (marketValue < 2000000 && marketValue > 1000000) { + // setState(() { + // assessment_level = 0.35; + // }); + return '35 '; + } else if (marketValue < 5000000 && marketValue > 2000000) { + // setState(() { + // assessment_level = 0.40; + // }); + return '40 '; + } else if (marketValue < 10000000 && marketValue > 5000000) { + // setState(() { + // assessment_level = 0.50; + // }); + return '50 '; + } else if (marketValue > 10000000) { + // setState(() { + // assessment_level = 0.60; + // }); + return '60 '; + } + break; + case 'Agricultural': + if (marketValue < 300000) { + // setState(() { + // assessment_level = 0.45; + // }); + return '45 '; + } else if (marketValue < 500000 && marketValue > 300000) { + // setState(() { + // assessment_level = 0.50; + // }); + return '50 '; + } else if (marketValue < 750000 && marketValue > 5000000) { + // setState(() { + // assessment_level = 0.55; + // }); + return '55 '; + } else if (marketValue < 1000000 && marketValue > 750000) { + // setState(() { + // assessment_level = 0.60; + // }); + return '60 '; + } else if (marketValue < 2000000 && marketValue > 1000000) { + // setState(() { + // assessment_level = 0.65; + // }); + return '65 '; + } else if (marketValue > 2000000) { + // setState(() { + // assessment_level = 0.70; + // }); + return '70 '; + } + break; + case 'Commercial': + if (marketValue < 300000) { + // setState(() { + // assessment_level = 0.30; + // }); + return '30 '; + } else if (marketValue < 500000 && marketValue > 300000) { + // setState(() { + // assessment_level = 0.35; + // }); + return '35 '; + } else if (marketValue < 750000 && marketValue > 500000) { + // setState(() { + // assessment_level = 0.40; + // }); + return '40 '; + } else if (marketValue < 1000000 && marketValue > 750000) { + // setState(() { + // assessment_level = 0.50; + // }); + return '50 '; + } else if (marketValue < 2000000 && marketValue > 1000000) { + // setState(() { + // assessment_level = 0.60; + // }); + return '60 '; + } else if (marketValue < 5000000 && marketValue > 2000000) { + // setState(() { + // assessment_level = 0.70; + // }); + return '70 '; + } else if (marketValue < 10000000 && marketValue > 5000000) { + // setState(() { + // assessment_level = 0.75; + // }); + return '75 '; + } else if (marketValue > 10000000) { + // setState(() { + // assessment_level = 0.80; + // }); + } + break; + case 'Industrial': + if (marketValue < 300000) { + // setState(() { + // assessment_level = 0.30; + // }); + return '30 '; + } else if (marketValue < 500000 && marketValue > 300000) { + // setState(() { + // assessment_level = 0.35; + // }); + return '35 '; + } else if (marketValue < 750000 && marketValue > 500000) { + // setState(() { + // assessment_level = 0.40; + // }); + return '40 '; + } else if (marketValue < 1000000 && marketValue > 750000) { + // setState(() { + // assessment_level = 0.50; + // }); + return '50 '; + } else if (marketValue < 2000000 && marketValue > 1000000) { + // setState(() { + // assessment_level = 0.60; + // }); + return '60 '; + } else if (marketValue < 5000000 && marketValue > 2000000) { + // setState(() { + // assessment_level = 0.70; + // }); + return '70 '; + } else if (marketValue < 10000000 && marketValue > 5000000) { + // setState(() { + // assessment_level = 0.75; + // }); + return '75 '; + } else if (marketValue > 10000000) { + // setState(() { + // assessment_level = 0.80; + // }); + return '80 '; + } + break; + case 'Mineral': + break; + case 'Timberland': + if (marketValue < 300000) { + // setState(() { + // assessment_level = 0.45; + // }); + return '45 '; + } else if (marketValue < 500000 && marketValue > 300000) { + // setState(() { + // assessment_level = 0.50; + // }); + return '50 '; + } else if (marketValue < 750000 && marketValue > 500000) { + // setState(() { + // assessment_level = 0.55; + // }); + return '55 '; + } else if (marketValue < 1000000 && marketValue > 750000) { + // setState(() { + // assessment_level = 0.60; + // }); + return '60 '; + } else if (marketValue < 2000000 && marketValue > 1000000) { + // setState(() { + // assessment_level = 0.65; + // }); + return '65 '; + } else if (marketValue < 2000000) { + // setState(() { + // assessment_level = 0.70; + // }); + return '70 '; + } + break; + default: + } + return ''; + } + + double assessmentValue(marketValue, property_class, appraisal) { + switch (property_class) { + case 'Residential': + if (marketValue < 175000) { + // setState(() { + // assessment_level = 0; + // }); + return marketValue * 0; + } else if (marketValue < 300000 && marketValue > 175000) { + // setState(() { + // assessment_level = 0.10; + // }); + return marketValue * 0.10; + } else if (marketValue < 500000 && marketValue > 300000) { + // setState(() { + // assessment_level = 0.20; + // }); + return marketValue * 0.20; + } else if (marketValue < 750000 && marketValue > 500000) { + // setState(() { + // assessment_level = 0.25; + // }); + return marketValue * 0.25; + } else if (marketValue < 1000000 && marketValue > 750000) { + // setState(() { + // assessment_level = 0.30; + // }); + return marketValue * 0.30; + } else if (marketValue < 2000000 && marketValue > 1000000) { + // setState(() { + // assessment_level = 0.35; + // }); + return marketValue * 0.35; + } else if (marketValue < 5000000 && marketValue > 2000000) { + // setState(() { + // assessment_level = 0.40; + // }); + return marketValue * 0.40; + } else if (marketValue < 10000000 && marketValue > 5000000) { + // setState(() { + // assessment_level = 0.50; + // }); + return marketValue * 0.50; + } else if (marketValue > 10000000) { + // setState(() { + // assessment_level = 0.60; + // }); + return marketValue * 0.60; + } + break; + case 'Agricultural': + if (marketValue < 300000) { + // setState(() { + // assessment_level = 0.45; + // }); + return marketValue * 0.45; + } else if (marketValue < 500000 && marketValue > 300000) { + // setState(() { + // assessment_level = 0.50; + // }); + return marketValue * 0.50; + } else if (marketValue < 750000 && marketValue > 5000000) { + // setState(() { + // assessment_level = 0.55; + // }); + return marketValue * 0.55; + } else if (marketValue < 1000000 && marketValue > 750000) { + // setState(() { + // assessment_level = 0.60; + // }); + return marketValue * 0.60; + } else if (marketValue < 2000000 && marketValue > 1000000) { + // setState(() { + // assessment_level = 0.65; + // }); + return marketValue * 0.65; + } else if (marketValue > 2000000) { + // setState(() { + // assessment_level = 0.70; + // }); + return marketValue * 0.70; + } + break; + case 'Commercial': + if (marketValue < 300000) { + // setState(() { + // assessment_level = 0.30; + // }); + return marketValue * 0.30; + } else if (marketValue < 500000 && marketValue > 300000) { + // setState(() { + // assessment_level = 0.35; + // }); + return marketValue * 0.35; + } else if (marketValue < 750000 && marketValue > 500000) { + // setState(() { + // assessment_level = 0.40; + // }); + return marketValue * 0.40; + } else if (marketValue < 1000000 && marketValue > 750000) { + // setState(() { + // assessment_level = 0.50; + // }); + return marketValue * 0.50; + } else if (marketValue < 2000000 && marketValue > 1000000) { + // setState(() { + // assessment_level = 0.60; + // }); + return marketValue * 0.60; + } else if (marketValue < 5000000 && marketValue > 2000000) { + // setState(() { + // assessment_level = 0.70; + // }); + return marketValue * 0.70; + } else if (marketValue < 10000000 && marketValue > 5000000) { + // setState(() { + // assessment_level = 0.75; + // }); + return marketValue * 0.75; + } else if (marketValue > 10000000) { + // setState(() { + // assessment_level = 0.80; + // }); + } + break; + case 'Industrial': + if (marketValue < 300000) { + // setState(() { + // assessment_level = 0.30; + // }); + return marketValue * 0.30; + } else if (marketValue < 500000 && marketValue > 300000) { + // setState(() { + // assessment_level = 0.35; + // }); + return marketValue * 0.35; + } else if (marketValue < 750000 && marketValue > 500000) { + // setState(() { + // assessment_level = 0.40; + // }); + return marketValue * 0.40; + } else if (marketValue < 1000000 && marketValue > 750000) { + // setState(() { + // assessment_level = 0.50; + // }); + return marketValue * 0.50; + } else if (marketValue < 2000000 && marketValue > 1000000) { + // setState(() { + // assessment_level = 0.60; + // }); + return marketValue * 0.60; + } else if (marketValue < 5000000 && marketValue > 2000000) { + // setState(() { + // assessment_level = 0.70; + // }); + return marketValue * 0.70; + } else if (marketValue < 10000000 && marketValue > 5000000) { + // setState(() { + // assessment_level = 0.75; + // }); + return marketValue * 0.75; + } else if (marketValue > 10000000) { + // setState(() { + // assessment_level = 0.80; + // }); + return marketValue * 0.80; + } + break; + case 'Mineral': + break; + case 'Timberland': + if (marketValue < 300000) { + // setState(() { + // assessment_level = 0.45; + // }); + return marketValue * 0.45; + } else if (marketValue < 500000 && marketValue > 300000) { + // setState(() { + // assessment_level = 0.50; + // }); + return marketValue * 0.50; + } else if (marketValue < 750000 && marketValue > 500000) { + // setState(() { + // assessment_level = 0.55; + // }); + return marketValue * 0.55; + } else if (marketValue < 1000000 && marketValue > 750000) { + // setState(() { + // assessment_level = 0.60; + // }); + return marketValue * 0.60; + } else if (marketValue < 2000000 && marketValue > 1000000) { + // setState(() { + // assessment_level = 0.65; + // }); + return marketValue * 0.65; + } else if (marketValue < 2000000) { + // setState(() { + // assessment_level = 0.70; + // }); + return marketValue * 0.70; + } + break; + default: + } + return 0; + } + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is PropertyAssessmentLoaded) { + return BlocConsumer( + listener: (context, state) { + // TODO: implement listener + }, builder: (context, state) { + if (state is PropertyAppraisalLoaded) { + final appraisal = state.appraisal; + return BlocConsumer( + listener: (context, state) { + // TODO: implement listener + }, + builder: (context, state) { + if (state is SignatoriesLoaded) { + return Column( + children: [ + Expanded( + flex: 1, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Container( + margin: const EdgeInsets.only( + left: 20.0, right: 20.0), + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + children: [ + Container( + margin: const EdgeInsets.only( + left: 0, + top: 20, + right: 0, + bottom: 20), + child: const Text( + 'PROPERTY ASSESSMENT', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 18), + textAlign: TextAlign.left), + ), + Column( + children: [ + Row( + children: [ + Container( + width: 100, + margin: const EdgeInsets.only( + top: 15, left: 15), + padding: + const EdgeInsets.all(5.0), + child: const Text( + 'Actual Use', + style: TextStyle( + fontWeight: + FontWeight.bold, + fontSize: 13, + ), + textAlign: TextAlign.center, + ), + ), + Container( + width: 100, + margin: const EdgeInsets.only( + top: 15, left: 15), + padding: + const EdgeInsets.all(5.0), + child: const Text( + 'Market Value', + style: TextStyle( + fontWeight: + FontWeight.bold, + fontSize: 13, + ), + textAlign: TextAlign.center, + ), + ), + Container( + width: 100, + margin: const EdgeInsets.only( + top: 15, left: 15), + padding: + const EdgeInsets.all(5.0), + child: const Text( + 'Ass. Level', + style: TextStyle( + fontWeight: + FontWeight.bold, + fontSize: 13, + ), + textAlign: TextAlign.center, + ), + ), + Container( + width: 100, + margin: const EdgeInsets.only( + top: 15, left: 15), + padding: + const EdgeInsets.all(5.0), + child: const Text( + 'Ass. Value', + style: TextStyle( + fontWeight: + FontWeight.bold, + fontSize: 13, + ), + textAlign: TextAlign.center, + ), + ), + ], + ), + SizedBox( + height: 59, + child: SingleChildScrollView( + scrollDirection: + Axis.horizontal, + child: Row( + children: List.generate( + appraisal.length, + (index) { + return Container( + height: 100, + child: Row( + children: [ + Container( + width: 100, + margin: + const EdgeInsets + .only( + top: 15, + left: 15), + padding: + const EdgeInsets + .all(5.0), + child: Text( + formKey.currentState! + .value[ + 'actual_use'], + style: + TextStyle( + fontWeight: + FontWeight + .bold, + fontSize: 13, + ), + textAlign: + TextAlign + .center, + ), + ), + Container( + width: 100, + margin: + const EdgeInsets + .only( + top: 15, + left: 15), + padding: + const EdgeInsets + .all(5.0), + child: Text( + NumberFormat + .currency( + locale: + 'en-PH', + symbol: "₱", + ).format(double + .parse(appraisal[ + index] + .marketValue)), + style: + TextStyle( + fontWeight: + FontWeight + .bold, + fontSize: 13, + ), + textAlign: + TextAlign + .center, + ), + ), + Container( + width: 100, + margin: + const EdgeInsets + .only( + top: 15, + left: 15), + padding: + const EdgeInsets + .all(5.0), + child: Text( + assessmentLevel( + double + .parse( + appraisal + .elementAt(index) + .marketValue, + ), + formKey + .currentState! + .value['actual_use'], + ) + + '%', + style: + TextStyle( + fontWeight: + FontWeight + .bold, + fontSize: 13, + ), + textAlign: + TextAlign + .center, + ), + ), + Container( + width: 100, + margin: + const EdgeInsets + .only( + top: 15, + left: 15), + padding: + const EdgeInsets + .all(5.0), + child: Text( + NumberFormat + .currency( + locale: + 'en-PH', + symbol: "₱", + ).format( + assessmentValue( + double.parse( + appraisal[index] + .marketValue), + formKey.currentState! + .value[ + 'actual_use'], + appraisal, + ), + ), + style: + TextStyle( + fontWeight: + FontWeight + .bold, + fontSize: 13, + ), + textAlign: + TextAlign + .center, + ), + ), + const SizedBox( + height: 80, + ), + ], + ), + ); + }, + ), + ), + ), + ) + ], + ), + ]))), + ), + Expanded( + flex: 3, + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Column(children: [ + Row( + mainAxisAlignment: + MainAxisAlignment.spaceAround, + children: [ + Row( + children: [ + const Text('Taxable'), + Checkbox( + checkColor: Colors.white, + value: isTaxable, + onChanged: (bool? value) { + setState(() { + isTaxable = value!; + }); + }, + ) + ], + ), + Row( + children: [ + const Text('Exempt'), + Checkbox( + checkColor: Colors.white, + value: isExempt, + onChanged: (bool? value) { + setState(() { + isExempt = value!; + }); + }, + ) + ], + ), + ], + ), + Column( + children: [ + const SizedBox( + height: 20, + ), + const Text( + 'EFFECTIVITY OF ASSESSMENT / REASSESSMENT :', + style: + TextStyle(fontWeight: FontWeight.bold), + ), + const SizedBox( + height: 20, + ), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceAround, + children: [ + const Text('Qtr.'), + SizedBox( + width: 70, + height: 25, + child: FormBuilderTextField( + name: 'qtr', + validator: + FormBuilderValidators.compose([]), + ), + ), + const SizedBox( + width: 20, + ), + const Text('Yr.'), + SizedBox( + width: 70, + height: 25, + child: FormBuilderTextField( + name: 'yr', + validator: + FormBuilderValidators.compose([]), + ), + ), + ], + ), + ], + ), + const SizedBox( + height: 30, + ), + Align( + alignment: Alignment.centerLeft, + child: Text( + 'APPRAISED/ASSESSED BY:', + style: TextStyle(fontWeight: FontWeight.bold), + textAlign: TextAlign.start, + ), + ), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceAround, + children: [ + Column( + children: [ + SizedBox( + width: 200, + child: FormBuilderDropdown( + name: 'appraised_by', + autofocus: false, + items: state.signatories + .map((signatories) => + DropdownMenuItem( + value: signatories, + child: Text(signatories + .firstname + + ' ' + + signatories + .middlename + + ' ' + + signatories.lastname), + )) + .toList()), + ), + Text('Name'), + ], + ), + SizedBox( + width: 15, + ), + Column( + children: [ + SizedBox( + width: 100, + child: customDatTimePicker( + "Date", "", 'app_date')), + Text('Date'), + ], + ), + ], + ), + SizedBox( + height: 30, + ), + Align( + alignment: Alignment.centerLeft, + child: Text( + 'RECOMMENDING APPROVAL:', + style: + TextStyle(fontWeight: FontWeight.bold), + )), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceAround, + children: [ + Column( + children: [ + SizedBox( + width: 200, + child: FormBuilderDropdown( + name: 'rec_approval', + autofocus: false, + items: state.signatories + .map((signatories) => + DropdownMenuItem( + value: signatories, + child: Text(signatories + .firstname + + ' ' + + signatories + .middlename + + ' ' + + signatories.lastname), + )) + .toList()), + ), + Text('Name'), + ], + ), + SizedBox( + width: 15, + ), + Column( + children: [ + SizedBox( + width: 100, + child: customDatTimePicker( + "Date", "", 'rec_date'), + ), + Text('Date'), + ], + ), + ], + ), + SizedBox( + height: 30, + ), + Align( + alignment: Alignment.centerLeft, + child: Text( + 'APPROVED BY:', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + )), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Column( + children: [ + SizedBox( + width: 200, + child: FormBuilderDropdown( + name: 'apprvd_by', + autofocus: false, + items: state.signatories + .map((signatories) => + DropdownMenuItem( + value: signatories, + child: Text(signatories + .firstname + + ' ' + + signatories + .middlename + + ' ' + + signatories.lastname), + )) + .toList()), + ), + Text('Name'), + ], + ), + ], + ), + SizedBox( + height: 50, + ), + + Align( + alignment: Alignment.centerLeft, + child: Text( + 'MEMORANDA: ', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + )), + SizedBox( + height: 30, + ), + SizedBox( + width: 500, + child: FormBuilderTextField( + name: 'memoranda', + decoration: + normalTextFieldStyle("Memoranda", ""), + minLines: + 6, // any number you need (It works as the rows for the textarea) + keyboardType: TextInputType.multiline, + maxLines: null, + ), + ), + SizedBox( + height: 30, + ), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text('Sworn Statement No. :'), + SizedBox( + width: 150, + height: 20, + child: FormBuilderTextField( + name: 'sworn_statement', + decoration: InputDecoration(), + validator: + FormBuilderValidators.compose([]), + ), + ), + ], + ), + SizedBox( + height: 30, + ), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text('Date Received:'), + SizedBox( + width: 150, + height: 20, + child: customDatTimePicker( + "Date Received", + "", + 'date_received')), + ], + ), + SizedBox( + height: 30, + ), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text('Date of Entry in the Rec. of Ass. :'), + SizedBox( + width: 150, + height: 20, + child: customDatTimePicker( + 'Date of Entry in the Rec. of Ass. :', + '', + 'date_of_entry')), + ], + ), + SizedBox( + height: 30, + ), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text('By:'), + SizedBox( + width: 150, + height: 20, + child: FormBuilderTextField( + name: 'by', + decoration: InputDecoration(), + validator: + FormBuilderValidators.compose([]), + ), + ), + ], + ), + SizedBox( + height: 30, + ), + ElevatedButton( + onPressed: () { + final List + propertyAssessments = []; + + appraisal.asMap().forEach( + (index, PropertyAppraisal appraisal) { + PropertyAssessment ass = PropertyAssessment( + id: 1, + bldgapprDetailsId: 440, + actualUse: formKey + .currentState!.value['actual_use'], + marketValue: appraisal.marketValue, + assessmentLevel: assessmentLevel( + double.parse( + appraisal.marketValue, + ), + formKey + .currentState!.value['actual_use'], + ), + assessedValue: assessmentValue( + double.parse(appraisal.marketValue), + formKey + .currentState!.value['actual_use'], + appraisal, + ).toString(), + taxable: isTaxable, + exempt: isExempt, + qtr: int.parse( + formKey.currentState!.value['qtr']), + yr: int.parse( + formKey.currentState!.value['yr']), + appraisedbyName: formKey.currentState! + .value['appraised_by'].firstname + + ' ' + + formKey + .currentState! + .value['appraised_by'] + .middlename + + ' ' + + formKey.currentState! + .value['appraised_by'].lastname, + appraisedbyDate: formKey + .currentState!.value['app_date'], + recommendapprName: formKey.currentState! + .value['rec_approval'].firstname + + ' ' + + formKey + .currentState! + .value['rec_approval'] + .middlename + + ' ' + + formKey.currentState! + .value['rec_approval'].lastname, + recommendapprDate: formKey + .currentState!.value['rec_date'], + approvedbyName: formKey.currentState! + .value['apprvd_by'].firstname + + ' ' + + formKey.currentState! + .value['apprvd_by'].middlename + + ' ' + + formKey.currentState! + .value['apprvd_by'].lastname, + memoranda: formKey + .currentState!.value['memoranda'], + swornstatementNo: formKey.currentState! + .value['sworn_statement'], + dateReceived: formKey + .currentState!.value['date_received'], + entryDateAssessment: formKey + .currentState!.value['date_of_entry'], + entryDateBy: + formKey.currentState!.value['by'], + ); + + propertyAssessments.add(ass); + }); + + context.read() + ..add(UpdatePropertyAssessment( + assessment: propertyAssessments[0])); + widget.function(); + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.red, + foregroundColor: Colors.red), + child: SizedBox( + width: 200, + height: 50, + child: Align( + alignment: Alignment.center, + child: Text( + 'Save', + style: TextStyle( + color: Colors.white, + ), + textAlign: TextAlign.center, + ), + ), + ), + ), + SizedBox( + height: 30, + ), + // Row( + // mainAxisAlignment: + // MainAxisAlignment.spaceAround, + // children: [ + // ElevatedButton( + // onPressed: () { + // // Decrement activeStep, when the previous button is tapped. However, check for lower bound i.e., must be greater than 0. + // if (activeStep > 0) { + // setState(() { + // activeStep--; + // }); + // } + // }, + // style: ElevatedButton.styleFrom( + // shape: const CircleBorder(), + // padding: + // const EdgeInsets.all(30), + // backgroundColor: Colors.red, + // foregroundColor: Colors.red), + // child: const Icon( + // Icons.chevron_left, + // color: Colors.white)), + + // ], + // ) + ]), + ), + ) + ], + ); + } + return Container(); + }, + ); + } + if (state is PropertyAppraisalErrorState) { + return Text(state.error); + } + return Container(); + }); + } + if (state is PropertyAssessmentErrorState) { + return Text(state.error); + } + return Text('null'); + }, + ); + } +} diff --git a/lib/screens/passo/Building/add_building_components/property_info.dart b/lib/screens/passo/Building/add_building_components/property_info.dart new file mode 100644 index 0000000..86c49d9 --- /dev/null +++ b/lib/screens/passo/Building/add_building_components/property_info.dart @@ -0,0 +1,113 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:intl/intl.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:unit2/widgets/passo/custom_button.dart'; +import 'package:unit2/widgets/passo/custom_formBuilder_fields.dart'; + +class PropertyInfoPage extends StatefulWidget { + final VoidCallback handleButtonPress; + PropertyInfoPage(this.handleButtonPress); + + @override + _PropertyInfoPage createState() => _PropertyInfoPage(); +} + +class _PropertyInfoPage extends State { + int tempId = 0; + final transaction_codes = ['New', 'Revision']; + + Future _loadTempId() async { + final prefs = await SharedPreferences.getInstance(); + setState(() { + tempId = (prefs.getInt('tempid') ?? 0); + }); + } + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + margin: const EdgeInsets.only( + left: 0, top: 20, right: 0, bottom: 10), + child: const Text('PROPERTY OWNER INFO', + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18), + textAlign: TextAlign.left), + ), + const SizedBox(height: 15), + customDropDownField("Transaction Code", "", "transaction_code", + transaction_codes), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + // optional flex property if flex is 1 because the default flex is 1 + flex: 1, + child: customTextField("ARP No. / TD No.", "", 'arp_td')), + const SizedBox(width: 10.0), + Expanded( + // optional flex property if flex is 1 because the default flex is 1 + flex: 1, + child: customTextField("Pin", "", 'pin')), + ], + ), + customTextField("Owner", "", 'owner'), + customTextField("Address", "", 'address'), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + flex: 1, + child: customTextField("Tel No.", "", 'tel_no'), + ), + const SizedBox(width: 10.0), + Expanded( + // optional flex property if flex is 1 because the default flex is 1 + flex: 1, + child: customTextField("TIN", "", 'tin')) + ]), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + flex: 1, + child: customTextField( + "Administrator / Benificial User", "", 'benificiary'), + ), + const SizedBox(width: 10.0), + Expanded( + // optional flex property if flex is 1 because the default flex is 1 + flex: 1, + child: customTextField("TIN", "", 'benificiary_tin')) + ]), + Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: < + Widget>[ + Expanded( + flex: 1, + child: customTextField("Address", "", 'benificiary_address'), + ), + const SizedBox(width: 10.0), + Expanded( + // optional flex property if flex is 1 because the default flex is 1 + flex: 1, + child: customTextField("Tel No.", "", 'benificiary_telno')) + ]), + const SizedBox(height: 25), + CustomButton( + icon: const Icon(Icons.chevron_right, color: Colors.white), + onPressed: () { + widget.handleButtonPress(); + }, + ) + ]), + ), + ); + } +} diff --git a/lib/screens/passo/Building/add_building_components/structural_materials.dart b/lib/screens/passo/Building/add_building_components/structural_materials.dart new file mode 100644 index 0000000..31476be --- /dev/null +++ b/lib/screens/passo/Building/add_building_components/structural_materials.dart @@ -0,0 +1,228 @@ +import 'package:flutter/material.dart'; +import 'package:multiselect/multiselect.dart'; + +class MaterialOption { + final String id; + final String label; + + MaterialOption(this.id, this.label); +} + +class StructuralMaterialsPage extends StatefulWidget { + @override + _StructuralMaterialsPage createState() => _StructuralMaterialsPage(); +} + +class _StructuralMaterialsPage extends State { + List foundation = []; + List column = []; + List beam = []; + List truss_framing = []; + List roof = []; + List flooring = []; + List walls = []; + + List columnOptions = [ + MaterialOption('steel', 'Steel'), + MaterialOption('concrete', 'Reinforced Concrete'), + MaterialOption('wood', 'Wood'), + ]; + + List selectedColumnValues = []; + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + padding: const EdgeInsets.all(30.0), + child: Column( + children: [ + Container( + margin: + const EdgeInsets.only(left: 0, top: 20, right: 0, bottom: 10), + child: const Text('STRUCTURAL MATERIALS', + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18), + textAlign: TextAlign.left), + ), + const Align( + alignment: Alignment.centerLeft, + child: Text( + 'FOUNDATION', + textAlign: TextAlign.start, + ), + ), + Padding( + padding: const EdgeInsets.only(top: 10.0, bottom: 10.0), + // DropDownMultiSelect comes from multiselect + child: DropDownMultiSelect( + selected_values_style: TextStyle(color: Colors.black), + onChanged: (List x) { + setState(() { + foundation = x; + }); + }, + options: const ['Reinforced Concrete', 'Plain Concrete'], + selectedValues: foundation, + whenEmpty: 'Select Foundations', + ), + ), + const Align( + alignment: Alignment.centerLeft, + child: Text( + 'COLUMNS', + textAlign: TextAlign.start, + ), + ), + Padding( + padding: const EdgeInsets.only(top: 10.0, bottom: 10.0), + // DropDownMultiSelect comes from multiselect + child: DropDownMultiSelect( + selected_values_style: TextStyle(color: Colors.black), + onChanged: (List x) { + setState(() { + column = x; + }); + }, + options: const ['Steel', 'Reinforced Concrete', 'Wood'], + selectedValues: column, + whenEmpty: 'Select Columns', + ), + ), + const Align( + alignment: Alignment.centerLeft, + child: Text( + 'BEAMS', + textAlign: TextAlign.start, + ), + ), + Padding( + padding: const EdgeInsets.only(top: 10.0, bottom: 10.0), + // DropDownMultiSelect comes from multiselect + child: DropDownMultiSelect( + selected_values_style: TextStyle(color: Colors.black), + onChanged: (List x) { + setState(() { + beam = x; + }); + }, + options: const ['Steel', 'Reinforced Concrete', 'Wood'], + selectedValues: beam, + whenEmpty: 'Select Beams', + ), + ), + const Align( + alignment: Alignment.centerLeft, + child: Text( + 'TRUSS FRAMING', + textAlign: TextAlign.start, + ), + ), + Padding( + padding: const EdgeInsets.only(top: 10.0, bottom: 10.0), + // DropDownMultiSelect comes from multiselect + child: DropDownMultiSelect( + selected_values_style: TextStyle(color: Colors.black), + onChanged: (List x) { + setState(() { + truss_framing = x; + }); + }, + options: const ['Steel', 'Wood'], + selectedValues: truss_framing, + whenEmpty: 'Select Truss Framing', + ), + ), + const Align( + alignment: Alignment.centerLeft, + child: Text( + 'ROOF', + textAlign: TextAlign.start, + ), + ), + Padding( + padding: const EdgeInsets.only(top: 10.0, bottom: 10.0), + // DropDownMultiSelect comes from multiselect + child: DropDownMultiSelect( + selected_values_style: TextStyle(color: Colors.black), + onChanged: (List x) { + setState(() { + roof = x; + }); + }, + options: const [ + 'Reinforced Concrete', + 'Tiles', + 'G.I Sheet', + 'Aluminum', + 'Asbestos', + 'Long Span', + 'Concrete Desk', + 'Nipa/Anahaw/Cogon' + ], + selectedValues: roof, + whenEmpty: 'Select Roofs', + ), + ), + const Align( + alignment: Alignment.centerLeft, + child: Text( + 'FLOORING', + textAlign: TextAlign.start, + ), + ), + Padding( + padding: const EdgeInsets.only(top: 10.0, bottom: 10.0), + // DropDownMultiSelect comes from multiselect + child: DropDownMultiSelect( + selected_values_style: TextStyle(color: Colors.black), + onChanged: (List x) { + setState(() { + flooring = x; + }); + }, + options: const [ + 'Reinforced Concrete', + 'Plain Cement', + 'Marble', + 'Wood', + 'Tiles' + ], + selectedValues: flooring, + whenEmpty: 'Select Floorings', + ), + ), + const Align( + alignment: Alignment.centerLeft, + child: Text( + 'WALLS & PARTITIONS', + textAlign: TextAlign.start, + ), + ), + Padding( + padding: const EdgeInsets.only(top: 10.0, bottom: 10.0), + // DropDownMultiSelect comes from multiselect + child: DropDownMultiSelect( + selected_values_style: TextStyle(color: Colors.black), + onChanged: (List x) { + setState(() { + walls = x; + }); + }, + options: const [ + 'Reinforced Concrete', + 'Plain Concrete', + 'Wood', + 'CHIB', + 'G.I Sheet', + 'Build-a-wall', + 'Sawali', + 'Bamboo' + ], + selectedValues: walls, + whenEmpty: 'Select Foundation', + ), + ), + ], + ), + ); + } +} diff --git a/lib/screens/passo/Test Envi/multi_dropdown.dart b/lib/screens/passo/Test Envi/multi_dropdown.dart new file mode 100644 index 0000000..61a88e4 --- /dev/null +++ b/lib/screens/passo/Test Envi/multi_dropdown.dart @@ -0,0 +1,94 @@ +import 'package:flutter/material.dart'; +import 'package:multiselect/multiselect.dart'; + +class Multi_Select extends StatelessWidget { + const Multi_Select(); + + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + brightness: Brightness.light, + primarySwatch: Colors.red, + primaryColor: Colors.red, + primaryColorLight: Colors.redAccent, + inputDecorationTheme: const InputDecorationTheme( + filled: true, + fillColor: Color(0xFFEEEEEE), + ), + ), + themeMode: ThemeMode.dark, + darkTheme: ThemeData( + brightness: Brightness.dark, + primarySwatch: Colors.red, + primaryColor: Colors.red, + primaryColorLight: Colors.redAccent, + appBarTheme: const AppBarTheme(backgroundColor: Color(0xFF1b1926)), + snackBarTheme: const SnackBarThemeData(backgroundColor: Colors.red), + canvasColor: const Color(0xFF272537), + dialogBackgroundColor: const Color(0xFF343346), + inputDecorationTheme: const InputDecorationTheme( + filled: true, + fillColor: Color(0xFF383849), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.transparent), + borderRadius: BorderRadius.all( + Radius.circular(35.0), + ), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.transparent), + borderRadius: BorderRadius.all( + Radius.circular(35.0), + ), + ), + ), + ), + home: const _Multi_Select(title: 'Flutter Demo Home Page'), + ); + } +} + +class _Multi_Select extends StatefulWidget { + const _Multi_Select({required this.title}); + final String title; + + @override + State<_Multi_Select> createState() => _Multi_SelectState(); +} + +class _Multi_SelectState extends State<_Multi_Select> { + int _counter = 0; + + void _incrementCounter() { + setState(() { + _counter++; + }); + } + + List selected = []; + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: Padding( + padding: const EdgeInsets.all(20.0), + // DropDownMultiSelect comes from multiselect + child: DropDownMultiSelect( + selected_values_style: TextStyle(color: Colors.white), + onChanged: (List x) { + setState(() { + selected = x; + }); + }, + options: ['a', 'b', 'c', 'd'], + selectedValues: selected, + whenEmpty: 'Select Something', + ), + ), + )); + } +} diff --git a/lib/screens/passo/Test Envi/speed_dial.dart b/lib/screens/passo/Test Envi/speed_dial.dart new file mode 100644 index 0000000..7d4c1c9 --- /dev/null +++ b/lib/screens/passo/Test Envi/speed_dial.dart @@ -0,0 +1,529 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter_speed_dial/flutter_speed_dial.dart'; + +void main() => runApp(const SpeedDial()); + +class SpeedDials extends StatefulWidget { + const SpeedDials({Key? key}) : super(key: key); + @override + _SpeedDials createState() => _SpeedDials(); +} + +class _SpeedDials extends State { + var theme = ValueNotifier(ThemeMode.dark); + + @override + Widget build(BuildContext context) { + const appTitle = 'Flutter Speed Dial Example'; + return ValueListenableBuilder( + valueListenable: theme, + builder: (context, value, child) => MaterialApp( + title: appTitle, + home: MyHomePage(theme: theme), + debugShowCheckedModeBanner: false, + theme: ThemeData( + brightness: Brightness.light, + primaryColor: Colors.blue, + ), + darkTheme: ThemeData( + brightness: Brightness.dark, + primaryColor: Colors.lightBlue[900], + ), + themeMode: value, + )); + } +} + +class MyHomePage extends StatefulWidget { + final ValueNotifier theme; + const MyHomePage({Key? key, required this.theme}) : super(key: key); + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State with TickerProviderStateMixin { + var renderOverlay = true; + var visible = true; + var switchLabelPosition = false; + var extend = false; + var mini = false; + var rmicons = false; + var customDialRoot = false; + var closeManually = false; + var useRAnimation = true; + var isDialOpen = ValueNotifier(false); + var speedDialDirection = SpeedDialDirection.up; + var buttonSize = const Size(56.0, 56.0); + var childrenButtonSize = const Size(56.0, 56.0); + var selectedfABLocation = FloatingActionButtonLocation.endDocked; + var items = [ + FloatingActionButtonLocation.startFloat, + FloatingActionButtonLocation.startDocked, + FloatingActionButtonLocation.centerFloat, + FloatingActionButtonLocation.endFloat, + FloatingActionButtonLocation.endDocked, + FloatingActionButtonLocation.startTop, + FloatingActionButtonLocation.centerTop, + FloatingActionButtonLocation.endTop, + ]; + @override + Widget build(BuildContext context) { + return WillPopScope( + onWillPop: () async { + if (isDialOpen.value) { + isDialOpen.value = false; + return false; + } + return true; + }, + child: Scaffold( + appBar: AppBar( + title: const Text("Flutter Speed Dial Example"), + ), + body: SingleChildScrollView( + padding: const EdgeInsets.all(16), + physics: const BouncingScrollPhysics(), + child: Center( + child: Container( + constraints: const BoxConstraints(maxWidth: 800), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: const EdgeInsets.symmetric( + vertical: 6, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("SpeedDial Location", + style: Theme.of(context).textTheme.bodyLarge), + const SizedBox(height: 10), + Container( + decoration: BoxDecoration( + color: Theme.of(context).brightness == + Brightness.dark + ? Colors.grey[800] + : Colors.grey[200], + borderRadius: BorderRadius.circular(10)), + child: DropdownButton( + value: selectedfABLocation, + isExpanded: true, + icon: const Icon(Icons.arrow_drop_down), + iconSize: 20, + underline: const SizedBox(), + onChanged: (fABLocation) => setState( + () => selectedfABLocation = fABLocation!), + selectedItemBuilder: (BuildContext context) { + return items.map((item) { + return Align( + alignment: Alignment.centerLeft, + child: Container( + padding: const EdgeInsets.symmetric( + vertical: 4, horizontal: 10), + child: Text(item.value))); + }).toList(); + }, + items: items.map((item) { + return DropdownMenuItem< + FloatingActionButtonLocation>( + value: item, + child: Text( + item.value, + ), + ); + }).toList(), + ), + ), + ], + ), + ), + Container( + padding: const EdgeInsets.symmetric( + vertical: 6, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("SpeedDial Direction", + style: Theme.of(context).textTheme.bodyLarge), + const SizedBox(height: 10), + Container( + decoration: BoxDecoration( + color: Theme.of(context).brightness == + Brightness.dark + ? Colors.grey[800] + : Colors.grey[200], + borderRadius: BorderRadius.circular(10)), + child: DropdownButton( + value: speedDialDirection, + isExpanded: true, + icon: const Icon(Icons.arrow_drop_down), + iconSize: 20, + underline: const SizedBox(), + onChanged: (sdo) { + setState(() { + speedDialDirection = sdo!; + selectedfABLocation = (sdo.isUp && + selectedfABLocation.value + .contains("Top")) || + (sdo.isLeft && + selectedfABLocation.value + .contains("start")) + ? FloatingActionButtonLocation.endDocked + : sdo.isDown && + !selectedfABLocation.value + .contains("Top") + ? FloatingActionButtonLocation.endTop + : sdo.isRight && + selectedfABLocation.value + .contains("end") + ? FloatingActionButtonLocation + .startDocked + : selectedfABLocation; + }); + }, + selectedItemBuilder: (BuildContext context) { + return SpeedDialDirection.values + .toList() + .map((item) { + return Container( + padding: const EdgeInsets.symmetric( + vertical: 4, horizontal: 10), + child: Align( + alignment: Alignment.centerLeft, + child: Text( + describeEnum(item).toUpperCase())), + ); + }).toList(); + }, + items: SpeedDialDirection.values + .toList() + .map((item) { + return DropdownMenuItem( + value: item, + child: Text(describeEnum(item).toUpperCase()), + ); + }).toList(), + ), + ), + ], + ), + ), + if (!customDialRoot) + SwitchListTile( + contentPadding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, + ), + value: extend, + title: const Text("Extend Speed Dial"), + onChanged: (val) { + setState(() { + extend = val; + }); + }), + SwitchListTile( + contentPadding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, + ), + value: visible, + title: const Text("Visible"), + onChanged: (val) { + setState(() { + visible = val; + }); + }), + SwitchListTile( + contentPadding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, + ), + value: mini, + title: const Text("Mini"), + onChanged: (val) { + setState(() { + mini = val; + }); + }), + SwitchListTile( + contentPadding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, + ), + value: customDialRoot, + title: const Text("Custom dialRoot"), + onChanged: (val) { + setState(() { + customDialRoot = val; + }); + }), + SwitchListTile( + contentPadding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, + ), + value: renderOverlay, + title: const Text("Render Overlay"), + onChanged: (val) { + setState(() { + renderOverlay = val; + }); + }), + SwitchListTile( + contentPadding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, + ), + value: closeManually, + title: const Text("Close Manually"), + onChanged: (val) { + setState(() { + closeManually = val; + }); + }), + SwitchListTile( + contentPadding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, + ), + value: rmicons, + title: const Text("Remove Icons (for children)"), + onChanged: (val) { + setState(() { + rmicons = val; + }); + }), + if (!customDialRoot) + SwitchListTile( + contentPadding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, + ), + value: useRAnimation, + title: const Text("Use Rotation Animation"), + onChanged: (val) { + setState(() { + useRAnimation = val; + }); + }), + SwitchListTile( + contentPadding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, + ), + value: switchLabelPosition, + title: const Text("Switch Label Position"), + onChanged: (val) { + setState(() { + switchLabelPosition = val; + if (val) { + if ((selectedfABLocation.value.contains("end") || + selectedfABLocation.value + .toLowerCase() + .contains("top")) && + speedDialDirection.isUp) { + selectedfABLocation = + FloatingActionButtonLocation.startDocked; + } else if ((selectedfABLocation.value + .contains("end") || + !selectedfABLocation.value + .toLowerCase() + .contains("top")) && + speedDialDirection.isDown) { + selectedfABLocation = + FloatingActionButtonLocation.startTop; + } + } + }); + }), + const Text("Button Size"), + Slider( + value: buttonSize.width, + min: 50, + max: 500, + label: "Button Size", + onChanged: (val) { + setState(() { + buttonSize = Size(val, val); + }); + }, + ), + const Text("Children Button Size"), + Slider( + value: childrenButtonSize.height, + min: 50, + max: 500, + onChanged: (val) { + setState(() { + childrenButtonSize = Size(val, val); + }); + }, + ), + Container( + padding: const EdgeInsets.symmetric( + vertical: 6, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Navigation", + style: Theme.of(context).textTheme.bodyLarge), + const SizedBox(height: 10), + ElevatedButton( + onPressed: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => + MyHomePage(theme: widget.theme), + ), + ); + }, + child: const Text("Push Duplicate Page")), + ], + ), + ), + ], + ), + ), + )), + floatingActionButtonLocation: selectedfABLocation, + floatingActionButton: SpeedDial( + // animatedIcon: AnimatedIcons.menu_close, + // animatedIconTheme: IconThemeData(size: 22.0), + // / This is ignored if animatedIcon is non null + // child: Text("open"), + // activeChild: Text("close"), + icon: Icons.add, + activeIcon: Icons.close, + spacing: 3, + mini: mini, + openCloseDial: isDialOpen, + childPadding: const EdgeInsets.all(5), + spaceBetweenChildren: 4, + dialRoot: customDialRoot + ? (ctx, open, toggleChildren) { + return ElevatedButton( + onPressed: toggleChildren, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue[900], + padding: const EdgeInsets.symmetric( + horizontal: 22, vertical: 18), + ), + child: const Text( + "Custom Dial Root", + style: TextStyle(fontSize: 17), + ), + ); + } + : null, + buttonSize: + buttonSize, // it's the SpeedDial size which defaults to 56 itself + // iconTheme: IconThemeData(size: 22), + label: extend + ? const Text("Open") + : null, // The label of the main button. + /// The active label of the main button, Defaults to label if not specified. + activeLabel: extend ? const Text("Close") : null, + + /// Transition Builder between label and activeLabel, defaults to FadeTransition. + // labelTransitionBuilder: (widget, animation) => ScaleTransition(scale: animation,child: widget), + /// The below button size defaults to 56 itself, its the SpeedDial childrens size + childrenButtonSize: childrenButtonSize, + visible: visible, + direction: speedDialDirection, + switchLabelPosition: switchLabelPosition, + + /// If true user is forced to close dial manually + closeManually: closeManually, + + /// If false, backgroundOverlay will not be rendered. + renderOverlay: renderOverlay, + // overlayColor: Colors.black, + // overlayOpacity: 0.5, + onOpen: () => debugPrint('OPENING DIAL'), + onClose: () => debugPrint('DIAL CLOSED'), + useRotationAnimation: useRAnimation, + tooltip: 'Open Speed Dial', + heroTag: 'speed-dial-hero-tag', + // foregroundColor: Colors.black, + // backgroundColor: Colors.white, + // activeForegroundColor: Colors.red, + // activeBackgroundColor: Colors.blue, + elevation: 8.0, + animationCurve: Curves.elasticInOut, + isOpenOnStart: false, + shape: customDialRoot + ? const RoundedRectangleBorder() + : const StadiumBorder(), + // childMargin: EdgeInsets.symmetric(horizontal: 10, vertical: 5), + children: [ + SpeedDialChild( + child: !rmicons ? const Icon(Icons.accessibility) : null, + backgroundColor: Colors.red, + foregroundColor: Colors.white, + label: 'First', + onTap: () => setState(() => rmicons = !rmicons), + onLongPress: () => debugPrint('FIRST CHILD LONG PRESS'), + ), + SpeedDialChild( + child: !rmicons ? const Icon(Icons.brush) : null, + backgroundColor: Colors.deepOrange, + foregroundColor: Colors.white, + label: 'Second', + onTap: () => debugPrint('SECOND CHILD'), + ), + SpeedDialChild( + child: !rmicons ? const Icon(Icons.margin) : null, + backgroundColor: Colors.indigo, + foregroundColor: Colors.white, + label: 'Show Snackbar', + visible: true, + onTap: () => ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text(("Third Child Pressed")))), + onLongPress: () => debugPrint('THIRD CHILD LONG PRESS'), + ), + ], + ), + bottomNavigationBar: BottomAppBar( + shape: const CircularNotchedRectangle(), + notchMargin: 8.0, + child: Row( + mainAxisAlignment: selectedfABLocation == + FloatingActionButtonLocation.startDocked + ? MainAxisAlignment.end + : selectedfABLocation == FloatingActionButtonLocation.endDocked + ? MainAxisAlignment.start + : MainAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + IconButton( + icon: const Icon(Icons.nightlight_round), + tooltip: "Switch Theme", + onPressed: () => { + widget.theme.value = widget.theme.value.index == 2 + ? ThemeMode.light + : ThemeMode.dark + }, + ), + ValueListenableBuilder( + valueListenable: isDialOpen, + builder: (ctx, value, _) => IconButton( + icon: const Icon(Icons.open_in_browser), + tooltip: (!value ? "Open" : "Close") + (" Speed Dial"), + onPressed: () => {isDialOpen.value = !isDialOpen.value}, + )) + ], + ), + ), + ), + ); + } +} + +extension EnumExt on FloatingActionButtonLocation { + /// Get Value of The SpeedDialDirection Enum like Up, Down, etc. in String format + String get value => toString().split(".")[1]; +} diff --git a/lib/screens/passo/passo_home.dart b/lib/screens/passo/passo_home.dart new file mode 100644 index 0000000..dc6240d --- /dev/null +++ b/lib/screens/passo/passo_home.dart @@ -0,0 +1,235 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:unit2/bloc/passo/additional_item/additional_item_bloc.dart'; +import 'package:unit2/bloc/passo/class_components/class_components_bloc.dart'; +import 'package:unit2/bloc/passo/property_appraisal/property_appraisal_bloc.dart'; +import 'package:unit2/bloc/passo/property_assessment/property_assessment_bloc.dart'; +import 'package:unit2/bloc/passo/property_info/property_info_bloc.dart'; +import 'package:unit2/bloc/passo/signatories/signatories_bloc.dart'; +import 'package:unit2/bloc/passo/unit_construct/unit_construct_bloc.dart'; +import 'package:unit2/bloc/user/user_bloc.dart'; +import 'package:unit2/model/passo/property_info.dart'; +import 'package:unit2/model/profile/basic_information/primary-information.dart'; +import 'package:unit2/screens/passo/Building/add_building.dart'; +import 'package:unit2/screens/passo/Test%20Envi/multi_dropdown.dart'; +import 'package:unit2/screens/passo/Test%20Envi/speed_dial.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/widgets/error_state.dart'; +import 'package:flutter_speed_dial/flutter_speed_dial.dart'; + +class PassoHome extends StatelessWidget { + const PassoHome({super.key}); + + @override + Widget build(BuildContext context) { + int? profileId; + String? token; + Profile profile; + return Scaffold( + appBar: AppBar( + backgroundColor: primary, + centerTitle: true, + title: const Text("FAAS LIST"), + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocBuilder(builder: (context, state) { + if (state is UserLoggedIn) { + profileId = state.userData!.user!.login!.user!.profileId; + token = state.userData!.user!.login!.token!; + profile = state.userData!.employeeInfo!.profile!; + return BlocConsumer( + listener: ( + context, + state, + ) { + if (state is PropertyInfoLoading) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is PropertyInfoLoaded || + state is PropertyInfoErrorState) { + final progress = ProgressHUD.of(context); + progress?.dismiss(); + } + }, + builder: (context, state) { + if (state is PropertyInfoLoaded) { + List propertyList = state.property_info; + return Container( + padding: const EdgeInsets.symmetric( + vertical: 12, horizontal: 12), + child: Expanded( + child: ListView.builder( + shrinkWrap: true, + itemCount: propertyList.length, + itemBuilder: (BuildContext context, int index) { + return _listCard( + propertyList[index], context, index); + }, + ), + ), + ); + } + // if(state is PropertyInfoErrorState){ + // return SomethingWentWrong( + // message: onError, + // onpressed: () { + // BlocProvider.of( + // NavigationService.navigatorKey.currentContext!) + // .add(GetApkVersion()); + // return MaterialPageRoute(builder: (_) { + // return const UniT2Login(); + // }); + // }, + // ); + // } + return Container(); + }, + ); + } + return Container(); + })), + floatingActionButton: SpeedDial( + +//provide here features of your parent FAB + icon: Icons.add, + backgroundColor: const Color(0xffd92828), + heroTag: null, + useRotationAnimation: true, + activeIcon: Icons.close, + animationCurve: Curves.elasticInOut, + children: [ + SpeedDialChild( + child: const Icon( + Icons.maps_home_work_rounded, + color: Color(0xffd92828), + ), + label: 'Building & Other Structure', + onTap: () { + Navigator.push(context, + MaterialPageRoute(builder: (BuildContext context) { + return MultiBlocProvider(providers: [ + BlocProvider( + create: (context) => + ClassComponentsBloc()..add(LoadClassComponents()), + ), + BlocProvider( + create: (context) => + UnitConstructBloc()..add(LoadUnitConstruct()), + ), + BlocProvider( + create: (context) => + AdditionalItemBloc()..add(LoadAdditionalItems()), + ), + BlocProvider( + create: (context) => PropertyAppraisalBloc() + ..add(LoadPropertyAppraisal()), + ), + BlocProvider( + create: (context) => PropertyAssessmentBloc() + ..add(LoadPropertyAssessment())), + BlocProvider( + create: (context) => + SignatoriesBloc()..add(LoadSignatories())), + ], child: AddBuilding()); + })); + }), + SpeedDialChild( + child: const Icon( + Icons.forest_rounded, + color: Color(0xffd92828), + ), + label: 'Land/Other Improvements', + onTap: () {}, + ), + SpeedDialChild( + child: const Icon( + Icons.precision_manufacturing_rounded, + color: Color(0xffd92828), + ), + label: 'Machinery', + onTap: () {}, + ), + SpeedDialChild( + child: const Icon( + Icons.report_problem_rounded, + color: Color(0xffd92828), + ), + label: 'Testing Screen', + onTap: () { + Navigator.of(context) + .push(MaterialPageRoute(builder: (ctx) => Multi_Select())); + }, + ), + ]), + ); + } +} + +Card _listCard(PropertyInfo property_info, context, index) { + return Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + //set border radius more than 50% of height and width to make circle + ), + margin: const EdgeInsets.all(5.0), + elevation: 5, + shadowColor: Colors.grey, + child: Padding( + padding: const EdgeInsets.all(15.0), + child: InkWell( + onTap: () async {}, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(width: 20), + Row( + children: [ + Text( + '${property_info.owner}', + textAlign: TextAlign.start, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + const SizedBox(height: 5), + Text( + '${property_info.tdn}', + textAlign: TextAlign.start, + style: const TextStyle( + fontSize: 13, + ), + ), + ], + ), + Container( + alignment: Alignment.topCenter, + padding: const EdgeInsets.all(3.0), + decoration: BoxDecoration( + border: Border.all(color: Colors.red), + borderRadius: BorderRadius.all(Radius.circular(8.0))), + child: const Text(' BUILDING ', + style: TextStyle( + color: Colors.red, + fontSize: 10, + fontWeight: FontWeight.bold)), + ), + IconButton(onPressed: () {}, icon: const Icon(Icons.chevron_right)), + ], + ), + ), + ), + ); +} diff --git a/lib/screens/unit2/homepage.dart/components/dashboard.dart b/lib/screens/unit2/homepage.dart/components/dashboard.dart index e1056ac..07e0d11 100644 --- a/lib/screens/unit2/homepage.dart/components/dashboard.dart +++ b/lib/screens/unit2/homepage.dart/components/dashboard.dart @@ -12,7 +12,7 @@ import '../../../../bloc/docsms/docsms_bloc.dart'; class DashBoard extends StatelessWidget { final List roles; final int userId; - const DashBoard({super.key, required this.roles,required this.userId}); + const DashBoard({super.key, required this.roles, required this.userId}); @override Widget build(BuildContext context) { List finishRoles = []; @@ -25,7 +25,6 @@ class DashBoard extends StatelessWidget { shrinkWrap: true, itemCount: roles.length, itemBuilder: (BuildContext context, int index) { - //// gridview.count return roles[index].roles.isNotEmpty ? SizedBox( @@ -55,18 +54,24 @@ class DashBoard extends StatelessWidget { if (role.role.name!.toLowerCase() == 'qr code scanner' && !finishRoles.contains("security")) { - print("1 true"); + print("1 true"); finishRoles.add('scanner'); for (var element in role.role.modules!) { if (element!.name!.toLowerCase() == 'unit2') { for (var element in element.objects!) { - if (element!.id == 9 && + if (element!.id == 9 && element.operations! .contains("read")) { return CardLabel( ontap: () { - PassCheckArguments passCheckArguments = PassCheckArguments(roleId: role.role.id!, userId: userId); - Navigator.pushNamed(context, '/pass-check',arguments: passCheckArguments); + PassCheckArguments + passCheckArguments = + PassCheckArguments( + roleId: role.role.id!, + userId: userId); + Navigator.pushNamed( + context, '/pass-check', + arguments: passCheckArguments); }, icon: role.icon, title: "Pass Check", @@ -79,7 +84,7 @@ class DashBoard extends StatelessWidget { } else if (role.role.name!.toLowerCase() == 'security guard' && !finishRoles.contains('scanner')) { - print("2 true"); + print("2 true"); finishRoles.add('security'); for (var element in role.role.modules!) { if (element!.name!.toLowerCase() == 'unit2') { @@ -96,10 +101,12 @@ class DashBoard extends StatelessWidget { } } } - return Container(color: Colors.red,); + return Container( + color: Colors.red, + ); } else if (role.role.name!.toLowerCase() == 'field surveyor') { - print("3 true"); + print("3 true"); for (var element in role.role.modules!) { if (element!.name!.toLowerCase() == 'rpass') { for (var element in element.objects!) { @@ -107,7 +114,10 @@ class DashBoard extends StatelessWidget { element.operations! .contains("read")) { return CardLabel( - ontap: () {}, + ontap: () { + Navigator.pushNamed( + context, '/passo-home'); + }, icon: role.icon, title: "Field Surveyor", ); @@ -118,7 +128,7 @@ class DashBoard extends StatelessWidget { return Container(); } else if (role.role.name!.toLowerCase() == 'process server') { - print("4 true"); + print("4 true"); for (var element in role.role.modules!) { if (element!.name!.toLowerCase() == 'document management') { @@ -131,12 +141,17 @@ class DashBoard extends StatelessWidget { String? qrBarcode = await qrScanner(); if (qrBarcode != null) { - Navigator.push(NavigationService.navigatorKey.currentContext!, MaterialPageRoute(builder: - (BuildContext context) { + Navigator.push( + NavigationService.navigatorKey + .currentContext!, + MaterialPageRoute(builder: + (BuildContext context) { return BlocProvider( - create: (context) => DocsmsBloc() - ..add(LoadDocument( - documentId: qrBarcode)), + create: (context) => + DocsmsBloc() + ..add(LoadDocument( + documentId: + qrBarcode)), child: const AutoReceiveDocument(), ); @@ -151,12 +166,12 @@ class DashBoard extends StatelessWidget { } } return Container(); - } else if (role.role.name!.toLowerCase() == + } else if (role.role.name!.toLowerCase() == 'establishment point-person' && !finishRoles.contains('superadmin')) { finishRoles.add('establishment point-person'); for (var element in role.role.modules!) { - print("5 true"); + print("5 true"); if (element!.name!.toLowerCase() == 'unit2') { for (var element in element.objects!) { if (element!.id == 7 && @@ -172,8 +187,7 @@ class DashBoard extends StatelessWidget { } } return Container(); - - } else if (role.role.name!.toLowerCase() == + } else if (role.role.name!.toLowerCase() == 'establishment point-person' && !finishRoles.contains('superadmin')) { finishRoles.add('establishment point-person'); @@ -193,12 +207,9 @@ class DashBoard extends StatelessWidget { } } return Container(); - - } else{ + } else { return Wrap(); } - - }).toList()), const SizedBox( height: 8, @@ -212,7 +223,7 @@ class DashBoard extends StatelessWidget { } } -class PassCheckArguments{ +class PassCheckArguments { final int roleId; final int userId; const PassCheckArguments({required this.roleId, required this.userId}); diff --git a/lib/sevices/passo/building/additional_items_services.dart b/lib/sevices/passo/building/additional_items_services.dart new file mode 100644 index 0000000..5215809 --- /dev/null +++ b/lib/sevices/passo/building/additional_items_services.dart @@ -0,0 +1,53 @@ +import 'dart:convert'; +import 'dart:developer'; +import 'dart:io'; + +import 'package:unit2/model/passo/additional_items.dart'; +import 'package:http/http.dart' as http; + +class AdditionalItemsServices { + static final AdditionalItemsServices _instance = AdditionalItemsServices(); + static AdditionalItemsServices get instance => _instance; + + Future> getAdditionalItems(tempID) async { + http.Response response = await http.get(Uri.parse( + 'http://192.168.10.218:8000/api/rptass_app/additional_items/?bldgappr_details_id=$tempID')); + if (response.statusCode == 200) { + final List result = jsonDecode(response.body)['data']; + + return result.map(((e) => AdditionalItems.fromJson(e))).toList(); + } else { + throw Exception(response.reasonPhrase); + } + } + + Future postAdditionalItems(AdditionalItems items) async { + http.Response? response; + try { + response = await http.post( + Uri.parse( + "http://192.168.10.218:8000/api/rptass_app/additional_items/"), + headers: { + HttpHeaders.contentTypeHeader: "application/json", + }, + body: jsonEncode(items.toJson())); + } catch (e) { + log(e.toString()); + } + return response; + } + + Future removeAdditionalItems(id) async { + http.Response response = await http.delete( + Uri.parse( + 'http://192.168.10.218:8000/api/rptass_app/additional_items/?id=$id'), + ); + print(id); + if (response.statusCode == 200) { + print(response.body); + return response; + } else { + throw Exception(response.reasonPhrase); + } + } +} diff --git a/lib/sevices/passo/building/location_landref_services.dart b/lib/sevices/passo/building/location_landref_services.dart new file mode 100644 index 0000000..6098b76 --- /dev/null +++ b/lib/sevices/passo/building/location_landref_services.dart @@ -0,0 +1,27 @@ +import 'dart:convert'; +import 'dart:developer'; +import 'dart:io'; + +import 'package:http/http.dart' as http; +import 'package:unit2/model/passo/bldg_loc.dart'; + +class LocationLandrefServices { + static final LocationLandrefServices _instance = LocationLandrefServices(); + static LocationLandrefServices get instance => _instance; + + Future bldgLocPutInfo(BldgLoc data, id) async { + http.Response? response; + try { + response = await http.put(Uri.parse( + // ignore: unnecessary_brace_in_string_interps + "http://192.168.10.218:8000/api/rptass_app/bldgappr_location/?bldgappr_details_id=${id}"), + headers: { + HttpHeaders.contentTypeHeader: "application/json", + }, + body: jsonEncode(data.toJson())); + } catch (e) { + log(e.toString()); + } + return response; + } +} diff --git a/lib/sevices/passo/building/property_appraisal_services.dart b/lib/sevices/passo/building/property_appraisal_services.dart new file mode 100644 index 0000000..44774c7 --- /dev/null +++ b/lib/sevices/passo/building/property_appraisal_services.dart @@ -0,0 +1,43 @@ +import 'dart:convert'; +import 'dart:developer'; +import 'dart:io'; + +import 'package:unit2/model/passo/property_appraisal.dart'; +import 'package:http/http.dart' as http; + +class PropertyAppraisalServices { + static final PropertyAppraisalServices _instance = + PropertyAppraisalServices(); + static PropertyAppraisalServices get instance => _instance; + + Future> getPropertyAppraisal(tempID) async { + http.Response response = await http.get(Uri.parse( + 'http://192.168.10.218:8000/api/rptass_app/property_appraisal/?bldgappr_details_id=$tempID')); + print('Appraisal'); + print(response.body); + if (response.statusCode == 200) { + final List result = jsonDecode(response.body)['data']; + + return result.map(((e) => PropertyAppraisal.fromJson(e))).toList(); + } else { + throw Exception(response.reasonPhrase); + } + } + + Future postPropertyAppraisal( + PropertyAppraisal appraisal) async { + http.Response? response; + try { + response = await http.post( + Uri.parse( + "http://192.168.10.218:8000/api/rptass_app/property_appraisal/"), + headers: { + HttpHeaders.contentTypeHeader: "application/json", + }, + body: jsonEncode(appraisal.toJson())); + } catch (e) { + log(e.toString()); + } + return response; + } +} diff --git a/lib/sevices/passo/building/property_assessment_services.dart b/lib/sevices/passo/building/property_assessment_services.dart new file mode 100644 index 0000000..ae98b46 --- /dev/null +++ b/lib/sevices/passo/building/property_assessment_services.dart @@ -0,0 +1,61 @@ +import 'dart:convert'; +import 'dart:developer'; +import 'dart:io'; + +import 'package:unit2/model/passo/property_assessment.dart'; +import 'package:http/http.dart' as http; + +class PropertyAssessmentServices { + static final PropertyAssessmentServices _instance = + PropertyAssessmentServices(); + static PropertyAssessmentServices get instance => _instance; + + Future> getPropertyAssessment(tempID) async { + http.Response response = await http.get(Uri.parse( + 'http://192.168.10.218:8000/api/rptass_app/property_assessment/?bldgappr_details_id=$tempID')); + print('Assessment'); + print(response.statusCode); + if (response.statusCode == 200) { + final List result = jsonDecode(response.body)['data']; + + return result.map(((e) => PropertyAssessment.fromJson(e))).toList(); + } else { + throw Exception(response.reasonPhrase); + } + } + + Future postPropertyAssessment( + PropertyAssessment assessment) async { + http.Response? response; + try { + response = await http.post( + Uri.parse( + "http://192.168.10.218:8000/api/rptass_app/property_assessment/"), + headers: { + HttpHeaders.contentTypeHeader: "application/json", + }, + body: jsonEncode(assessment.toJson())); + } catch (e) { + log(e.toString()); + } + return response; + } + + Future propertyAssessmentPutInfo( + PropertyAssessment assessment, id) async { + print(id); + http.Response? response; + try { + response = await http.put(Uri.parse( + // ignore: unnecessary_brace_in_string_interps + "http://192.168.10.218:8000/api/rptass_app/property_assessment/?bldgappr_details_id=${id}"), + headers: { + HttpHeaders.contentTypeHeader: "application/json", + }, + body: jsonEncode(assessment.toJson())); + } catch (e) { + log(e.toString()); + } + return response; + } +} diff --git a/lib/sevices/passo/building/property_info_services.dart b/lib/sevices/passo/building/property_info_services.dart new file mode 100644 index 0000000..5503ad3 --- /dev/null +++ b/lib/sevices/passo/building/property_info_services.dart @@ -0,0 +1,74 @@ +import 'dart:convert'; +import 'dart:developer'; +import 'dart:io'; + +import 'package:unit2/model/passo/bldg_loc.dart'; +import 'package:unit2/model/passo/land_ref.dart'; +import 'package:unit2/model/passo/property_info.dart'; +import 'package:http/http.dart' as http; + +class PropertyInfoService { + static final PropertyInfoService _instance = PropertyInfoService(); + static PropertyInfoService get instance => _instance; + + Future> getpropertyInfo() async { + http.Response response = await http.get(Uri.parse( + 'http://192.168.10.218:8000/api/rptass_app/bldgappr_details/')); + print(response.body); + if (response.statusCode == 200) { + final List result = jsonDecode(response.body)['data']; + + return result.map(((e) => PropertyInfo.fromJson(e))).toList(); + } else { + throw Exception(response.reasonPhrase); + } + } + + Future postPropertyInfo(PropertyInfo faas) async { + http.Response? response; + try { + response = await http.post( + Uri.parse( + "http://192.168.10.218:8000/api/rptass_app/bldgappr_details/"), + headers: { + HttpHeaders.contentTypeHeader: "application/json", + }, + body: jsonEncode(faas.toJson())); + } catch (e) { + log(e.toString()); + } + return response; + } + + Future bldgLocPutInfo(BldgLoc data, id) async { + http.Response? response; + try { + response = await http.put(Uri.parse( + // ignore: unnecessary_brace_in_string_interps + "http://192.168.10.218:8000/api/rptass_app/bldgappr_location/?bldgappr_details_id=${id}"), + headers: { + HttpHeaders.contentTypeHeader: "application/json", + }, + body: jsonEncode(data.toJson())); + } catch (e) { + log(e.toString()); + } + return response; + } + + Future landRefPutInfo(LandRef data, id) async { + http.Response? response; + try { + response = await http.put(Uri.parse( + // ignore: unnecessary_brace_in_string_interps + "http://192.168.10.218:8000/api/rptass_app/bldgappr_landref/?bldgappr_details_id=${id}"), + headers: { + HttpHeaders.contentTypeHeader: "application/json", + }, + body: jsonEncode(data.toJson())); + } catch (e) { + log(e.toString()); + } + return response; + } +} diff --git a/lib/sevices/passo/class_components_services.dart b/lib/sevices/passo/class_components_services.dart new file mode 100644 index 0000000..2d3e786 --- /dev/null +++ b/lib/sevices/passo/class_components_services.dart @@ -0,0 +1,21 @@ +import 'dart:convert'; + +import 'package:unit2/model/passo/class_components.dart'; +import 'package:http/http.dart' as http; + +class ClassComponentService { + static final ClassComponentService _instance = ClassComponentService(); + static ClassComponentService get instance => _instance; + + Future> getClassComponent() async { + http.Response response = await http.get(Uri.parse( + 'http://192.168.10.218:8000/api/rptass_app/class_components/')); + if (response.statusCode == 200) { + final List result = jsonDecode(response.body)['data']; + + return result.map(((e) => ClassComponents.fromJson(e))).toList(); + } else { + throw Exception(response.reasonPhrase); + } + } +} diff --git a/lib/sevices/passo/signatories_service.dart b/lib/sevices/passo/signatories_service.dart new file mode 100644 index 0000000..76eeba7 --- /dev/null +++ b/lib/sevices/passo/signatories_service.dart @@ -0,0 +1,24 @@ +import 'dart:convert'; +import 'package:http/http.dart' as http; + +import 'package:unit2/model/passo/signatories.dart'; + +class SignatoriesServices { + static final SignatoriesServices _instance = SignatoriesServices(); + static SignatoriesServices get instance => _instance; + + Future> getSignatories() async { + http.Response response = await http.get( + Uri.parse('http://192.168.10.218:8000/api/rptass_app/signatories/')); + print('Signatories'); + print(response.statusCode); + if (response.statusCode == 200) { + final List result = jsonDecode(response.body)['data']; + + return result.map(((e) => Signatories.fromJson(e))).toList(); + } else { + print(response.reasonPhrase); + throw Exception(response.reasonPhrase); + } + } +} diff --git a/lib/sevices/passo/unit_construct_services.dart b/lib/sevices/passo/unit_construct_services.dart new file mode 100644 index 0000000..7ef90d5 --- /dev/null +++ b/lib/sevices/passo/unit_construct_services.dart @@ -0,0 +1,21 @@ +import 'dart:convert'; + +import 'package:unit2/model/passo/unit_construct.dart'; +import 'package:http/http.dart' as http; + +class UnitConstructService { + static final UnitConstructService _instance = UnitConstructService(); + static UnitConstructService get instance => _instance; + + Future> getUnitConstruct() async { + http.Response response = await http.get(Uri.parse( + 'http://192.168.10.218:8000/api/rptass_app/unitconstruct_values/')); + if (response.statusCode == 200) { + final List result = jsonDecode(response.body)['data']; + + return result.map(((e) => UnitConstruct.fromJson(e))).toList(); + } else { + throw Exception(response.reasonPhrase); + } + } +} diff --git a/lib/utils/app_router.dart b/lib/utils/app_router.dart index 3bf9c9b..9ea6064 100644 --- a/lib/utils/app_router.dart +++ b/lib/utils/app_router.dart @@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/role/pass_check/pass_check_bloc.dart'; import 'package:unit2/bloc/sos/sos_bloc.dart'; +import 'package:unit2/screens/passo/passo_home.dart'; import 'package:unit2/screens/sos/index.dart'; import 'package:unit2/screens/unit2/homepage.dart/components/dashboard.dart'; import 'package:unit2/screens/unit2/homepage.dart/components/menu.dart'; @@ -55,13 +56,24 @@ class AppRouter { ); }); case '/pass-check': - PassCheckArguments arguments = routeSettings.arguments as PassCheckArguments; + PassCheckArguments arguments = + routeSettings.arguments as PassCheckArguments; return MaterialPageRoute(builder: (BuildContext context) { - return BlocProvider( - create: (context) => PassCheckBloc()..add(GetPassCheckAreas(roleId: arguments.roleId, userId: arguments.userId)), - child: QRCodeScannerSettings(roleId: arguments.roleId, userId: arguments.userId,), + return BlocProvider( + create: (context) => PassCheckBloc() + ..add(GetPassCheckAreas( + roleId: arguments.roleId, userId: arguments.userId)), + child: QRCodeScannerSettings( + roleId: arguments.roleId, + userId: arguments.userId, + ), ); }); + case '/passo-home': + // BlocProvider.of( NavigationService.navigatorKey.currentContext!).add(LoadLoggedInUser()); + return MaterialPageRoute(builder: (_) { + return const PassoHome(); + }); default: return MaterialPageRoute(builder: (context) { return Container(); diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index 5dea2f3..7c94dd6 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -4,11 +4,11 @@ class Url { static Url get instance => _instance; String host() { - // return '192.168.10.183:3000'; - return 'agusandelnorte.gov.ph'; + // return '192.168.10.183:3000'; + // return 'agusandelnorte.gov.ph'; // return "192.168.10.219:3000"; // return "192.168.10.241"; - // return "192.168.10.221:3004"; + return "192.168.10.221:3004"; // return "playweb.agusandelnorte.gov.ph"; // return 'devapi.agusandelnorte.gov.ph:3004'; } @@ -17,211 +17,231 @@ class Url { return '/api/account/auth/login/'; } - String profileInformation(){ + String profileInformation() { return 'api/jobnet_app/profile/pds/'; } - String latestApk(){ + String latestApk() { return "/api/system_app/apk_version/latest"; } ////SOS paths - String sosRequest(){ + String sosRequest() { return "/api/sos_app/sos_request/"; } //// DOCSMS paths - String getDocument(){ + String getDocument() { return "/api/web_app/public/document_viewer/"; } ////ELIGIBILITIES PATHS -String eligibilities(){ - return "/api/jobnet_app/eligibilities/"; -} + String eligibilities() { + return "/api/jobnet_app/eligibilities/"; + } -String getEligibilities(){ - return "/api/jobnet_app/profile/pds/eligibility/"; -} + String getEligibilities() { + return "/api/jobnet_app/profile/pds/eligibility/"; + } -String addEligibility(){ - return "/api/jobnet_app/profile/pds/eligibility/"; -} -String deleteEligibility(){ - return "/api/jobnet_app/profile/pds/eligibility/"; -} + String addEligibility() { + return "/api/jobnet_app/profile/pds/eligibility/"; + } + + String deleteEligibility() { + return "/api/jobnet_app/profile/pds/eligibility/"; + } + + String updateEligibility() { + return "/api/jobnet_app/profile/pds/eligibility/"; + } -String updateEligibility(){ - return "/api/jobnet_app/profile/pds/eligibility/"; -} //// work history paths -String workhistory(){ - return "/api/jobnet_app/profile/pds/work/"; -} -String getPositions(){ - return "/api/jobnet_app/positions/"; -} -String getAgencies(){ + String workhistory() { + return "/api/jobnet_app/profile/pds/work/"; + } + + String getPositions() { + return "/api/jobnet_app/positions/"; + } + + String getAgencies() { return "/api/jobnet_app/agencies/"; -} + } -String getAgencyCategory(){ - return "api/jobnet_app/agency_categories/"; -} - -String identifications(){ - return "/api/jobnet_app/profile/pds/basic/identification/"; -} + String getAgencyCategory() { + return "api/jobnet_app/agency_categories/"; + } + String identifications() { + return "/api/jobnet_app/profile/pds/basic/identification/"; + } ////educational background paths -String educationalBackground(){ - return "/api/jobnet_app/profile/pds/education/"; -} -String getSchools(){ - return "/api/jobnet_app/schools/"; -} -String getPrograms(){ - return "api/jobnet_app/education_programs/"; -} -String getHonors(){ - return "/api/jobnet_app/honors"; -} + String educationalBackground() { + return "/api/jobnet_app/profile/pds/education/"; + } + + String getSchools() { + return "/api/jobnet_app/schools/"; + } + + String getPrograms() { + return "api/jobnet_app/education_programs/"; + } + + String getHonors() { + return "/api/jobnet_app/honors"; + } //// learning and development paths -String learningAndDevelopments(){ - return "api/jobnet_app/profile/pds/learning_development/"; -} -String conductedTrainings(){ - return "api/jobnet_app/conducted_trainings/"; -} -String learningAndDevelopmentType(){ - return "api/jobnet_app/learning_development/"; -} -String learningAndDevelopmentTopics(){ - return "api/jobnet_app/training_topics/"; -} + String learningAndDevelopments() { + return "api/jobnet_app/profile/pds/learning_development/"; + } + + String conductedTrainings() { + return "api/jobnet_app/conducted_trainings/"; + } + + String learningAndDevelopmentType() { + return "api/jobnet_app/learning_development/"; + } + + String learningAndDevelopmentTopics() { + return "api/jobnet_app/training_topics/"; + } //// references paths -String reference(){ - return "/api/jobnet_app/profile/pds/personal_reference/"; -} - + String reference() { + return "/api/jobnet_app/profile/pds/personal_reference/"; + } ////voluntary works -String getVoluntaryWorks(){ - return "/api/jobnet_app/profile/pds/voluntary_work/"; -} + String getVoluntaryWorks() { + return "/api/jobnet_app/profile/pds/voluntary_work/"; + } //// skills hobbies -String skillsHobbies(){ - return "/api/jobnet_app/profile/pds/other/skill_hobby/"; -} -String getAllSkillsHobbies(){ - return "/api/jobnet_app/skill_hobby/"; -} + String skillsHobbies() { + return "/api/jobnet_app/profile/pds/other/skill_hobby/"; + } + + String getAllSkillsHobbies() { + return "/api/jobnet_app/skill_hobby/"; + } + //// orgmemberships -String getOrgMemberShips(){ - return "/api/jobnet_app/profile/pds/other/org_membership/"; -} + String getOrgMemberShips() { + return "/api/jobnet_app/profile/pds/other/org_membership/"; + } ////non academic recognition -String getNonAcademicRecognition(){ - return "/api/jobnet_app/profile/pds/other/non_acad_recognition/"; -} + String getNonAcademicRecognition() { + return "/api/jobnet_app/profile/pds/other/non_acad_recognition/"; + } ////citizenship -String citizenship(){ - return "/api/jobnet_app/profile/pds/basic/citizenship/"; -} + String citizenship() { + return "/api/jobnet_app/profile/pds/basic/citizenship/"; + } ////family paths -String getFamilies(){ - return "/api/jobnet_app/profile/pds/family/"; -} -String addEmergency(){ - return "/api/profile_app/person_emergency/"; -} -String getRelationshipTypes(){ - return "/api/jobnet_app/relationship_types"; -} -String updatePersonalInfor(){ - return "/api/jobnet_app/profile/pds/basic/personal/"; -} + String getFamilies() { + return "/api/jobnet_app/profile/pds/family/"; + } + String addEmergency() { + return "/api/profile_app/person_emergency/"; + } + + String getRelationshipTypes() { + return "/api/jobnet_app/relationship_types"; + } + + String updatePersonalInfor() { + return "/api/jobnet_app/profile/pds/basic/personal/"; + } //// contacts path -String getServiceTypes(){ - return "/api/jobnet_app/comm_service_type/"; + String getServiceTypes() { + return "/api/jobnet_app/comm_service_type/"; + } -} //// address path -String addressPath(){ - return "/api/jobnet_app/profile/pds/basic/address/"; -} + String addressPath() { + return "/api/jobnet_app/profile/pds/basic/address/"; + } + + String contactPath() { + return "/api/jobnet_app/profile/pds/basic/contact/"; + } + + String getCommunicationProvider() { + return "/api/jobnet_app/comm_services/"; + } + + String deleteContact() { + return "/api/jobnet_app/profile/pds/basic/contact/"; + } -String contactPath(){ - return "/api/jobnet_app/profile/pds/basic/contact/"; -} -String getCommunicationProvider(){ - return "/api/jobnet_app/comm_services/"; -} -String deleteContact (){ - return "/api/jobnet_app/profile/pds/basic/contact/"; -} ////profile other info -String getReligions(){ - return "/api/profile_app/religion/"; -} -String getEthnicity(){ - return "/api/profile_app/ethnicity/"; -} + String getReligions() { + return "/api/profile_app/religion/"; + } -String getDisability(){ - return "api/profile_app/disability/"; -} + String getEthnicity() { + return "/api/profile_app/ethnicity/"; + } -String getIndigency(){ - return "/api/profile_app/indigenous/"; -} + String getDisability() { + return "api/profile_app/disability/"; + } -String getGenders(){ - return "/api/profile_app/gender/"; -} + String getIndigency() { + return "/api/profile_app/indigenous/"; + } + + String getGenders() { + return "/api/profile_app/gender/"; + } /////ROLES // pass check -String getAssignAreas(){ - return "/api/account/auth/assigned_role_area/"; -} + String getAssignAreas() { + return "/api/account/auth/assigned_role_area/"; + } -String getPasserInfo(){ - return "/api/profile_app/person_basicinfo/"; -} - -String postLogs(){ - return "/api/unit2_app/monitoring/pass_check/"; -} + String getPasserInfo() { + return "/api/profile_app/person_basicinfo/"; + } + String postLogs() { + return "/api/unit2_app/monitoring/pass_check/"; + } //// location utils path - String getCounties(){ + String getCounties() { return "/api/jobnet_app/countries/"; } - String getRegions(){ + + String getRegions() { return "/api/web_app/location/region/"; } - String getProvinces(){ + + String getProvinces() { return "api/web_app/location/province/"; } - String getCities(){ + + String getCities() { return "/api/web_app/location/citymun/"; } - String getBarangays(){ + + String getBarangays() { return "/api/web_app/location/barangay/"; } - String getAddressCategory(){ + + String getAddressCategory() { return "/api/jobnet_app/address_categories/"; } -} \ No newline at end of file +} diff --git a/lib/widgets/passo/custom_button.dart b/lib/widgets/passo/custom_button.dart new file mode 100644 index 0000000..4823030 --- /dev/null +++ b/lib/widgets/passo/custom_button.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +class CustomButton extends StatelessWidget { + final VoidCallback onPressed; + final Icon icon; + + CustomButton({required this.onPressed, required this.icon}); + + @override + Widget build(BuildContext context) { + return ElevatedButton( + onPressed: onPressed, + style: ElevatedButton.styleFrom( + shape: const CircleBorder(), + padding: const EdgeInsets.all(30), + backgroundColor: Colors.red, + foregroundColor: Colors.white, + ), + child: icon, + ); + } +} diff --git a/lib/widgets/passo/custom_formBuilder_fields.dart b/lib/widgets/passo/custom_formBuilder_fields.dart new file mode 100644 index 0000000..3dc7a25 --- /dev/null +++ b/lib/widgets/passo/custom_formBuilder_fields.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:unit2/theme-data.dart/form-style.dart'; + +Widget customTextField(String labelText, String hintText, String keyText) { + return Container( + margin: const EdgeInsets.only(left: 0, top: 10, right: 0, bottom: 0), + child: FormBuilderTextField( + name: keyText, + decoration: normalTextFieldStyle(labelText, hintText), + validator: FormBuilderValidators.compose([]), + ), + ); +} + +Widget customDropDownField(String labelText, String hintText, String keyText, + List dropdownItems) { + return Container( + margin: const EdgeInsets.only(left: 0, top: 10, right: 0, bottom: 0), + child: FormBuilderDropdown( + name: keyText, + autofocus: false, + decoration: normalTextFieldStyle(labelText, hintText), + items: dropdownItems + .map((items) => DropdownMenuItem( + value: items, + child: Text(items), + )) + .toList()), + ); +} + +Widget customDatTimePicker(String labelText, String hintText, String keyText) { + return Container( + margin: const EdgeInsets.only(left: 0, top: 10, right: 0, bottom: 0), + child: FormBuilderDateTimePicker( + name: keyText, + initialEntryMode: DatePickerEntryMode.calendarOnly, + initialValue: DateTime.now(), + inputType: InputType.date, + decoration: normalTextFieldStyle(labelText, hintText), + initialTime: const TimeOfDay(hour: 8, minute: 0), + // locale: const Locale.fromSubtags(languageCode: 'fr'), + ), + ); +} diff --git a/pubspec.lock b/pubspec.lock index 43d40ba..952f940 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -555,6 +555,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.2" + flutter_speed_dial: + dependency: "direct main" + description: + name: flutter_speed_dial + sha256: "41d7ad0bc224248637b3a5e0b9083e912a75445bdb450cf82b8ed06d7af7c61d" + url: "https://pub.dev" + source: hosted + version: "6.2.0" flutter_spinkit: dependency: "direct main" description: @@ -701,6 +709,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + im_stepper: + dependency: "direct main" + description: + name: im_stepper + sha256: "84ca411f7c4666cb8762a6dd6eec0353c96c67f674124614263875d0570ca634" + url: "https://pub.dev" + source: hosted + version: "1.0.1+1" image: dependency: transitive description: @@ -845,6 +861,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.9" + multi_select_flutter: + dependency: "direct main" + description: + name: multi_select_flutter + sha256: "503857b415d390d29159df8a9d92d83c6aac17aaf1c307fb7bcfc77d097d20ed" + url: "https://pub.dev" + source: hosted + version: "4.1.3" + multiselect: + dependency: "direct main" + description: + name: multiselect + sha256: "8d0c4a7b89bee6c5e5e4a25ecba68c796dafc1917492d5606f0caa3947d5905a" + url: "https://pub.dev" + source: hosted + version: "0.1.0" + navigation_builder: + dependency: transitive + description: + name: navigation_builder + sha256: "95e25150191d9cd4e4b86504f33cd9e786d1e6732edb2e3e635bbedc5ef0dea7" + url: "https://pub.dev" + source: hosted + version: "0.0.3" nested: dependency: transitive description: @@ -1198,7 +1238,7 @@ packages: source: hosted version: "0.7.8" shared_preferences: - dependency: transitive + dependency: "direct main" description: name: shared_preferences sha256: "396f85b8afc6865182610c0a2fc470853d56499f75f7499e2a73a9f0539d23d0" @@ -1338,6 +1378,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.11.0" + states_rebuilder: + dependency: transitive + description: + name: states_rebuilder + sha256: bf1a5ab5c543acdefce35e60f482eb7ab592339484fe3266d147ee597f18dc92 + url: "https://pub.dev" + source: hosted + version: "6.3.0" stream_channel: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 8bdae44..7ff1dce 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -82,7 +82,11 @@ dependencies: searchable_paginated_dropdown: ^1.2.0 audioplayers: ^4.1.0 assets_audio_player: ^3.0.6 - + flutter_speed_dial: ^6.2.0 + im_stepper: ^1.0.1+1 + shared_preferences: ^2.0.20 + multiselect: ^0.1.0 + multi_select_flutter: ^4.1.3 dev_dependencies: flutter_test: From c32ed5119860089b664eb01187c53675e8fc1eec Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Tue, 1 Aug 2023 16:20:38 +0800 Subject: [PATCH 75/86] Implemented attachment features to profile screens --- assets/svgs/jpg.svg | 33 + assets/svgs/pdf.svg | 32 + assets/svgs/png.svg | 28 + ios/Podfile.lock | 50 ++ .../profile/education/education_bloc.dart | 34 +- .../profile/education/education_event.dart | 11 + .../profile/education/education_state.dart | 3 +- .../profile/eligibility/eligibility_bloc.dart | 62 +- .../eligibility/eligibility_event.dart | 27 + .../eligibility/eligibility_state.dart | 50 +- .../learning_development_bloc.dart | 32 +- .../learning_development_event.dart | 11 + .../learning_development_state.dart | 3 +- .../voluntary_works/voluntary_work_event.dart | 10 + .../profile/workHistory/workHistory_bloc.dart | 31 +- .../workHistory/workHistory_event.dart | 10 + .../workHistory/workHistory_state.dart | 3 +- lib/model/profile/attachment.dart | 122 ++++ lib/model/profile/educational_background.dart | 6 +- lib/model/profile/eligibility.dart | 6 +- lib/model/profile/learning_development.dart | 5 +- lib/model/profile/work_history.dart | 7 +- .../profile/components/education_screen.dart | 581 ++++++++++++++---- .../components/eligibility_screen.dart | 462 +++++++++++--- .../learning_and_development_screen.dart | 546 ++++++++++++---- .../components/work_history_screen.dart | 534 ++++++++++++---- .../profile/shared/multiple_attachment.dart | 150 +++++ .../profile/shared/single_attachment.dart | 49 ++ lib/sevices/profile/eligibility_services.dart | 136 ++-- lib/utils/attachment_categories.dart | 77 +++ lib/utils/urls.dart | 6 + pubspec.lock | 8 + pubspec.yaml | 1 + 33 files changed, 2586 insertions(+), 540 deletions(-) create mode 100644 assets/svgs/jpg.svg create mode 100644 assets/svgs/pdf.svg create mode 100644 assets/svgs/png.svg create mode 100644 lib/model/profile/attachment.dart create mode 100644 lib/screens/profile/shared/multiple_attachment.dart create mode 100644 lib/screens/profile/shared/single_attachment.dart create mode 100644 lib/utils/attachment_categories.dart diff --git a/assets/svgs/jpg.svg b/assets/svgs/jpg.svg new file mode 100644 index 0000000..f6e0591 --- /dev/null +++ b/assets/svgs/jpg.svg @@ -0,0 +1,33 @@ + + + + + + + + + diff --git a/assets/svgs/pdf.svg b/assets/svgs/pdf.svg new file mode 100644 index 0000000..5226e09 --- /dev/null +++ b/assets/svgs/pdf.svg @@ -0,0 +1,32 @@ + + + + + + + + + + diff --git a/assets/svgs/png.svg b/assets/svgs/png.svg new file mode 100644 index 0000000..77671a4 --- /dev/null +++ b/assets/svgs/png.svg @@ -0,0 +1,28 @@ + + + + + + + + + diff --git a/ios/Podfile.lock b/ios/Podfile.lock index cdf552e..0bcedcc 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -11,8 +11,42 @@ PODS: - SwiftProtobuf - device_info (0.0.1): - Flutter + - DKImagePickerController/Core (4.3.4): + - DKImagePickerController/ImageDataManager + - DKImagePickerController/Resource + - DKImagePickerController/ImageDataManager (4.3.4) + - DKImagePickerController/PhotoGallery (4.3.4): + - DKImagePickerController/Core + - DKPhotoGallery + - DKImagePickerController/Resource (4.3.4) + - DKPhotoGallery (0.0.17): + - DKPhotoGallery/Core (= 0.0.17) + - DKPhotoGallery/Model (= 0.0.17) + - DKPhotoGallery/Preview (= 0.0.17) + - DKPhotoGallery/Resource (= 0.0.17) + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Core (0.0.17): + - DKPhotoGallery/Model + - DKPhotoGallery/Preview + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Model (0.0.17): + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Preview (0.0.17): + - DKPhotoGallery/Model + - DKPhotoGallery/Resource + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Resource (0.0.17): + - SDWebImage + - SwiftyGif - easy_app_installer (0.0.1): - Flutter + - file_picker (0.0.1): + - DKImagePickerController/PhotoGallery + - Flutter - Flutter (1.0.0) - fluttertoast (0.0.2): - Flutter @@ -36,6 +70,9 @@ PODS: - Flutter - rive_common (0.0.1): - Flutter + - SDWebImage (5.17.0): + - SDWebImage/Core (= 5.17.0) + - SDWebImage/Core (5.17.0) - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS @@ -43,6 +80,7 @@ PODS: - Flutter - FMDB (>= 2.7.5) - SwiftProtobuf (1.20.3) + - SwiftyGif (5.4.4) - Toast (4.0.0) DEPENDENCIES: @@ -52,6 +90,7 @@ DEPENDENCIES: - barcode_scan2 (from `.symlinks/plugins/barcode_scan2/ios`) - device_info (from `.symlinks/plugins/device_info/ios`) - easy_app_installer (from `.symlinks/plugins/easy_app_installer/ios`) + - file_picker (from `.symlinks/plugins/file_picker/ios`) - Flutter (from `Flutter`) - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) - location (from `.symlinks/plugins/location/ios`) @@ -66,9 +105,13 @@ DEPENDENCIES: SPEC REPOS: trunk: + - DKImagePickerController + - DKPhotoGallery - FMDB - MTBBarcodeScanner + - SDWebImage - SwiftProtobuf + - SwiftyGif - Toast EXTERNAL SOURCES: @@ -84,6 +127,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/device_info/ios" easy_app_installer: :path: ".symlinks/plugins/easy_app_installer/ios" + file_picker: + :path: ".symlinks/plugins/file_picker/ios" Flutter: :path: Flutter fluttertoast: @@ -113,7 +158,10 @@ SPEC CHECKSUMS: audioplayers_darwin: 877d9a4d06331c5c374595e46e16453ac7eafa40 barcode_scan2: 0af2bb63c81b4565aab6cd78278e4c0fa136dbb0 device_info: d7d233b645a32c40dfdc212de5cf646ca482f175 + DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac + DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 easy_app_installer: 29abe397da7d86721fee853281202f414373f45c + file_picker: ce3938a0df3cc1ef404671531facef740d03f920 Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 fluttertoast: fafc4fa4d01a6a9e4f772ecd190ffa525e9e2d9c FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a @@ -125,9 +173,11 @@ SPEC CHECKSUMS: permission_handler_apple: 8f116445eff3c0e7c65ad60f5fef5490aa94b4e4 platform_device_id: 81b3e2993881f87d0c82ef151dc274df4869aef5 rive_common: 60ae7896ab40f9513974f36f015de33f70d2c5c5 + SDWebImage: 750adf017a315a280c60fde706ab1e552a3ae4e9 shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a SwiftProtobuf: b02b5075dcf60c9f5f403000b3b0c202a11b6ae1 + SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 diff --git a/lib/bloc/profile/education/education_bloc.dart b/lib/bloc/profile/education/education_bloc.dart index e965101..cb43d54 100644 --- a/lib/bloc/profile/education/education_bloc.dart +++ b/lib/bloc/profile/education/education_bloc.dart @@ -3,6 +3,9 @@ import 'package:equatable/equatable.dart'; import 'package:unit2/model/profile/educational_background.dart'; import 'package:unit2/sevices/profile/education_services.dart'; +import '../../../model/profile/attachment.dart'; +import '../../../utils/attachment_categories.dart'; + part 'education_event.dart'; part 'education_state.dart'; @@ -11,20 +14,25 @@ class EducationBloc extends Bloc { List schools = []; List programs = []; List honors = []; + List attachmentCategories = []; EducationBloc() : super(EducationInitial()) { on((event, emit) async { emit(EducationalBackgroundLoadingState()); try { + if (attachmentCategories.isEmpty) { + attachmentCategories = + await AttachmentServices.instance.getCategories(); + } if (educationalBackgrounds.isEmpty) { List educations = await EducationService .instace .getEducationalBackground(event.profileId, event.token); educationalBackgrounds = educations; emit(EducationalBackgroundLoadedState( - educationalBackground: educationalBackgrounds)); + educationalBackground: educationalBackgrounds, attachmentCategory: attachmentCategories)); } else { emit(EducationalBackgroundLoadedState( - educationalBackground: educationalBackgrounds)); + educationalBackground: educationalBackgrounds,attachmentCategory: attachmentCategories)); } } catch (e) { emit(EducationalBackgroundErrorState(message: e.toString())); @@ -89,7 +97,7 @@ class EducationBloc extends Bloc { ////LOAD on((event, emit) { emit(EducationalBackgroundLoadedState( - educationalBackground: educationalBackgrounds)); + educationalBackground: educationalBackgrounds,attachmentCategory: attachmentCategories)); }); //// SHOW EDIT FORM on((event, emit) async { @@ -134,5 +142,25 @@ class EducationBloc extends Bloc { emit(EducationalBackgroundErrorState(message: e.toString())); } }); + ////Add attachment + on((event, emit) async { + emit(EducationalBackgroundLoadingState()); + try { + Map status = await AttachmentServices.instance + .attachment( + categoryId: event.categoryId, + module: event.attachmentModule, + paths: event.filePaths, + token: event.token, + profileId: event.profileId); + if (status['success']) { + emit(EducationAddedState(response: status)); + } else { + emit(EducationAddedState(response: status)); + } + } catch (e) { + emit(EducationalBackgroundErrorState(message: e.toString())); + } + }); } } diff --git a/lib/bloc/profile/education/education_event.dart b/lib/bloc/profile/education/education_event.dart index 59a431b..667befb 100644 --- a/lib/bloc/profile/education/education_event.dart +++ b/lib/bloc/profile/education/education_event.dart @@ -63,3 +63,14 @@ class DeleteEducation extends EducationEvent{ @override List get props => [educationalBackground, profileId, token]; } +////Add attachment +class AddAttachment extends EducationEvent{ + final String categoryId; + final String attachmentModule; + final List filePaths; + final String token; + final String profileId; + const AddAttachment({required this.attachmentModule, required this.filePaths, required this.categoryId, required this.profileId, required this.token}); + @override + List get props => [categoryId,attachmentModule,filePaths, token,profileId]; +} diff --git a/lib/bloc/profile/education/education_state.dart b/lib/bloc/profile/education/education_state.dart index 2a0f1ec..a8bebe0 100644 --- a/lib/bloc/profile/education/education_state.dart +++ b/lib/bloc/profile/education/education_state.dart @@ -10,8 +10,9 @@ abstract class EducationState extends Equatable { class EducationInitial extends EducationState {} class EducationalBackgroundLoadedState extends EducationState { + final List< AttachmentCategory> attachmentCategory; final List educationalBackground; - const EducationalBackgroundLoadedState({required this.educationalBackground}); + const EducationalBackgroundLoadedState({required this.educationalBackground, required this.attachmentCategory}); @override List get props => [educationalBackground]; } diff --git a/lib/bloc/profile/eligibility/eligibility_bloc.dart b/lib/bloc/profile/eligibility/eligibility_bloc.dart index e405355..c47b309 100644 --- a/lib/bloc/profile/eligibility/eligibility_bloc.dart +++ b/lib/bloc/profile/eligibility/eligibility_bloc.dart @@ -1,9 +1,11 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:unit2/utils/attachment_categories.dart'; import '../../../model/location/city.dart'; import '../../../model/location/country.dart'; import '../../../model/location/provinces.dart'; import '../../../model/location/region.dart'; +import '../../../model/profile/attachment.dart'; import '../../../model/profile/eligibility.dart'; import '../../../model/utils/eligibility.dart'; import '../../../sevices/profile/eligibility_services.dart'; @@ -18,10 +20,13 @@ class EligibilityBloc extends Bloc { List globalRegions = []; List globalEligibilities = []; List eligibilities = []; + List attachmentCategories = []; //// LOAD ELIGIBILTY on((event, emit) { emit(EligibilityLoadingState()); - emit(EligibilityLoaded(eligibilities: eligibilities)); + emit(EligibilityLoaded( + eligibilities: eligibilities, + attachmentCategory: attachmentCategories)); }); //// DELETE @@ -48,13 +53,21 @@ class EligibilityBloc extends Bloc { //// GET ELIGIBILITY on((event, emit) async { try { + if (attachmentCategories.isEmpty) { + attachmentCategories = + await AttachmentServices.instance.getCategories(); + } if (eligibilities.isNotEmpty) { - emit(EligibilityLoaded(eligibilities: eligibilities)); + emit(EligibilityLoaded( + eligibilities: eligibilities, + attachmentCategory: attachmentCategories)); } else { emit(EligibilityLoadingState()); eligibilities = await EligibilityService.instance .getEligibilities(event.profileId, event.token); - emit(EligibilityLoaded(eligibilities: eligibilities)); + emit(EligibilityLoaded( + eligibilities: eligibilities, + attachmentCategory: attachmentCategories)); } } catch (e) { emit(EligibilityErrorState(message: e.toString())); @@ -208,5 +221,48 @@ class EligibilityBloc extends Bloc { emit(const EligibilityErrorState( message: "Something went wrong. Please try again")); }); + ////Add attachment + on((event, emit) async { + emit(EligibilityLoadingState()); + try { + Map status = await AttachmentServices.instance + .attachment( + categoryId: event.categoryId, + module: event.attachmentModule, + paths: event.filePaths, + token: event.token, + profileId: event.profileId); + if (status['success']) { + emit(EligibilityAddedState(response: status)); + } else { + emit(EligibilityAddedState(response: status)); + } + } catch (e) { + emit(EligibilityErrorState(message: e.toString())); + } + }); + + on((event, emit) async { + // try { + final bool success = await EligibilityService.instance.deleteAttachment( + moduleId: int.parse(event.moduleId), + attachment: event.attachment, + profileId: event.profileId, + token: event.token); + if (success) { + EligibityCert eligibityCert = eligibilities.firstWhere((element) =>element.id.toString() == event.moduleId); + eligibityCert.attachments!.removeWhere((element){ + return event.attachment.id == element.id; + }); + emit(DeletedState( + success: success, + )); + } else { + emit(DeletedState(success: success)); + } + // } catch (e) { + // emit(EligibilityErrorState(message: e.toString())); + // } + }); } } diff --git a/lib/bloc/profile/eligibility/eligibility_event.dart b/lib/bloc/profile/eligibility/eligibility_event.dart index cd57562..8d17236 100644 --- a/lib/bloc/profile/eligibility/eligibility_event.dart +++ b/lib/bloc/profile/eligibility/eligibility_event.dart @@ -55,6 +55,7 @@ class DeleteEligibility extends EligibilityEvent { final String profileId; final int eligibilityId; final String token; + const DeleteEligibility( { required this.eligibilityId, @@ -68,4 +69,30 @@ class CallErrorState extends EligibilityEvent{ } +class AddAttachment extends EligibilityEvent{ + final String categoryId; + final String attachmentModule; + final List filePaths; + final String token; + final String profileId; + const AddAttachment({required this.attachmentModule, required this.filePaths, required this.categoryId, required this.profileId, required this.token}); + @override + List get props => [categoryId,attachmentModule,filePaths, token,profileId]; +} + +class DeleteAttachment extends EligibilityEvent{ + final String profileId; + final String token; + final Attachment attachment; + final String moduleId; + const DeleteAttachment({required this.attachment,required this.moduleId, required this.profileId, required this.token}); +} + + + + + + + + diff --git a/lib/bloc/profile/eligibility/eligibility_state.dart b/lib/bloc/profile/eligibility/eligibility_state.dart index 3b57f6d..3a1994a 100644 --- a/lib/bloc/profile/eligibility/eligibility_state.dart +++ b/lib/bloc/profile/eligibility/eligibility_state.dart @@ -2,14 +2,13 @@ part of 'eligibility_bloc.dart'; abstract class EligibilityState extends Equatable { const EligibilityState(); - + @override List get props => []; } class EligibilityInitial extends EligibilityState {} - class EditEligibilityState extends EligibilityState { final EligibityCert eligibityCert; final List eligibilities; @@ -23,13 +22,14 @@ class EditEligibilityState extends EligibilityState { final Province? currentProvince; final CityMunicipality? currentCity; final Country selectedCountry; - const EditEligibilityState({ - required this.provinces, - required this.cities, - required this.currentProvince, - required this.currentCity, - required this.currentRegion, - required this.currentEligibility, + + const EditEligibilityState({ + required this.provinces, + required this.cities, + required this.currentProvince, + required this.currentCity, + required this.currentRegion, + required this.currentEligibility, required this.isOverseas, required this.eligibityCert, required this.eligibilities, @@ -53,41 +53,43 @@ class AddEligibilityState extends EligibilityState { final List eligibilities; final List countries; final List regions; - const AddEligibilityState({ + const AddEligibilityState({ required this.eligibilities, required this.countries, required this.regions, }); @override - List get props => [eligibilities,countries,regions]; + List get props => [eligibilities, countries, regions]; } -class EligibilityEditedState extends EligibilityState{ - final Map response; + +class EligibilityEditedState extends EligibilityState { + final Map response; const EligibilityEditedState({required this.response}); @override - List get props =>[ response]; + List get props => [response]; } -class EligibilityAddedState extends EligibilityState{ - final Map response; +class EligibilityAddedState extends EligibilityState { + final Map response; - const EligibilityAddedState({ required this.response}); + const EligibilityAddedState({required this.response}); @override - List get props =>[response]; + List get props => [response]; } -class EligibilityLoadingState extends EligibilityState{ -} -class EligibilityErrorState extends EligibilityState{ +class EligibilityLoadingState extends EligibilityState {} + +class EligibilityErrorState extends EligibilityState { final String message; const EligibilityErrorState({required this.message}); - @override - List get props =>[message]; + @override + List get props => [message]; } class EligibilityLoaded extends EligibilityState { + final List< AttachmentCategory> attachmentCategory; final List eligibilities; - const EligibilityLoaded({required this.eligibilities}); + const EligibilityLoaded({required this.eligibilities, required this.attachmentCategory}); @override List get props => [eligibilities]; } diff --git a/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart b/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart index 6ef27c0..e7c145d 100644 --- a/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart +++ b/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart @@ -6,9 +6,11 @@ import 'package:unit2/sevices/profile/learningDevelopment_service.dart'; import '../../../model/location/barangay.dart'; import '../../../model/location/city.dart'; import '../../../model/location/provinces.dart'; +import '../../../model/profile/attachment.dart'; import '../../../model/profile/learning_development.dart'; import '../../../model/utils/agency.dart'; import '../../../model/utils/category.dart'; +import '../../../utils/attachment_categories.dart'; import '../../../utils/location_utilities.dart'; import '../../../utils/profile_utilities.dart'; part 'learning_development_event.dart'; @@ -27,7 +29,7 @@ class LearningDevelopmentBloc List globalBarangay = []; List agencies = []; List agencyCategory = []; - + List attachmentCategories = []; Region? currentRegion; Country? currentCountry; Province? currentProvince; @@ -37,12 +39,16 @@ class LearningDevelopmentBloc on((event, emit) async { emit(LearningDevelopmentLoadingState()); try { + if (attachmentCategories.isEmpty) { + attachmentCategories = + await AttachmentServices.instance.getCategories(); + } List learnings = await LearningDevelopmentServices .instance .getLearningDevelopments(event.profileId, event.token); learningsAndDevelopments = learnings; emit(LearningDevelopmentLoadedState( - learningsAndDevelopment: learningsAndDevelopments)); + learningsAndDevelopment: learningsAndDevelopments,attachmentCategory: attachmentCategories)); } catch (e) { emit(LearningDevelopmentErrorState(message: e.toString())); } @@ -50,7 +56,7 @@ class LearningDevelopmentBloc ////load on((event, emit) { emit(LearningDevelopmentLoadedState( - learningsAndDevelopment: learningsAndDevelopments)); + learningsAndDevelopment: learningsAndDevelopments,attachmentCategory: attachmentCategories)); }); //// show add form on((event, emit) async { @@ -266,5 +272,25 @@ class LearningDevelopmentBloc on((event, emit) { emit(LearningDevelopmentErrorState(message: event.message)); }); + ////Add attachment + on((event, emit) async { + emit(LearningDevelopmentLoadingState()); + try { + Map status = await AttachmentServices.instance + .attachment( + categoryId: event.categoryId, + module: event.attachmentModule, + paths: event.filePaths, + token: event.token, + profileId: event.profileId); + if (status['success']) { + emit(LearningDevelopmentAddedState(response: status)); + } else { + emit(LearningDevelopmentAddedState(response: status)); + } + } catch (e) { + emit(LearningDevelopmentErrorState(message: e.toString())); + } + }); } } diff --git a/lib/bloc/profile/learningDevelopment/learning_development_event.dart b/lib/bloc/profile/learningDevelopment/learning_development_event.dart index 1b1453f..cfde6bb 100644 --- a/lib/bloc/profile/learningDevelopment/learning_development_event.dart +++ b/lib/bloc/profile/learningDevelopment/learning_development_event.dart @@ -66,3 +66,14 @@ class CallErrorState extends LearningDevelopmentEvent{ final String message; const CallErrorState({required this.message}); } + +class AddAttachment extends LearningDevelopmentEvent{ + final String categoryId; + final String attachmentModule; + final List filePaths; + final String token; + final String profileId; + const AddAttachment({required this.attachmentModule, required this.filePaths, required this.categoryId, required this.profileId, required this.token}); + @override + List get props => [categoryId,attachmentModule,filePaths, token,profileId]; +} diff --git a/lib/bloc/profile/learningDevelopment/learning_development_state.dart b/lib/bloc/profile/learningDevelopment/learning_development_state.dart index e498dc9..323d076 100644 --- a/lib/bloc/profile/learningDevelopment/learning_development_state.dart +++ b/lib/bloc/profile/learningDevelopment/learning_development_state.dart @@ -10,8 +10,9 @@ abstract class LearningDevelopmentState extends Equatable { class LearningDevelopmentInitial extends LearningDevelopmentState {} class LearningDevelopmentLoadedState extends LearningDevelopmentState { + final List< AttachmentCategory> attachmentCategory; final List learningsAndDevelopment; - const LearningDevelopmentLoadedState({required this.learningsAndDevelopment}); + const LearningDevelopmentLoadedState({required this.learningsAndDevelopment, required this.attachmentCategory}); @override List get props => [learningsAndDevelopment]; } diff --git a/lib/bloc/profile/voluntary_works/voluntary_work_event.dart b/lib/bloc/profile/voluntary_works/voluntary_work_event.dart index 02ed79b..ba83ba5 100644 --- a/lib/bloc/profile/voluntary_works/voluntary_work_event.dart +++ b/lib/bloc/profile/voluntary_works/voluntary_work_event.dart @@ -72,3 +72,13 @@ class DeleteVoluntaryWork extends VoluntaryWorkEvent { @override List get props => [profileId, token, work]; } +class AddAttachment extends VoluntaryWorkEvent{ + final String categoryId; + final String attachmentModule; + final List filePaths; + final String token; + final String profileId; + const AddAttachment({required this.attachmentModule, required this.filePaths, required this.categoryId, required this.profileId, required this.token}); + @override + List get props => [categoryId,attachmentModule,filePaths, token,profileId]; +} diff --git a/lib/bloc/profile/workHistory/workHistory_bloc.dart b/lib/bloc/profile/workHistory/workHistory_bloc.dart index 2f8a28e..71f81ce 100644 --- a/lib/bloc/profile/workHistory/workHistory_bloc.dart +++ b/lib/bloc/profile/workHistory/workHistory_bloc.dart @@ -8,7 +8,9 @@ import 'package:unit2/model/utils/position.dart'; import 'package:unit2/sevices/profile/work_history_services.dart'; import 'package:unit2/utils/profile_utilities.dart'; +import '../../../model/profile/attachment.dart'; import '../../../model/utils/category.dart'; +import '../../../utils/attachment_categories.dart'; part 'workHistory_event.dart'; part 'workHistory_state.dart'; @@ -19,17 +21,22 @@ class WorkHistoryBloc extends Bloc { List agencies = []; List appointmentStatus = []; List agencyCategory = []; + List attachmentCategories = []; WorkHistoryBloc() : super(EducationInitial()) { ////GET WORK HISTORIES on((event, emit) async { emit(WorkHistoryLoadingState()); try { + if (attachmentCategories.isEmpty) { + attachmentCategories = + await AttachmentServices.instance.getCategories(); + } if (workExperiences.isEmpty) { List works = await WorkHistoryService.instance .getWorkExperiences(event.profileId, event.token); workExperiences = works; } - emit(WorkHistoryLoaded(workExperiences: workExperiences)); + emit(WorkHistoryLoaded(workExperiences: workExperiences, attachmentCategory: attachmentCategories)); } catch (e) { emit(WorkHistoryErrorState(message: e.toString())); } @@ -37,7 +44,7 @@ class WorkHistoryBloc extends Bloc { ///// LOAD WORK HISTORIES on((event, emit) { emit(WorkHistoryLoadingState()); - emit(WorkHistoryLoaded(workExperiences: workExperiences)); + emit(WorkHistoryLoaded(workExperiences: workExperiences,attachmentCategory: attachmentCategories)); }); ////DELETE on((event, emit) async { @@ -182,5 +189,25 @@ class WorkHistoryBloc extends Bloc { emit(WorkHistoryErrorState(message: e.toString())); } }); + ////Add Attachment + on((event, emit) async { + emit(WorkHistoryLoadingState()); + try { + Map status = await AttachmentServices.instance + .attachment( + categoryId: event.categoryId, + module: event.attachmentModule, + paths: event.filePaths, + token: event.token, + profileId: event.profileId); + if (status['success']) { + emit(WorkHistoryAddedState(response: status)); + } else { + emit(WorkHistoryAddedState(response: status)); + } + } catch (e) { + emit(WorkHistoryErrorState(message: e.toString())); + } + }); } } diff --git a/lib/bloc/profile/workHistory/workHistory_event.dart b/lib/bloc/profile/workHistory/workHistory_event.dart index 0f394b7..ebc1efd 100644 --- a/lib/bloc/profile/workHistory/workHistory_event.dart +++ b/lib/bloc/profile/workHistory/workHistory_event.dart @@ -60,5 +60,15 @@ class AddWorkHostory extends WorkHistorytEvent{ @override List get props => [workHistory,profileId,token,isPrivate]; } +class AddAttachment extends WorkHistorytEvent{ + final String categoryId; + final String attachmentModule; + final List filePaths; + final String token; + final String profileId; + const AddAttachment({required this.attachmentModule, required this.filePaths, required this.categoryId, required this.profileId, required this.token}); + @override + List get props => [categoryId,attachmentModule,filePaths, token,profileId]; +} diff --git a/lib/bloc/profile/workHistory/workHistory_state.dart b/lib/bloc/profile/workHistory/workHistory_state.dart index 2008857..d5905a8 100644 --- a/lib/bloc/profile/workHistory/workHistory_state.dart +++ b/lib/bloc/profile/workHistory/workHistory_state.dart @@ -11,7 +11,8 @@ class EducationInitial extends WorkHistoryState {} class WorkHistoryLoaded extends WorkHistoryState{ final List workExperiences; - const WorkHistoryLoaded({required this.workExperiences}); + final List< AttachmentCategory> attachmentCategory; + const WorkHistoryLoaded({required this.workExperiences,required this.attachmentCategory}); @override List get props => [workExperiences]; } diff --git a/lib/model/profile/attachment.dart b/lib/model/profile/attachment.dart new file mode 100644 index 0000000..17169e2 --- /dev/null +++ b/lib/model/profile/attachment.dart @@ -0,0 +1,122 @@ +// To parse this JSON data, do +// +// final attachment = attachmentFromJson(jsonString); + +import 'package:meta/meta.dart'; +import 'dart:convert'; + +Attachment attachmentFromJson(String str) => + Attachment.fromJson(json.decode(str)); + +String attachmentToJson(Attachment data) => json.encode(data.toJson()); + +class Attachment { + final int? id; + final String? source; + final AttachmentCategory? category; + final String? filename; + final Subclass? subclass; + final DateTime? createdAt; + + Attachment({ + required this.id, + required this.source, + required this.category, + required this.filename, + required this.subclass, + required this.createdAt, + }); + + factory Attachment.fromJson(Map json) => Attachment( + id: json["id"], + source: json["source"], + category: json['category'] == null + ? null + : AttachmentCategory.fromJson(json["category"]), + filename: json["filename"], + subclass: json['subclass'] == null + ? null + : Subclass.fromJson(json["subclass"]), + createdAt: json['created_at'] == null + ? null + : DateTime.parse(json["created_at"]), + ); + + Map toJson() => { + "id": id, + "source": source, + "category": category?.toJson(), + "filename": filename, + "subclass": subclass?.toJson(), + "created_at": createdAt?.toIso8601String(), + }; +} + +class AttachmentCategory { + final int id; + final Subclass subclass; + final String description; + + AttachmentCategory({ + required this.id, + required this.subclass, + required this.description, + }); + + factory AttachmentCategory.fromJson(Map json) => AttachmentCategory( + id: json["id"], + subclass: Subclass.fromJson(json["subclass"]), + description: json["description"], + ); + + Map toJson() => { + "id": id, + "subclass": subclass.toJson(), + "description": description, + }; +} + +class Subclass { + final int id; + final String name; + final AttachmentClass attachmentClass; + + Subclass({ + required this.id, + required this.name, + required this.attachmentClass, + }); + + factory Subclass.fromJson(Map json) => Subclass( + id: json["id"], + name: json["name"], + attachmentClass: AttachmentClass.fromJson(json["attachment_class"]), + ); + + Map toJson() => { + "id": id, + "name": name, + "attachment_class": attachmentClass.toJson(), + }; +} + +class AttachmentClass { + final int id; + final String name; + + AttachmentClass({ + required this.id, + required this.name, + }); + + factory AttachmentClass.fromJson(Map json) => + AttachmentClass( + id: json["id"], + name: json["name"], + ); + + Map toJson() => { + "id": id, + "name": name, + }; +} diff --git a/lib/model/profile/educational_background.dart b/lib/model/profile/educational_background.dart index 4deaee4..ef061a0 100644 --- a/lib/model/profile/educational_background.dart +++ b/lib/model/profile/educational_background.dart @@ -4,6 +4,8 @@ import 'dart:convert'; +import 'package:unit2/model/profile/attachment.dart'; + EducationalBackground educationalBackgroundFromJson(String str) => EducationalBackground.fromJson(json.decode(str)); String educationalBackgroundToJson(EducationalBackground data) => json.encode(data.toJson()); @@ -24,7 +26,7 @@ class EducationalBackground { final List? honors; final Education? education; final String? periodTo; - final dynamic attachments; + final List? attachments; final String? periodFrom; final int? unitsEarned; final String? yearGraduated; @@ -34,7 +36,7 @@ class EducationalBackground { honors: json["honors"] == null ? [] : List.from(json["honors"]!.map((x) => Honor.fromJson(x))), education: json["education"] == null ? null : Education.fromJson(json["education"]), periodTo: json["period_to"], - attachments: json["attachments"], + attachments: json['attachments'] ==null?null: List.from(json["attachments"].map((x) => Attachment.fromJson(x))), periodFrom: json["period_from"], unitsEarned: json["units_earned"], yearGraduated: json["year_graduated"], diff --git a/lib/model/profile/eligibility.dart b/lib/model/profile/eligibility.dart index f57007d..96dbb54 100644 --- a/lib/model/profile/eligibility.dart +++ b/lib/model/profile/eligibility.dart @@ -6,6 +6,7 @@ import 'package:meta/meta.dart'; import 'dart:convert'; import 'package:unit2/model/location/region.dart'; +import 'package:unit2/model/profile/attachment.dart'; import '../location/address_category.dart'; import '../location/city.dart'; @@ -33,7 +34,8 @@ class EligibityCert { final int? id; final double? rating; final DateTime? examDate; - final dynamic attachments; + final List? attachments; + final Eligibility? eligibility; final ExamAddress? examAddress; final DateTime? validityDate; @@ -45,7 +47,7 @@ class EligibityCert { examDate: json['exam_date'] == null ? null : DateTime.parse(json["exam_date"]), - attachments: null, + attachments: json['attachments'] ==null?null: List.from(json["attachments"].map((x) => Attachment.fromJson(x))), eligibility: json['eligibility'] == null ? null : Eligibility.fromJson(json["eligibility"]), diff --git a/lib/model/profile/learning_development.dart b/lib/model/profile/learning_development.dart index c00f276..17362f4 100644 --- a/lib/model/profile/learning_development.dart +++ b/lib/model/profile/learning_development.dart @@ -10,6 +10,7 @@ import '../location/city.dart'; import '../location/country.dart'; import '../utils/agency.dart'; import '../utils/industry_class.dart'; +import 'attachment.dart'; LearningDevelopement learningDevelopementFromJson(String str) => LearningDevelopement.fromJson(json.decode(str)); @@ -23,13 +24,13 @@ class LearningDevelopement { this.totalHoursAttended, }); - final dynamic attachments; + final List? attachments; final Agency? sponsoredBy; final ConductedTraining? conductedTraining; final double? totalHoursAttended; factory LearningDevelopement.fromJson(Map json) => LearningDevelopement( - attachments: json["attachments"], + attachments: json['attachments'] ==null?null: List.from(json["attachments"].map((x) => Attachment.fromJson(x))), sponsoredBy: json["sponsored_by"] == null ? null : Agency.fromJson(json["sponsored_by"]), conductedTraining: json["conducted_training"] == null ? null : ConductedTraining.fromJson(json["conducted_training"]), totalHoursAttended: json["total_hours_attended"], diff --git a/lib/model/profile/work_history.dart b/lib/model/profile/work_history.dart index 821e603..076cf47 100644 --- a/lib/model/profile/work_history.dart +++ b/lib/model/profile/work_history.dart @@ -8,6 +8,7 @@ import '../utils/agency.dart'; import '../utils/category.dart'; import '../utils/industry_class.dart'; import '../utils/position.dart'; +import 'attachment.dart'; WorkHistory workHistoryFromJson(String str) => WorkHistory.fromJson(json.decode(str)); @@ -21,7 +22,7 @@ class WorkHistory { this.toDate, this.position, this.fromDate, - // this.attachments, + this.attachments, this.salaryGrade, this.monthlySalary, this.appointmentStatus, @@ -33,7 +34,7 @@ class WorkHistory { final DateTime? toDate; final Position? position; final DateTime? fromDate; - // final dynamic attachments; + final List? attachments; final int? salaryGrade; final double? monthlySalary; final String? appointmentStatus; @@ -45,7 +46,7 @@ class WorkHistory { toDate: json["to_date"] == null ? null : DateTime.parse(json["to_date"]), position: json["position"] == null ? null : Position.fromJson(json["position"]), fromDate: json["from_date"] == null ? null : DateTime.parse(json["from_date"]), - // attachments: json["attachments"], + attachments: json['attachments'] ==null?null: List.from(json["attachments"].map((x) => Attachment.fromJson(x))), salaryGrade: json["salary_grade"], monthlySalary: json["monthly_salary"], appointmentStatus: json["appointment_status"], diff --git a/lib/screens/profile/components/education_screen.dart b/lib/screens/profile/components/education_screen.dart index 4514284..8f8363f 100644 --- a/lib/screens/profile/components/education_screen.dart +++ b/lib/screens/profile/components/education_screen.dart @@ -1,8 +1,12 @@ import 'package:app_popup_menu/app_popup_menu.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/educational_background.dart'; @@ -12,14 +16,26 @@ import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; + import '../../../bloc/profile/education/education_bloc.dart'; +import '../../../model/profile/attachment.dart'; +import '../../../theme-data.dart/btn-style.dart'; +import '../../../theme-data.dart/form-style.dart'; import '../../../utils/alerts.dart'; +import '../../../utils/global.dart'; import '../../../widgets/Leadings/close_leading.dart'; +import '../shared/multiple_attachment.dart'; +import '../shared/single_attachment.dart'; import 'education/edit_modal.dart'; + class EducationScreen extends StatelessWidget { const EducationScreen({super.key}); @override Widget build(BuildContext context) { + final parent = context; + FilePickerResult? result; + AttachmentCategory? selectedAttachmentCategory; + List attachmentCategories = []; int profileId; String? token; return Scaffold( @@ -144,6 +160,11 @@ class EducationScreen extends StatelessWidget { }, builder: (context, state) { if (state is EducationalBackgroundLoadedState) { + for (var cat in state.attachmentCategory) { + if (cat.subclass.id == 1) { + attachmentCategories.add(cat); + } + } if (state.educationalBackground.isNotEmpty) { return ListView.builder( padding: const EdgeInsets.symmetric( @@ -182,54 +203,91 @@ class EducationScreen extends StatelessWidget { decoration: box1(), padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 8), - child: Row( + child: Column( children: [ - Expanded( - child: Column( - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - Row( + Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment + .start, children: [ - Expanded( - child: Text(level, - style: Theme.of( - context) - .textTheme - .titleSmall!)), + Row( + children: [ + Expanded( + child: Text( + level, + style: Theme.of( + context) + .textTheme + .titleSmall!)), + Text( + "$periodFrom - $periodTo", + style: Theme.of( + context) + .textTheme + .bodyMedium, + ), + ], + ), + const SizedBox( + height: 5, + ), Text( - "$periodFrom - $periodTo", + school, style: Theme.of( context) .textTheme - .bodyMedium, + .titleMedium! + .copyWith( + color: + primary, + fontWeight: + FontWeight + .w500), ), - ], - ), - const SizedBox( - height: 5, - ), - Text( - school, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith( - color: primary, - fontWeight: - FontWeight - .w500), - ), - Container( - padding: - const EdgeInsets - .only(top: 8), - child: honors - .isNotEmpty - ? Column( + Container( + padding: + const EdgeInsets + .only( + top: 8), + child: honors + .isNotEmpty + ? Column( + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + const SizedBox( + height: + 8, + ), + const Text( + " honors: ", + style: + TextStyle(fontWeight: FontWeight.w600), + ), + Column( + children: honors + .map((Honor honor) => Text( + "-${honor.name!.trim()}", + style: Theme.of(context).textTheme.labelMedium, + )) + .toList(), + ), + ], + ) + : const SizedBox()), + program == null + ? const SizedBox() + : Column( mainAxisAlignment: MainAxisAlignment .start, @@ -238,104 +296,357 @@ class EducationScreen extends StatelessWidget { .start, children: [ const SizedBox( - height: 8, - ), - const Text( - " honors: ", - style: TextStyle( - fontWeight: - FontWeight.w600), - ), - Column( - children: honors - .map((Honor honor) => Text( - "-${honor.name!.trim()}", - style: Theme.of(context).textTheme.labelMedium, - )) - .toList(), + height: 5, ), + Text( + program), ], - ) - : const SizedBox()), - program == null - ? const SizedBox() - : Column( - mainAxisAlignment: - MainAxisAlignment - .start, - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - const SizedBox( - height: 5, ), - Text(program), - ], - ), - ]), - ), - AppPopupMenu( - offset: const Offset(-10, -10), - elevation: 3, - onSelected: (value) { - ////delete -= = = = = = = = =>> - if (value == 2) { - confirmAlert(context, () { - final progress = - ProgressHUD.of( - context); - progress!.showWithText( - "Loading..."); - context - .read() - .add(DeleteEducation( - educationalBackground: - state.educationalBackground[ - index], - profileId: - profileId, - token: token!)); - }, "Delete?", - "Confirm Delete?"); - } - if (value == 1) { - ////edit -= = = = = = = = =>> - final progress = - ProgressHUD.of(context); - progress!.showWithText( - "Loading..."); - context - .read() - .add(ShowEditEducationForm( - profileId: - profileId, - token: token!, - educationalBackground: - state.educationalBackground[ - index])); - } - }, - menuItems: [ - popMenuItem( - text: "Update", - value: 1, - icon: Icons.edit), - popMenuItem( - text: "Remove", - value: 2, - icon: Icons.delete), - popMenuItem( - text: "Attach", - value: 2, - icon: Icons.attach_file), + ]), + ), + AppPopupMenu( + offset: + const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + ////delete -= = = = = = = = =>> + if (value == 2) { + confirmAlert(context, + () { + final progress = + ProgressHUD.of( + context); + progress! + .showWithText( + "Loading..."); + context + .read< + EducationBloc>() + .add(DeleteEducation( + educationalBackground: + state.educationalBackground[ + index], + profileId: + profileId, + token: + token!)); + }, "Delete?", + "Confirm Delete?"); + } + if (value == 1) { + ////edit -= = = = = = = = =>> + final progress = + ProgressHUD.of( + context); + progress!.showWithText( + "Loading..."); + context + .read< + EducationBloc>() + .add(ShowEditEducationForm( + profileId: + profileId, + token: token!, + educationalBackground: + state.educationalBackground[ + index])); + } + if (value == 3) { + showDialog( + context: context, + builder: + (BuildContext + context) { + return AlertDialog( + contentPadding: + const EdgeInsets + .all(0), + backgroundColor: + Colors.grey + .shade100, + icon: + const Icon( + Icons + .file_copy, + size: 32, + color: + primary, + ), + title: const Text( + "File Attachment:"), + content: StatefulBuilder( + builder: + (context, + setState) { + return Padding( + padding: const EdgeInsets + .all( + 16.0), + child: Column( + mainAxisSize: + MainAxisSize + .min, + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + const Divider(), + Text( + program ?? + level, + style: + Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500, color: primary), + ), + const SizedBox( + height: + 5, + ), + Text( + school, + style: Theme.of(context).textTheme.titleSmall), + const SizedBox( + height: + 3, + ), + Text( + "$periodFrom - $periodTo", + style: Theme.of(context).textTheme.titleSmall), + const Divider(), + FormBuilderDropdown( + autovalidateMode: AutovalidateMode.always, + decoration: normalTextFieldStyle("attachment category", "attachment category"), + name: 'attachments_categorues', + validator: FormBuilderValidators.required(errorText: "This field is required"), + onChanged: (value) { + selectedAttachmentCategory = value; + }, + items: attachmentCategories.map((e) { + return DropdownMenuItem(value: e, child: Text(e.description)); + }).toList()), + const SizedBox( + height: + 8, + ), + Text( + "You may attach necessary documents such as Transcript of Records (TOR), diploma, and the likes.", + style: + Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.black), + ), + const SizedBox( + height: + 5, + ), + Text( + "Acceptable Files: PDF, JPEG, PNG", + style: + Theme.of(context).textTheme.bodySmall, + ), + Text( + "Max File Size (per attachment): 1MB", + style: + Theme.of(context).textTheme.bodySmall, + ), + const Divider(), + ElevatedButton( + style: ButtonStyle( + elevation: MaterialStateProperty.all(0), + backgroundColor: MaterialStateProperty.all(Colors.white), + ), + onPressed: () async { + FilePickerResult? newResult = await FilePicker.platform.pickFiles(allowMultiple: true, type: FileType.custom, allowedExtensions: [ + 'jpg', + 'png', + 'jpeg', + 'pdf' + ]); + setState(() { + if (newResult != null) { + result = newResult; + } + }); + }, + child: const Center( + child: Text( + "Select Files", + textAlign: TextAlign.center, + style: TextStyle(color: Colors.black), + ))), + const Divider(), + SingleChildScrollView( + child: + SizedBox( + height: 100, + width: double.maxFinite, + child: result == null + ? const SizedBox() + : Expanded( + child: ListView.builder( + itemCount: result!.files.length, + itemBuilder: (BuildContext context, index) { + final kb = result!.files[index].size / 1024; + final mb = kb / 1024; + final size = mb >= 1 ? '${mb.toStringAsFixed(2)}MB' : '${kb.toStringAsFixed(2)}KB'; + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: double.infinity, + child: Row( + children: [ + Flexible( + child: SizedBox( + child: result!.files[index].extension!.toLowerCase() == 'pdf' + ? SvgPicture.asset( + 'assets/svgs/pdf.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : result!.files[index].extension!.toLowerCase() == 'png' + ? SvgPicture.asset( + 'assets/svgs/png.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : result!.files[index].extension!.toLowerCase() == 'jpg' || result!.files[index].extension!.toLowerCase() == 'jpeg' + ? SvgPicture.asset( + 'assets/svgs/jpg.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : const SizedBox())), + const SizedBox( + width: 12, + ), + Flexible( + flex: 4, + child: Text( + result!.files[index].name, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.headlineLarge!.copyWith(fontSize: blockSizeVertical * 2), + ), + ), + const SizedBox(width: 12,), + Flexible( + flex: 2, + child: Text(size)) + ], + )), + const Divider() + ], + ); + }), + ), + ), + ), + const SizedBox( + height: + 12, + ), + SizedBox( + width: + double.maxFinite, + height: + 50, + child: ElevatedButton( + style: mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + List paths = []; + + if (selectedAttachmentCategory != null) { + for (var res in result!.files) { + paths.add(res.path!); + } + Navigator.pop(context); + parent.read().add(AddAttachment(attachmentModule: state.educationalBackground[index].id.toString(), filePaths: paths, categoryId: selectedAttachmentCategory!.id.toString(), token: token!, profileId: profileId.toString())); + } + }, + child: const Text("Submit")), + ) + ]), + ); + }), + ); + }); + } + }, + menuItems: [ + popMenuItem( + text: "Update", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Remove", + value: 2, + icon: Icons.delete), + popMenuItem( + text: "Attach", + value: 3, + icon: Icons + .attach_file), + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ) ], - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - ), - tooltip: "Options", - ) + ), + ////Show Attachments + SizedBox( + child: state + .educationalBackground[ + index] + .attachments == + null || + state + .educationalBackground[ + index] + .attachments! + .isEmpty + ? const SizedBox() + : state + .educationalBackground[ + index] + .attachments != + null && + state + .educationalBackground[ + index] + .attachments! + .length == + 1 + ? + ////Single Attachment view + SingleAttachment( + onpressed: () { + confirmAlert( + context, + () {}, + "Delete?", + "Confirm Delete?"); + }, + attachment: state + .educationalBackground[ + index] + .attachments! + .first, + ) + ////Multiple Attachments View + : MultipleAttachments( + eligibilityName: state + .educationalBackground[ + index] + .education! + .school! + .name!, + attachments: state + .educationalBackground[ + index] + .attachments!, + )) ], ), ), diff --git a/lib/screens/profile/components/eligibility_screen.dart b/lib/screens/profile/components/eligibility_screen.dart index e3ddd67..fc6b279 100644 --- a/lib/screens/profile/components/eligibility_screen.dart +++ b/lib/screens/profile/components/eligibility_screen.dart @@ -1,16 +1,24 @@ +import 'dart:io'; + import 'package:app_popup_menu/app_popup_menu.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; -import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; +import 'package:unit2/model/profile/attachment.dart'; import 'package:unit2/model/profile/eligibility.dart'; import 'package:unit2/screens/profile/components/eligibility/add_modal.dart'; import 'package:unit2/screens/profile/components/eligibility/edit_modal.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; +import 'package:unit2/theme-data.dart/btn-style.dart'; import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/theme-data.dart/form-style.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; @@ -19,21 +27,27 @@ import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; import '../../../bloc/profile/eligibility/eligibility_bloc.dart'; import '../../../utils/alerts.dart'; +import '../shared/multiple_attachment.dart'; +import '../shared/single_attachment.dart'; class EligibiltyScreen extends StatelessWidget { const EligibiltyScreen({super.key}); @override Widget build(BuildContext context) { + BuildContext parent = context; String? token; int? profileId; + FilePickerResult? result; + AttachmentCategory? selectedAttachmentCategory; + List attachmentCategories = []; return WillPopScope( onWillPop: () async { return true; }, child: Scaffold( - resizeToAvoidBottomInset: true, + resizeToAvoidBottomInset: true, appBar: AppBar( title: context.watch().state is AddEligibilityState ? const Text("Add Eligiblity") @@ -42,31 +56,37 @@ class EligibiltyScreen extends StatelessWidget { : const Text(elibilityScreenTitle), centerTitle: true, backgroundColor: primary, - actions: (context.watch().state is EligibilityLoaded) - - ? [ - AddLeading(onPressed: () { - context - .read() - .add(ShowAddEligibilityForm()); - }) - ] - :(context.watch().state is AddEligibilityState || context.watch().state is EditEligibilityState)? [ - CloseLeading(onPressed: () { - context.read().add(const LoadEligibility()); - }) - ]:[], + actions: + (context.watch().state is EligibilityLoaded) + ? [ + AddLeading(onPressed: () { + context + .read() + .add(ShowAddEligibilityForm()); + }) + ] + : (context.watch().state + is AddEligibilityState || + context.watch().state + is EditEligibilityState) + ? [ + CloseLeading(onPressed: () { + context + .read() + .add(const LoadEligibility()); + }) + ] + : [], ), body: BlocBuilder( builder: (context, state) { if (state is UserLoggedIn) { token = state.userData!.user!.login!.token; - profileId = - state.userData!.user!.login!.user!.profileId; + profileId = state.userData!.user!.login!.user!.profileId; return BlocBuilder( - builder: (context, state) { - if(state is ProfileLoaded){ - return ProgressHUD( + builder: (context, state) { + if (state is ProfileLoaded) { + return ProgressHUD( padding: const EdgeInsets.all(24), indicatorWidget: const SpinKitFadingCircle( color: Colors.white, @@ -84,8 +104,7 @@ class EligibiltyScreen extends StatelessWidget { state is DeletedState || state is EligibilityAddedState || state is EligibilityEditedState || - state is EligibilityErrorState - ) { + state is EligibilityErrorState) { final progress = ProgressHUD.of(context); progress!.dismiss(); } @@ -96,15 +115,17 @@ class EligibiltyScreen extends StatelessWidget { "Eligibility has been deleted successfully", () { Navigator.of(context).pop(); - context.read().add(const LoadEligibility( - )); + context + .read() + .add(const LoadEligibility()); }); } else { errorAlert(context, "Deletion Failed", "Error deleting eligibility", () { Navigator.of(context).pop(); - context.read().add(const LoadEligibility( - )); + context + .read() + .add(const LoadEligibility()); }); } } @@ -114,16 +135,18 @@ class EligibiltyScreen extends StatelessWidget { successAlert(context, "Adding Successfull!", state.response['message'], () { Navigator.of(context).pop(); - context.read().add(const LoadEligibility( - )); + context + .read() + .add(const LoadEligibility()); }); } else { errorAlert(context, "Adding Failed", "Something went wrong. Please try again.", () { Navigator.of(context).pop(); - context.read().add(const LoadEligibility( - )); + context + .read() + .add(const LoadEligibility()); }); } } @@ -133,16 +156,18 @@ class EligibiltyScreen extends StatelessWidget { successAlert(context, "Update Successfull!", state.response['message'], () { Navigator.of(context).pop(); - context.read().add(const LoadEligibility( - )); + context + .read() + .add(const LoadEligibility()); }); } else { errorAlert(context, "Update Failed", "Something went wrong. Please try again.", () { Navigator.of(context).pop(); - context.read().add(const LoadEligibility( - )); + context + .read() + .add(const LoadEligibility()); }); } } @@ -151,7 +176,11 @@ class EligibiltyScreen extends StatelessWidget { return BlocBuilder( builder: (context, state) { if (state is EligibilityLoaded) { - + for (var cat in state.attachmentCategory) { + if (cat.subclass.id == 3) { + attachmentCategories.add(cat); + } + } if (state.eligibilities.isNotEmpty) { return ListView.builder( padding: const EdgeInsets.symmetric( @@ -164,6 +193,7 @@ class EligibiltyScreen extends StatelessWidget { .eligibility! .title; return Column( + mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: @@ -180,6 +210,8 @@ class EligibiltyScreen extends StatelessWidget { children: [ Expanded( child: Column( + mainAxisSize: + MainAxisSize.min, mainAxisAlignment: MainAxisAlignment .start, @@ -196,9 +228,10 @@ class EligibiltyScreen extends StatelessWidget { .copyWith( fontWeight: FontWeight - .w500,color: primary), + .w500, + color: + primary), ), - const SizedBox( height: 5, ), @@ -212,11 +245,52 @@ class EligibiltyScreen extends StatelessWidget { height: 3, ), Text( - "Rating : ${state.eligibilities[index].rating ?? 'N/A'}.", + "Rating : ${state.eligibilities[index].rating ?? 'N/A'}", style: Theme.of( context) .textTheme - .titleSmall) + .titleSmall), + const Divider(), + ////Show Attachments + SizedBox( + child: state.eligibilities[index].attachments == + null || + state + .eligibilities[ + index] + .attachments! + .isEmpty + ? const SizedBox() + : state.eligibilities[index].attachments != + null && + state.eligibilities[index].attachments!.length == + 1 + ? + ////Single Attachment view + SingleAttachment( + onpressed: + () { + confirmAlert(context, + () { + parent.read().add(DeleteAttachment(attachment: state.eligibilities[index].attachments!.first, moduleId: state.eligibilities[index].attachments!.first.id.toString(), profileId: profileId.toString(), token: token!)); + }, "Delete?", + "Confirm Delete?"); + }, + attachment: state + .eligibilities[index] + .attachments! + .first, + ) + ////Multiple Attachments View + : MultipleAttachments( + eligibilityName: state + .eligibilities[index] + .eligibility! + .title, + attachments: state + .eligibilities[index] + .attachments!, + )) ]), ), AppPopupMenu( @@ -224,29 +298,27 @@ class EligibiltyScreen extends StatelessWidget { const Offset(-10, -10), elevation: 3, onSelected: (value) { - ////delete eligibilty-= = = = = = = = =>> if (value == 2) { - confirmAlert(context, () { - final progress = - ProgressHUD.of( - context); - progress!.showWithText( - "Loading..."); + final progress = + ProgressHUD.of( + context); + progress! + .showWithText( + "Loading..."); BlocProvider.of< EligibilityBloc>( context) .add(DeleteEligibility( - - eligibilityId: state .eligibilities[ index] .id!, profileId: - profileId.toString(), + profileId + .toString(), token: token!)); }, "Delete?", @@ -254,11 +326,11 @@ class EligibiltyScreen extends StatelessWidget { } if (value == 1) { ////edit eligibilty-= = = = = = = = =>> - final progress = - ProgressHUD.of( - context); - progress!.showWithText( - "Loading..."); + final progress = + ProgressHUD.of( + context); + progress!.showWithText( + "Loading..."); EligibityCert eligibityCert = state.eligibilities[ @@ -278,27 +350,247 @@ class EligibiltyScreen extends StatelessWidget { overseas; context - .read() + .read< + EligibilityBloc>() .add(ShowEditEligibilityForm( eligibityCert: eligibityCert)); } + ////Attachment + if (value == 3) { + showDialog( + context: context, + builder: + (BuildContext + context) { + return AlertDialog( + contentPadding: + const EdgeInsets + .all(0), + backgroundColor: + Colors.grey + .shade100, + icon: + const Icon( + Icons + .file_copy, + size: 32, + color: + primary, + ), + title: const Text( + "File Attachment:"), + content: StatefulBuilder( + builder: + (context, + setState) { + return Padding( + padding: const EdgeInsets + .all( + 16.0), + child: Column( + mainAxisSize: + MainAxisSize + .min, + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + const Divider(), + Text( + title, + style: + Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500, color: primary), + ), + const SizedBox( + height: + 5, + ), + Text( + "$licenseNumber: ${state.eligibilities[index].licenseNumber == null ? 'N/A' : state.eligibilities[index].licenseNumber.toString()}", + style: Theme.of(context).textTheme.titleSmall), + const SizedBox( + height: + 3, + ), + Text( + "Rating : ${state.eligibilities[index].rating ?? 'N/A'}", + style: Theme.of(context).textTheme.titleSmall), + const Divider(), + FormBuilderDropdown( + autovalidateMode: AutovalidateMode.always, + decoration: normalTextFieldStyle("attachment category", "attachment category"), + name: 'attachments_categorues', + validator: FormBuilderValidators.required(errorText: "This field is required"), + onChanged: (value) { + selectedAttachmentCategory = value; + }, + items: attachmentCategories.map((e) { + return DropdownMenuItem(value: e, child: Text(e.description)); + }).toList()), + const SizedBox( + height: + 8, + ), + Text( + "You may attach necessary documents such as Transcript of Records (TOR), diploma, and the likes.", + style: + Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.black), + ), + const SizedBox( + height: + 5, + ), + Text( + "Acceptable Files: PDF, JPEG, PNG", + style: + Theme.of(context).textTheme.bodySmall, + ), + Text( + "Max File Size (per attachment): 1MB", + style: + Theme.of(context).textTheme.bodySmall, + ), + const Divider(), + ElevatedButton( + style: ButtonStyle( + elevation: MaterialStateProperty.all(0), + backgroundColor: MaterialStateProperty.all(Colors.white), + ), + onPressed: () async { + FilePickerResult? newResult = await FilePicker.platform.pickFiles(allowMultiple: true, type: FileType.custom, allowedExtensions: [ + 'jpg', + 'png', + 'jpeg', + 'pdf' + ]); + setState(() { + if (newResult != null) { + result = newResult; + } + }); + }, + child: const Center( + child: Text( + "Select Files", + textAlign: TextAlign.center, + style: TextStyle(color: Colors.black), + ))), + const Divider(), + SingleChildScrollView( + child: + SizedBox( + height: 100, + width: double.maxFinite, + child: result == null + ? const SizedBox() + : Expanded( + child: ListView.builder( + itemCount: result!.files.length, + itemBuilder: (BuildContext context, index) { + final kb = result!.files[index].size / 1024; + final mb = kb / 1024; + final size = mb >= 1 ? '${mb.toStringAsFixed(2)}MB' : '${kb.toStringAsFixed(2)}KB'; + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: double.infinity, + child: Row( + children: [ + Flexible( + child: SizedBox( + child: result!.files[index].extension!.toLowerCase() == 'pdf' + ? SvgPicture.asset( + 'assets/svgs/pdf.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : result!.files[index].extension!.toLowerCase() == 'png' + ? SvgPicture.asset( + 'assets/svgs/png.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : result!.files[index].extension!.toLowerCase() == 'jpg' || result!.files[index].extension!.toLowerCase() == 'jpeg' + ? SvgPicture.asset( + 'assets/svgs/jpg.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : const SizedBox())), + const SizedBox( + width: 12, + ), + Flexible( + flex: 4, + child: Text( + result!.files[index].name, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.headlineLarge!.copyWith(fontSize: blockSizeVertical * 2), + ), + ), + const SizedBox(width: 8,), + Flexible( + flex: 2, + child: Text(size)) + ], + )), + const Divider() + ], + ); + }), + ), + ), + ), + const SizedBox( + height: + 12, + ), + SizedBox( + width: + double.maxFinite, + height: + 50, + child: ElevatedButton( + style: mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + List paths = []; + + if (selectedAttachmentCategory != null) { + for (var res in result!.files) { + paths.add(res.path!); + } + Navigator.pop(context); + parent.read().add(AddAttachment(attachmentModule: state.eligibilities[index].id.toString(), filePaths: paths, categoryId: selectedAttachmentCategory!.id.toString(), token: token!, profileId: profileId.toString())); + } + }, + child: const Text("Submit")), + ) + ]), + ); + }), + ); + }); + } }, - menuItems: [ - popMenuItem( - text: "Update", - value: 1, - icon: Icons.edit), - popMenuItem( - text: "Remove", - value: 2, - icon: Icons.delete), - popMenuItem( - text: "Attach", - value: 2, - icon: Icons.attach_file), - - ], + menuItems: [ + popMenuItem( + text: "Update", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Remove", + value: 2, + icon: Icons.delete), + popMenuItem( + text: "Attach", + value: 3, + icon: Icons + .attach_file), + ], icon: const Icon( Icons.more_vert, color: Colors.grey, @@ -310,7 +602,7 @@ class EligibiltyScreen extends StatelessWidget { ), const SizedBox( height: 5, - ) + ), ], ); }); @@ -322,17 +614,25 @@ class EligibiltyScreen extends StatelessWidget { } if (state is EditEligibilityState) { return EditEligibilityScreen( - profileId: profileId!, - token: token!, + profileId: profileId!, + token: token!, eligibityCert: state.eligibityCert); } if (state is AddEligibilityState) { - return AddEligibilityScreen(token: token!,profileId: profileId!,); + return AddEligibilityScreen( + token: token!, + profileId: profileId!, + ); } if (state is EligibilityErrorState) { - return SomethingWentWrong(message: state.message, onpressed: (){ - context.read().add(GetEligibilities(token: token!,profileId: profileId!)); - }); + return SomethingWentWrong( + message: state.message, + onpressed: () { + context.read().add( + GetEligibilities( + token: token!, + profileId: profileId!)); + }); } return Container( color: Colors.grey.shade200, @@ -342,11 +642,9 @@ class EligibiltyScreen extends StatelessWidget { }, ), ); - - } - return Container(); } - ); + return Container(); + }); } return Container(); }, diff --git a/lib/screens/profile/components/learning_and_development_screen.dart b/lib/screens/profile/components/learning_and_development_screen.dart index fc8df3b..c7cea00 100644 --- a/lib/screens/profile/components/learning_and_development_screen.dart +++ b/lib/screens/profile/components/learning_and_development_screen.dart @@ -1,11 +1,15 @@ import 'package:app_popup_menu/app_popup_menu.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter/src/widgets/placeholder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:intl/intl.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; @@ -19,8 +23,13 @@ import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; import '../../../bloc/profile/learningDevelopment/learning_development_bloc.dart'; +import '../../../model/profile/attachment.dart'; +import '../../../theme-data.dart/btn-style.dart'; +import '../../../theme-data.dart/form-style.dart'; import '../../../utils/alerts.dart'; import '../../../widgets/Leadings/close_leading.dart'; +import '../shared/multiple_attachment.dart'; +import '../shared/single_attachment.dart'; import 'learning_development/add_modal.dart'; class LearningAndDevelopmentScreen extends StatelessWidget { @@ -32,7 +41,11 @@ class LearningAndDevelopmentScreen extends StatelessWidget { Widget build(BuildContext context) { String token; int profileId; + BuildContext parent = context; DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); + FilePickerResult? result; + AttachmentCategory? selectedAttachmentCategory; + List attachmentCategories = []; return Scaffold( appBar: AppBar( title: context.watch().state @@ -167,9 +180,13 @@ class LearningAndDevelopmentScreen extends StatelessWidget { // TODO: implement listener }, builder: (context, state) { - print(state); + if (state is LearningDevelopmentLoadedState) { - + for (var cat in state.attachmentCategory) { + if (cat.subclass.id == 2) { + attachmentCategories.add(cat); + } + } if (state.learningsAndDevelopment.isNotEmpty) { return ListView.builder( padding: const EdgeInsets.symmetric( @@ -208,140 +225,403 @@ class LearningAndDevelopmentScreen extends StatelessWidget { padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 8), width: screenWidth, - child: Row( + child: Column( children: [ - Expanded( - child: Column( - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - Text( - training, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith( - fontWeight: - FontWeight - .w600, - color: primary), - ), - const SizedBox( - height: 8, - ), - Text( - provider, - style: Theme.of(context) - .textTheme - .titleSmall, - ), - const SizedBox( - height: 5, - ), - Text( - "$duration: $start to $end", - style: Theme.of(context) - .textTheme - .labelMedium, - ), - const SizedBox( - height: 5, - ), - ]), - ), - AppPopupMenu( - offset: const Offset(-10, -10), - elevation: 3, - onSelected: (value) { - ////delete -= = = = = = = = =>> - if (value == 2) { - confirmAlert(context, () { - final progress = - ProgressHUD.of( - context); - progress!.showWithText( - "Loading..."); - BlocProvider.of< - LearningDevelopmentBloc>( - context) - .add(DeleteLearningDevelopment( - trainingId: state - .learningsAndDevelopment[ - index] - .conductedTraining! - .id!, - hours: state - .learningsAndDevelopment[ - index] - .conductedTraining! - .totalHours!, - sponsorId: state - .learningsAndDevelopment[ - index] - .sponsoredBy - ?.id, - profileId: - profileId, - token: token)); - }, "Delete?", - "Confirm Delete?"); - } - if (value == 1) { - bool isOverseas; - ////edit = = = = = = = =>> - final progress = - ProgressHUD.of(context); - progress!.showWithText( - "Loading..."); + Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + Text( + training, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight + .w600, + color: primary), + ), + const SizedBox( + height: 8, + ), + Text( + provider, + style: Theme.of(context) + .textTheme + .titleSmall, + ), + const SizedBox( + height: 5, + ), + Text( + "$duration: $start to $end", + style: Theme.of(context) + .textTheme + .labelMedium, + ), + const SizedBox( + height: 5, + ), + ]), + ), + AppPopupMenu( + offset: const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + ////delete -= = = = = = = = =>> + if (value == 2) { + confirmAlert(context, () { + final progress = + ProgressHUD.of( + context); + progress!.showWithText( + "Loading..."); + BlocProvider.of< + LearningDevelopmentBloc>( + context) + .add(DeleteLearningDevelopment( + trainingId: state + .learningsAndDevelopment[ + index] + .conductedTraining! + .id!, + hours: state + .learningsAndDevelopment[ + index] + .conductedTraining! + .totalHours!, + sponsorId: state + .learningsAndDevelopment[ + index] + .sponsoredBy + ?.id, + profileId: + profileId, + token: token)); + }, "Delete?", + "Confirm Delete?"); + } + if (value == 1) { + bool isOverseas; + ////edit = = = = = = = =>> + final progress = + ProgressHUD.of(context); + progress!.showWithText( + "Loading..."); - if (state - .learningsAndDevelopment[ - index] - .conductedTraining - ?.venue - ?.cityMunicipality == - null) { - isOverseas = true; - } else { - isOverseas = false; - } - context - .read< - LearningDevelopmentBloc>() - .add(ShowEditLearningDevelopmentForm( - profileId: - profileId, - token: token, - learningDevelopment: - state.learningsAndDevelopment[ - index], - isOverseas: - isOverseas)); - } - }, - menuItems: [ - popMenuItem( - text: "Update", - value: 1, - icon: Icons.edit), - popMenuItem( - text: "Remove", - value: 2, - icon: Icons.delete), - popMenuItem( - text: "Attach", - value: 2, - icon: Icons.attach_file), + if (state + .learningsAndDevelopment[ + index] + .conductedTraining + ?.venue + ?.cityMunicipality == + null) { + isOverseas = true; + } else { + isOverseas = false; + } + context + .read< + LearningDevelopmentBloc>() + .add(ShowEditLearningDevelopmentForm( + profileId: + profileId, + token: token, + learningDevelopment: + state.learningsAndDevelopment[ + index], + isOverseas: + isOverseas)); + } + if(value == 3){ + showDialog( + context: context, + builder: + (BuildContext + context) { + return AlertDialog( + contentPadding: + const EdgeInsets + .all(0), + backgroundColor: + Colors.grey + .shade100, + icon: + const Icon( + Icons + .file_copy, + size: 32, + color: + primary, + ), + title: const Text( + "File Attachment:"), + content: StatefulBuilder( + builder: + (context, + setState) { + return Padding( + padding: const EdgeInsets + .all( + 16.0), + child: Column( + mainAxisSize: + MainAxisSize + .min, + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + const Divider(), + Text( + training, + style: + Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500, color: primary), + ), + const SizedBox( + height: + 5, + ), + Text( + provider, + style: Theme.of(context).textTheme.titleSmall), + const SizedBox( + height: + 3, + ), + Text( + "$start TO $end", + style: Theme.of(context).textTheme.titleSmall), + const Divider(), + FormBuilderDropdown( + autovalidateMode: AutovalidateMode.always, + decoration: normalTextFieldStyle("attachment category", "attachment category"), + name: 'attachments_categorues', + validator: FormBuilderValidators.required(errorText: "This field is required"), + onChanged: (value) { + selectedAttachmentCategory = value; + }, + items: attachmentCategories.map((e) { + return DropdownMenuItem(value: e, child: Text(e.description)); + }).toList()), + const SizedBox( + height: + 8, + ), + Text( + "You may attach necessary documents such as Transcript of Records (TOR), diploma, and the likes.", + style: + Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.black), + ), + const SizedBox( + height: + 5, + ), + Text( + "Acceptable Files: PDF, JPEG, PNG", + style: + Theme.of(context).textTheme.bodySmall, + ), + Text( + "Max File Size (per attachment): 1MB", + style: + Theme.of(context).textTheme.bodySmall, + ), + const Divider(), + ElevatedButton( + style: ButtonStyle( + elevation: MaterialStateProperty.all(0), + backgroundColor: MaterialStateProperty.all(Colors.white), + ), + onPressed: () async { + FilePickerResult? newResult = await FilePicker.platform.pickFiles(allowMultiple: true, type: FileType.custom, allowedExtensions: [ + 'jpg', + 'png', + 'jpeg', + 'pdf' + ]); + setState(() { + if (newResult != null) { + result = newResult; + } + }); + }, + child: const Center( + child: Text( + "Select Files", + textAlign: TextAlign.center, + style: TextStyle(color: Colors.black), + ))), + const Divider(), + SingleChildScrollView( + child: + SizedBox( + height: 100, + width: double.maxFinite, + child: result == null + ? const SizedBox() + : Expanded( + child: ListView.builder( + itemCount: result!.files.length, + itemBuilder: (BuildContext context, index) { + final kb = result!.files[index].size / 1024; + final mb = kb / 1024; + final size = mb >= 1 ? '${mb.toStringAsFixed(2)}MB' : '${kb.toStringAsFixed(2)}KB'; + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + + width: double.infinity, + child: Row( + children: [ + Flexible( + flex: 1, + child: SizedBox( + child: result!.files[index].extension!.toLowerCase() == 'pdf' + ? SvgPicture.asset( + 'assets/svgs/pdf.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : result!.files[index].extension!.toLowerCase() == 'png' + ? SvgPicture.asset( + 'assets/svgs/png.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : result!.files[index].extension!.toLowerCase() == 'jpg' || result!.files[index].extension!.toLowerCase() == 'jpeg' + ? SvgPicture.asset( + 'assets/svgs/jpg.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : const SizedBox())), + const SizedBox( + width: 12, + ), + Flexible( + flex: 4, + child: Text( + result!.files[index].name, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.headlineLarge!.copyWith(fontSize: blockSizeVertical * 2), + ), + ), + const SizedBox(width: 12,), + Flexible( + flex: 2, + child: Text(size)) + ], + )), + const Divider() + ], + ); + }), + ), + ), + ), + const SizedBox( + height: + 12, + ), + SizedBox( + width: + double.maxFinite, + height: + 50, + child: ElevatedButton( + style: mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + List paths = []; + + if (selectedAttachmentCategory != null) { + for (var res in result!.files) { + paths.add(res.path!); + } + Navigator.pop(context); + parent.read().add(AddAttachment(attachmentModule: state.learningsAndDevelopment[index].conductedTraining!.id.toString(), filePaths: paths, categoryId: selectedAttachmentCategory!.id.toString(), token: token!, profileId: profileId.toString())); + } + }, + child: const Text("Submit")), + ) + ]), + ); + }), + ); + }); + } + }, + menuItems: [ + popMenuItem( + text: "Update", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Remove", + value: 2, + icon: Icons.delete), + popMenuItem( + text: "Attach", + value: 3, + icon: Icons.attach_file), + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ) ], - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - ), - tooltip: "Options", - ) + ), + SizedBox( + child: state.learningsAndDevelopment[index].attachments == + null || + state + .learningsAndDevelopment[ + index] + .attachments! + .isEmpty + ? const SizedBox() + : state.learningsAndDevelopment[index].attachments != + null && + state.learningsAndDevelopment[index].attachments!.length == + 1 + ? + ////Single Attachment view + SingleAttachment( + onpressed: + () { + confirmAlert(context, + () { + + }, "Delete?", + "Confirm Delete?"); + }, + attachment: state + .learningsAndDevelopment[index] + .attachments! + .first, + ) + ////Multiple Attachments View + : MultipleAttachments( + eligibilityName: state + .learningsAndDevelopment[index] + .conductedTraining!.title!.title!, + + attachments: state + .learningsAndDevelopment[index] + .attachments!, + )) ], ), ), diff --git a/lib/screens/profile/components/work_history_screen.dart b/lib/screens/profile/components/work_history_screen.dart index 5372185..153f851 100644 --- a/lib/screens/profile/components/work_history_screen.dart +++ b/lib/screens/profile/components/work_history_screen.dart @@ -1,10 +1,14 @@ import 'dart:io'; import 'package:app_popup_menu/app_popup_menu.dart'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:intl/intl.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; @@ -19,9 +23,14 @@ import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; import '../../../bloc/profile/workHistory/workHistory_bloc.dart'; +import '../../../model/profile/attachment.dart'; import '../../../model/profile/work_history.dart'; +import '../../../theme-data.dart/btn-style.dart'; +import '../../../theme-data.dart/form-style.dart'; import '../../../utils/alerts.dart'; import '../../../utils/global.dart'; +import '../shared/multiple_attachment.dart'; +import '../shared/single_attachment.dart'; class WorkHistoryScreen extends StatelessWidget { const WorkHistoryScreen({super.key}); @@ -29,8 +38,12 @@ class WorkHistoryScreen extends StatelessWidget { @override Widget build(BuildContext context) { DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); + BuildContext parent = context; String? token; - int profileId; + int? profileId; + FilePickerResult? result; + AttachmentCategory? selectedAttachmentCategory; + List attachmentCategories = []; return Scaffold( appBar: AppBar( title: context.watch().state is AddWorkHistoryState @@ -160,6 +173,11 @@ class WorkHistoryScreen extends StatelessWidget { }, builder: (context, state) { if (state is WorkHistoryLoaded) { + for (var cat in state.attachmentCategory) { + if (cat.subclass.id == 4) { + attachmentCategories.add(cat); + } + } if (state.workExperiences.isNotEmpty) { return ListView.builder( padding: const EdgeInsets.symmetric( @@ -183,116 +201,421 @@ class WorkHistoryScreen extends StatelessWidget { .workExperiences[index] .toDate!); return Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, children: [ Container( width: screenWidth, decoration: box1(), padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 8), - child: Row(children: [ - Expanded( - child: Column( - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - position, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith( - fontWeight: - FontWeight.w600, - color: primary), - ), - const SizedBox( - height: 8, - ), - Text( - agency, - style: Theme.of(context) - .textTheme - .titleSmall! - .copyWith( - fontWeight: - FontWeight.w500), - ), - const SizedBox( - height: 5, - ), - Text( - "$from - $to ", - style: Theme.of(context) - .textTheme - .labelMedium, - ), - ], - )), - AppPopupMenu( - offset: const Offset(-10, -10), - elevation: 3, - onSelected: (value) { - ////delete workhistory-= = = = = = = = =>> - if (value == 2) { - confirmAlert(context, () { - final progress = - ProgressHUD.of(context); - progress!.showWithText( - "Loading..."); - BlocProvider.of< - WorkHistoryBloc>( - context) - .add(DeleteWorkHistory( - profileId: profileId, - token: token!, - workHistory: - state.workExperiences[ + child: Column( + children: [ + Row(children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + position, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight + .w600, + color: primary), + ), + const SizedBox( + height: 8, + ), + Text( + agency, + style: Theme.of(context) + .textTheme + .titleSmall! + .copyWith( + fontWeight: + FontWeight + .w500), + ), + const SizedBox( + height: 5, + ), + Text( + "$from - $to ", + style: Theme.of(context) + .textTheme + .labelMedium, + ), + ], + )), + AppPopupMenu( + offset: + const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + ////delete workhistory-= = = = = = = = =>> + if (value == 2) { + confirmAlert(context, () { + final progress = + ProgressHUD.of( + context); + progress!.showWithText( + "Loading..."); + BlocProvider.of< + WorkHistoryBloc>( + context) + .add( + DeleteWorkHistory( + profileId: profileId!, + token: token!, + workHistory: state + .workExperiences[ index], - )); - }, "Delete?", - "Confirm Delete?"); - } - if (value == 1) { - ////edit eligibilty-= = = = = = = = =>> - final progress = - ProgressHUD.of(context); - progress!.showWithText( - "Loading..."); - WorkHistory workHistory = - state.workExperiences[ - index]; - context - .read() - .add( - ShowEditWorkHistoryForm( + )); + }, "Delete?", + "Confirm Delete?"); + } + if (value == 1) { + ////edit eligibilty-= = = = = = = = =>> + final progress = + ProgressHUD.of( + context); + progress!.showWithText( + "Loading..."); + WorkHistory workHistory = + state.workExperiences[ + index]; + context + .read< + WorkHistoryBloc>() + .add(ShowEditWorkHistoryForm( workHistory: workHistory)); - } - }, - menuItems: [ - popMenuItem( - text: "Update", - value: 1, - icon: Icons.edit), - popMenuItem( - text: "Remove", - value: 2, - icon: Icons.delete), - popMenuItem( - text: "Attach", - value: 2, - icon: Icons.attach_file), - - ], - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - ), - tooltip: "Options", - ) - ]), + } + ////Attachment + if (value == 3) { + showDialog( + context: context, + builder: (BuildContext + context) { + return AlertDialog( + contentPadding: + const EdgeInsets + .all(0), + backgroundColor: + Colors.grey + .shade100, + icon: const Icon( + Icons.file_copy, + size: 32, + color: primary, + ), + title: const Text( + "File Attachment:"), + content: StatefulBuilder( + builder: (context, + setState) { + return Padding( + padding: + const EdgeInsets + .all( + 16.0), + child: Column( + mainAxisSize: + MainAxisSize + .min, + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + const Divider(), + Text( + position, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(fontWeight: FontWeight.w600, color: primary), + ), + const SizedBox( + height: + 8, + ), + Text( + agency, + style: Theme.of(context) + .textTheme + .titleSmall! + .copyWith(fontWeight: FontWeight.w500), + ), + const SizedBox( + height: + 5, + ), + Text( + "$from - $to ", + style: Theme.of(context) + .textTheme + .labelMedium, + ), + const Divider(), + FormBuilderDropdown( + autovalidateMode: AutovalidateMode + .always, + decoration: normalTextFieldStyle( + "attachment category", "attachment category"), + name: + 'attachments_categorues', + validator: + FormBuilderValidators.required(errorText: "This field is required"), + onChanged: (value) { + selectedAttachmentCategory = value; + }, + items: attachmentCategories.map((e) { + return DropdownMenuItem(value: e, child: Text(e.description)); + }).toList()), + const SizedBox( + height: + 8, + ), + Text( + "You may attach necessary documents such as Transcript of Records (TOR), diploma, and the likes.", + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith(color: Colors.black), + ), + const SizedBox( + height: + 5, + ), + Text( + "Acceptable Files: PDF, JPEG, PNG", + style: Theme.of(context) + .textTheme + .bodySmall, + ), + Text( + "Max File Size (per attachment): 1MB", + style: Theme.of(context) + .textTheme + .bodySmall, + ), + const Divider(), + ElevatedButton( + style: + ButtonStyle( + elevation: MaterialStateProperty.all(0), + backgroundColor: MaterialStateProperty.all(Colors.white), + ), + onPressed: + () async { + FilePickerResult? newResult = await FilePicker.platform.pickFiles(allowMultiple: true, type: FileType.custom, allowedExtensions: [ + 'jpg', + 'png', + 'jpeg', + 'pdf' + ]); + setState(() { + if (newResult != null) { + result = newResult; + } + }); + }, + child: const Center( + child: Text( + "Select Files", + textAlign: TextAlign.center, + style: TextStyle(color: Colors.black), + ))), + const Divider(), + SingleChildScrollView( + child: + SizedBox( + height: + 100, + width: + double.maxFinite, + child: result == null + ? const SizedBox() + : Expanded( + child: ListView.builder( + itemCount: result!.files.length, + itemBuilder: (BuildContext context, index) { + final kb = result!.files[index].size / 1024; + final mb = kb / 1024; + final size = mb >= 1 ? '${mb.toStringAsFixed(2)}MB' : '${kb.toStringAsFixed(2)}KB'; + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: double.infinity, + child: Row( + children: [ + Flexible( + child: SizedBox( + child: result!.files[index].extension!.toLowerCase() == 'pdf' + ? SvgPicture.asset( + 'assets/svgs/pdf.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : result!.files[index].extension!.toLowerCase() == 'png' + ? SvgPicture.asset( + 'assets/svgs/png.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : result!.files[index].extension!.toLowerCase() == 'jpg' || result!.files[index].extension!.toLowerCase() == 'jpeg' + ? SvgPicture.asset( + 'assets/svgs/jpg.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : const SizedBox())), + const SizedBox( + width: 12, + ), + Flexible( + flex: 4, + child: Text( + result!.files[index].name, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.headlineLarge!.copyWith(fontSize: blockSizeVertical * 2), + ), + ), + const SizedBox(width: 12,), + Flexible( + flex: 2, + child: Text(size)) + ], + )), + const Divider() + ], + ); + }), + ), + ), + ), + const SizedBox( + height: + 12, + ), + SizedBox( + width: + double.maxFinite, + height: + 50, + child: ElevatedButton( + style: mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + List paths = []; + + if (selectedAttachmentCategory != null) { + for (var res in result!.files) { + paths.add(res.path!); + } + Navigator.pop(context); + parent.read().add(AddAttachment(attachmentModule: state.workExperiences[index].id.toString(), filePaths: paths, categoryId: selectedAttachmentCategory!.id.toString(), token: token!, profileId: profileId.toString())); + } + }, + child: const Text("Submit")), + ) + ]), + ); + }), + ); + }); + } + }, + menuItems: [ + popMenuItem( + text: "Update", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Remove", + value: 2, + icon: Icons.delete), + popMenuItem( + text: "Attach", + value: 3, + icon: + Icons.attach_file), + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ) + ]), + const Divider(), + ////Show Attachments + SizedBox( + child: state + .workExperiences[ + index] + .attachments == + null || + state + .workExperiences[ + index] + .attachments! + .isEmpty + ? const SizedBox() + : state + .workExperiences[ + index] + .attachments != + null && + state + .workExperiences[ + index] + .attachments! + .length == + 1 + ? + ////Single Attachment view + SingleAttachment( + onpressed: () { + confirmAlert( + context, + () {}, + "Delete?", + "Confirm Delete?"); + }, + attachment: state + .workExperiences[ + index] + .attachments! + .first, + ) + ////Multiple Attachments View + : MultipleAttachments( + eligibilityName: state + .workExperiences[ + index] + .position! + .title!, + attachments: state + .workExperiences[ + index] + .attachments!, + )) + ], + ), ), const SizedBox( height: 5, @@ -308,13 +631,13 @@ class WorkHistoryScreen extends StatelessWidget { } if (state is AddWorkHistoryState) { return AddWorkHistoryScreen( - profileId: profileId, + profileId: profileId!, token: token!, ); } if (state is EditWorkHistoryState) { return EditWorkHistoryScreen( - profileId: profileId, + profileId: profileId!, token: token!, ); } @@ -324,7 +647,8 @@ class WorkHistoryScreen extends StatelessWidget { onpressed: () { context.read().add( GetWorkHistories( - profileId: profileId, token: token!)); + profileId: profileId!, + token: token!)); }); } return Container(); diff --git a/lib/screens/profile/shared/multiple_attachment.dart b/lib/screens/profile/shared/multiple_attachment.dart new file mode 100644 index 0000000..a818bf7 --- /dev/null +++ b/lib/screens/profile/shared/multiple_attachment.dart @@ -0,0 +1,150 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:fluttericon/entypo_icons.dart'; + +import '../../../model/profile/attachment.dart'; +import '../../../theme-data.dart/box_shadow.dart'; +import '../../../theme-data.dart/colors.dart'; +import '../../../utils/global.dart'; + +class MultipleAttachments extends StatelessWidget { + final List attachments; + final String eligibilityName; + const MultipleAttachments({ + super.key, + required this.attachments, + required this.eligibilityName + }); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Flexible( + flex: + 2, + child: Container( + padding: const EdgeInsets.all(5), + decoration: box1().copyWith(color: Colors.grey.shade300, boxShadow: []), + child: Text( + attachments.first.filename!, + overflow: TextOverflow.ellipsis, + ))), + const SizedBox( + width: + 8, + ), + Flexible( + child: + FittedBox( + child: + Container( + padding: + const EdgeInsets.all(3), + decoration: + box1().copyWith(color: Colors.grey.shade300, boxShadow: []), + child: + InkWell( + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text("$eligibilityName Attachments",textAlign: TextAlign.center,), + content: Column( + mainAxisSize: MainAxisSize.min, + children: attachments.map((e) { + String ext = e.filename!.substring(e.filename!.lastIndexOf(".")); + + return Column( + children: [ + Row( + children: [ + Flexible( + child: SizedBox( + child: ext == '.pdf' + ? SvgPicture.asset( + 'assets/svgs/pdf.svg', + height: blockSizeVertical * 5, + allowDrawingOutsideViewBox: true, + ) + : ext == '.png' + ? SvgPicture.asset( + 'assets/svgs/png.svg', + height: blockSizeVertical * 5.5, + allowDrawingOutsideViewBox: true, + ) + : ext == '.jpg' + ? SvgPicture.asset( + 'assets/svgs/jpg.svg', + height: blockSizeVertical * 5, + allowDrawingOutsideViewBox: true, + ) + : SvgPicture.asset( + 'assets/svgs/jpg.svg', + height: blockSizeVertical * 5, + allowDrawingOutsideViewBox: true, + ), + ), + ), + const SizedBox(width: 8,), + Flexible( + flex: 4, + child: Tooltip( + message: e.filename, + child: Text( + e.filename!, + overflow: TextOverflow.ellipsis, + )), + ), + const SizedBox(width: 8,), + Flexible( + child: Row( + children: [ + Flexible( + child: IconButton( + icon: const Icon( + Entypo.right_open_mini, + color: Colors.black87, + ), + onPressed: () {}, + ), + ), + const SizedBox(width: 8,), + Flexible( + child: IconButton( + icon: const Icon( + Icons.delete, + color: primary, + ), + onPressed: () {}, + ), + ), + ], + ), + ), + ], + ), + const Divider() + ], + ); + }).toList(), + )); + }); + }, + child: Row( + children: const [ + Text(" See more.."), + Icon( + Icons.keyboard_arrow_right, + color: Colors.black54, + ), + ], + ), + ), + ), + )), + ], + ); + } +} diff --git a/lib/screens/profile/shared/single_attachment.dart b/lib/screens/profile/shared/single_attachment.dart new file mode 100644 index 0000000..92f46e7 --- /dev/null +++ b/lib/screens/profile/shared/single_attachment.dart @@ -0,0 +1,49 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +import '../../../model/profile/attachment.dart'; +import '../../../theme-data.dart/box_shadow.dart'; +import '../../../theme-data.dart/colors.dart'; +import '../../../utils/alerts.dart'; + +class SingleAttachment extends StatelessWidget { + final Function()? onpressed; + final Attachment attachment; + const SingleAttachment({ + required this.attachment, + required this.onpressed, + super.key, + }); + + @override + Widget build(BuildContext context) { + return Container( + padding: + const EdgeInsets.all( + 5), + decoration: box1().copyWith( + color: Colors + .grey + .shade300, + boxShadow: []), + child: + Row( + children: [ + Expanded( + child: + Text( + attachment.filename!, + overflow: TextOverflow.ellipsis, + ), + ), + const SizedBox( + width: + 8, + ), + GestureDetector( + onTap:onpressed, + child: const Icon(Icons.delete,color: primary,)) + ], + )); + } +} \ No newline at end of file diff --git a/lib/sevices/profile/eligibility_services.dart b/lib/sevices/profile/eligibility_services.dart index adfdb8d..9738e8a 100644 --- a/lib/sevices/profile/eligibility_services.dart +++ b/lib/sevices/profile/eligibility_services.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import 'package:unit2/model/profile/attachment.dart'; import 'package:unit2/model/profile/eligibility.dart'; import 'package:unit2/utils/request.dart'; import 'package:unit2/utils/urls.dart'; @@ -8,33 +9,33 @@ import 'package:http/http.dart' as http; class EligibilityService { static final EligibilityService _instance = EligibilityService(); static EligibilityService get instance => _instance; - - - Future> getEligibilities(int profileId, String token)async{ - List eligibilities = []; + + Future> getEligibilities( + int profileId, String token) async { + List eligibilities = []; String authToken = "Token $token"; - String path = "${Url.instance.getEligibilities()}$profileId/"; - Map headers = { + String path = "${Url.instance.getEligibilities()}$profileId/"; + Map headers = { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': authToken - }; - try{ - http.Response response = await Request.instance.getRequest(path: path,headers: headers,param: {}); - if(response.statusCode == 200){ - Map data = jsonDecode(response.body); - if (data['data']!= null) { - data['data'].forEach((var cert) { - EligibityCert eligibility = EligibityCert.fromJson(cert); - eligibilities.add(eligibility); - }); - } + try { + http.Response response = await Request.instance + .getRequest(path: path, headers: headers, param: {}); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + data['data'].forEach((var cert) { + EligibityCert eligibility = EligibityCert.fromJson(cert); + eligibilities.add(eligibility); + }); } - }catch(e){ - throw e.toString(); } - return eligibilities; + } catch (e) { + throw e.toString(); + } + return eligibilities; } Future delete( @@ -50,18 +51,17 @@ class EligibilityService { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': authtoken }; - try{ - http.Response response = await Request.instance - .deleteRequest(path: path, headers: headers, body: body, param: params); - if (response.statusCode == 200) { - Map data = jsonDecode(response.body); - success = data['success']; - } else { - success = false; - } - - }catch(e){ - throw(e.toString()); + try { + http.Response response = await Request.instance.deleteRequest( + path: path, headers: headers, body: body, param: params); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + success = data['success']; + } else { + success = false; + } + } catch (e) { + throw (e.toString()); } return success!; } @@ -70,7 +70,7 @@ class EligibilityService { {required EligibityCert eligibityCert, required String token, required int profileId}) async { - Map? _response={}; + Map? _response = {}; String authtoken = "Token $token"; String path = '${Url.instance.addEligibility()}$profileId/'; Map headers = { @@ -94,7 +94,7 @@ class EligibilityService { Map data = jsonDecode(response.body); _response = data; } else { - _response.addAll({'success':false}); + _response.addAll({'success': false}); } return _response; @@ -103,11 +103,12 @@ class EligibilityService { } } - Future> update( + Future> update( {required EligibityCert eligibityCert, required String token, - required int profileId, required int oldEligibility}) async { - Map? response={}; + required int profileId, + required int oldEligibility}) async { + Map? response = {}; String authtoken = "Token $token"; String path = '${Url.instance.addEligibility()}$profileId/'; Map headers = { @@ -132,7 +133,7 @@ class EligibilityService { Map data = jsonDecode(res.body); response = data; } else { - response.addAll({'success':false}); + response.addAll({'success': false}); } return response; @@ -140,4 +141,63 @@ class EligibilityService { throw e.toString(); } } + + + + Future deleteAttachment( + {required Attachment attachment, + required int moduleId, + required String profileId, + required String token}) async { + bool? success; + String authtoken = "Token $token"; + String path = "${Url.instance.attachments()}$profileId/"; + Map? body; + try{ + body = { + "attachment_module": moduleId, + attachment: [ + { + "id": attachment.id, + "created_at": attachment.createdAt, + "source": attachment.source, + "filename": attachment.filename, + "category": { + "id": attachment.category?.id, + "subclass": { + "id": attachment.category?.subclass.id, + "name": attachment.category?.subclass.name, + "attachment_class": { + "id": attachment.category?.subclass.attachmentClass.id, + "name": attachment.category?.subclass.attachmentClass.name + } + }, + "description": attachment.category?.description + } + } + ] + }; + }catch(e){ + print("body error"+e.toString()); + } + + Map params = {"force_mode": "true"}; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authtoken + }; + // try { + http.Response response = await Request.instance + .deleteRequest(path: path, headers: headers, body: body, param: params); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + success = data['success']; + } else { + success = false; + } + // } catch (e) { + // throw (e.toString()); + // } + return success!; + } } diff --git a/lib/utils/attachment_categories.dart b/lib/utils/attachment_categories.dart new file mode 100644 index 0000000..cce37ff --- /dev/null +++ b/lib/utils/attachment_categories.dart @@ -0,0 +1,77 @@ +import 'dart:convert'; + +import 'package:unit2/utils/request.dart'; +import 'package:unit2/utils/urls.dart'; + +import '../model/profile/attachment.dart'; +import 'package:http/http.dart' as http; + +class AttachmentServices { + static final AttachmentServices _instance = AttachmentServices(); + static AttachmentServices get instance => _instance; + + Future> getCategories() async { + List attachmentCategories = []; + String path = Url.instance.attachmentCategories(); + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + try { + http.Response response = await Request.instance + .getRequest(param: {}, path: path, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + for (var cat in data['data']) { + AttachmentCategory newCat = AttachmentCategory.fromJson(cat); + attachmentCategories.add(newCat); + } + } + } catch (e) { + throw e.toString(); + } + return attachmentCategories; + } + + Future> attachment( + {required String categoryId, + required String module, + required List paths, + required String token, + required String profileId}) async { + String authtoken = "Token $token"; + Map headers = {'Authorization': authtoken}; + String path = Url.instance.attachments(); + Map? response = {}; + Map body = { + "attachment_category_id": categoryId.toString(), + "attachment_module": module.toString() + }; + + try { + var request = http.MultipartRequest( + 'POST', Uri.parse('http://${Url.instance.host()}$path$profileId/')); + request.fields.addAll(body); + request.headers.addAll(headers); + paths.forEach((element) async { + request.files + .add(await http.MultipartFile.fromPath('attachments', element)); + }); + + http.StreamedResponse res = await request.send(); + final steamResponse = await res.stream.bytesToString(); + Map data = jsonDecode(steamResponse); + if (res.statusCode == 201) { + response = data; + } else { + String message = data['response']['details']; + response.addAll({'message': message}); + response.addAll( + {'success': false}, + ); + } + } catch (e) { + throw e.toString(); + } + return response; + } +} diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index 9669a61..bd6932e 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -167,6 +167,9 @@ class Url { String getServiceTypes() { return "/api/jobnet_app/comm_service_type/"; } + String attachments(){ + return "/api/jobnet_app/profile/attachment/"; + } //// address path String addressPath() { @@ -319,4 +322,7 @@ class Url { String getAddressCategory() { return "/api/jobnet_app/address_categories/"; } + String attachmentCategories(){ + return "/api/jobnet_app/attachment_categories/"; + } } diff --git a/pubspec.lock b/pubspec.lock index 9004766..fd4f759 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -449,6 +449,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.1.4" + file_picker: + dependency: "direct main" + description: + name: file_picker + sha256: "9d6e95ec73abbd31ec54d0e0df8a961017e165aba1395e462e5b31ea0c165daf" + url: "https://pub.dev" + source: hosted + version: "5.3.1" file_utils: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index fcaa5d5..c5a2cc0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -86,6 +86,7 @@ dependencies: flutter_staggered_animations: ^1.1.1 group_list_view: ^1.1.1 search_page: ^2.3.0 + file_picker: ^5.3.1 dev_dependencies: From 018f3760a5a16f5ceb2e5d9e65ec8430baec3968 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Mon, 7 Aug 2023 14:33:38 +0800 Subject: [PATCH 76/86] Adding attachments feature for profile --- .../profile/education/education_bloc.dart | 62 +- .../profile/education/education_event.dart | 13 +- .../profile/education/education_state.dart | 21 +- .../profile/eligibility/eligibility_bloc.dart | 53 +- .../eligibility/eligibility_event.dart | 11 +- .../eligibility/eligibility_state.dart | 18 +- .../learning_development_bloc.dart | 37 +- .../learning_development_event.dart | 15 +- .../learning_development_state.dart | 24 +- .../voluntary_works/voluntary_work_state.dart | 6 + .../profile/workHistory/workHistory_bloc.dart | 59 +- .../workHistory/workHistory_event.dart | 16 +- .../workHistory/workHistory_state.dart | 19 + lib/main.dart | 11 - lib/model/profile/attachment.dart | 28 +- lib/model/profile/educational_background.dart | 2 +- lib/model/profile/eligibility.dart | 2 +- lib/model/profile/learning_development.dart | 2 +- lib/model/profile/work_history.dart | 2 +- .../profile/components/education_screen.dart | 185 +++- .../components/eligibility_screen.dart | 857 ++++++++++-------- .../learning_and_development_screen.dart | 692 ++++++++------ .../components/work_history_screen.dart | 162 +++- .../profile/shared/multiple_attachment.dart | 332 ++++--- .../profile/shared/single_attachment.dart | 6 +- lib/sevices/profile/eligibility_services.dart | 8 +- ...tegories.dart => attachment_services.dart} | 55 +- lib/utils/urls.dart | 6 +- 28 files changed, 1741 insertions(+), 963 deletions(-) rename lib/utils/{attachment_categories.dart => attachment_services.dart} (58%) diff --git a/lib/bloc/profile/education/education_bloc.dart b/lib/bloc/profile/education/education_bloc.dart index cb43d54..0117b6d 100644 --- a/lib/bloc/profile/education/education_bloc.dart +++ b/lib/bloc/profile/education/education_bloc.dart @@ -4,7 +4,7 @@ import 'package:unit2/model/profile/educational_background.dart'; import 'package:unit2/sevices/profile/education_services.dart'; import '../../../model/profile/attachment.dart'; -import '../../../utils/attachment_categories.dart'; +import '../../../utils/attachment_services.dart'; part 'education_event.dart'; part 'education_state.dart'; @@ -14,12 +14,12 @@ class EducationBloc extends Bloc { List schools = []; List programs = []; List honors = []; - List attachmentCategories = []; + List attachmentCategories = []; EducationBloc() : super(EducationInitial()) { on((event, emit) async { emit(EducationalBackgroundLoadingState()); try { - if (attachmentCategories.isEmpty) { + if (attachmentCategories.isEmpty) { attachmentCategories = await AttachmentServices.instance.getCategories(); } @@ -28,11 +28,13 @@ class EducationBloc extends Bloc { .instace .getEducationalBackground(event.profileId, event.token); educationalBackgrounds = educations; - emit(EducationalBackgroundLoadedState( - educationalBackground: educationalBackgrounds, attachmentCategory: attachmentCategories)); + emit(EducationalBackgroundLoadedState( + educationalBackground: educationalBackgrounds, + attachmentCategory: attachmentCategories)); } else { emit(EducationalBackgroundLoadedState( - educationalBackground: educationalBackgrounds,attachmentCategory: attachmentCategories)); + educationalBackground: educationalBackgrounds, + attachmentCategory: attachmentCategories)); } } catch (e) { emit(EducationalBackgroundErrorState(message: e.toString())); @@ -85,7 +87,8 @@ class EducationBloc extends Bloc { token: event.token, profileId: event.profileId); if (status['success']) { - educationalBackgrounds.removeWhere((element) => event.educationalBackground.id == element.id); + educationalBackgrounds.removeWhere( + (element) => event.educationalBackground.id == element.id); EducationalBackground educationalBackground = EducationalBackground.fromJson(status['data']); educationalBackgrounds.add(educationalBackground); @@ -97,7 +100,8 @@ class EducationBloc extends Bloc { ////LOAD on((event, emit) { emit(EducationalBackgroundLoadedState( - educationalBackground: educationalBackgrounds,attachmentCategory: attachmentCategories)); + educationalBackground: educationalBackgrounds, + attachmentCategory: attachmentCategories)); }); //// SHOW EDIT FORM on((event, emit) async { @@ -143,8 +147,12 @@ class EducationBloc extends Bloc { } }); ////Add attachment - on((event, emit) async { + on((event, emit) async { emit(EducationalBackgroundLoadingState()); + EducationalBackground educationalBackground = + educationalBackgrounds.firstWhere( + (element) => element.id.toString() == event.attachmentModule); + List attachments = []; try { Map status = await AttachmentServices.instance .attachment( @@ -153,7 +161,18 @@ class EducationBloc extends Bloc { paths: event.filePaths, token: event.token, profileId: event.profileId); + if (status['success']) { + status['data'].forEach((element) { + Attachment newAttachment = Attachment.fromJson(element); + attachments.add(newAttachment); + }); + educationalBackground.attachments == null + ? educationalBackground.attachments = attachments + : educationalBackground.attachments = [ + ...educationalBackground.attachments!, + ...attachments + ]; emit(EducationAddedState(response: status)); } else { emit(EducationAddedState(response: status)); @@ -162,5 +181,30 @@ class EducationBloc extends Bloc { emit(EducationalBackgroundErrorState(message: e.toString())); } }); + on((event, emit) async { + emit(EducationalBackgroundLoadingState()); + try { + final bool success = await AttachmentServices.instance.deleteAttachment( + attachment: event.attachment, + moduleId: event.moduleId, + profileId: event.profileId.toString(), + token: event.token); + if (success) { + final EducationalBackground educationalBackground = + educationalBackgrounds + .firstWhere((element) => element.id == event.moduleId); + educationalBackground.attachments + ?.removeWhere((element) => element.id == event.attachment.id); + educationalBackgrounds + .removeWhere((element) => element.id == event.moduleId); + educationalBackgrounds.add(educationalBackground); + emit(EducationAttachmentDeletedState(success: success)); + } else { + emit(EducationAttachmentDeletedState(success: success)); + } + } catch (e) { + emit(EducationalBackgroundErrorState(message: e.toString())); + } + }); } } diff --git a/lib/bloc/profile/education/education_event.dart b/lib/bloc/profile/education/education_event.dart index 667befb..9b68a0f 100644 --- a/lib/bloc/profile/education/education_event.dart +++ b/lib/bloc/profile/education/education_event.dart @@ -64,13 +64,22 @@ class DeleteEducation extends EducationEvent{ List get props => [educationalBackground, profileId, token]; } ////Add attachment -class AddAttachment extends EducationEvent{ +class AddEducationAttachment extends EducationEvent{ final String categoryId; final String attachmentModule; final List filePaths; final String token; final String profileId; - const AddAttachment({required this.attachmentModule, required this.filePaths, required this.categoryId, required this.profileId, required this.token}); + const AddEducationAttachment({required this.attachmentModule, required this.filePaths, required this.categoryId, required this.profileId, required this.token}); @override List get props => [categoryId,attachmentModule,filePaths, token,profileId]; } + +////Delete Attachment +class DeleteEducationAttachment extends EducationEvent{ + final int moduleId; + final Attachment attachment; + final String token; + final int profileId; + const DeleteEducationAttachment({required this.attachment, required this.moduleId, required this.profileId, required this.token}); +} diff --git a/lib/bloc/profile/education/education_state.dart b/lib/bloc/profile/education/education_state.dart index a8bebe0..3cfe505 100644 --- a/lib/bloc/profile/education/education_state.dart +++ b/lib/bloc/profile/education/education_state.dart @@ -10,9 +10,10 @@ abstract class EducationState extends Equatable { class EducationInitial extends EducationState {} class EducationalBackgroundLoadedState extends EducationState { - final List< AttachmentCategory> attachmentCategory; + final List attachmentCategory; final List educationalBackground; - const EducationalBackgroundLoadedState({required this.educationalBackground, required this.attachmentCategory}); + const EducationalBackgroundLoadedState( + {required this.educationalBackground, required this.attachmentCategory}); @override List get props => [educationalBackground]; } @@ -58,6 +59,8 @@ class EducationAddedState extends EducationState { @override List get props => [response]; } + + //// Edited State class EditedEducationState extends EducationState { final Map response; @@ -73,3 +76,17 @@ class EducationDeletedState extends EducationState { @override List get props => [success]; } + +////Attachment AddedState +class EducationAttachmentAddedState extends EducationState { + final Map response; + const EducationAttachmentAddedState({required this.response}); +} + +////Attachment Deleted State State +class EducationAttachmentDeletedState extends EducationState { + final bool success; + const EducationAttachmentDeletedState({required this.success}); + @override + List get props => [success]; +} \ No newline at end of file diff --git a/lib/bloc/profile/eligibility/eligibility_bloc.dart b/lib/bloc/profile/eligibility/eligibility_bloc.dart index c47b309..1be36b4 100644 --- a/lib/bloc/profile/eligibility/eligibility_bloc.dart +++ b/lib/bloc/profile/eligibility/eligibility_bloc.dart @@ -1,6 +1,6 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; -import 'package:unit2/utils/attachment_categories.dart'; +import 'package:unit2/utils/attachment_services.dart'; import '../../../model/location/city.dart'; import '../../../model/location/country.dart'; import '../../../model/location/provinces.dart'; @@ -39,11 +39,11 @@ class EligibilityBloc extends Bloc { if (success) { eligibilities.removeWhere( ((EligibityCert element) => element.id == event.eligibilityId)); - emit(DeletedState( + emit(EligibilityDeletedState( success: success, )); } else { - emit(DeletedState(success: success)); + emit(EligibilityDeletedState(success: success)); } } catch (e) { emit(EligibilityErrorState(message: e.toString())); @@ -222,8 +222,11 @@ class EligibilityBloc extends Bloc { message: "Something went wrong. Please try again")); }); ////Add attachment - on((event, emit) async { + on((event, emit) async { emit(EligibilityLoadingState()); + List attachments = []; + EligibityCert eligibityCert = eligibilities.firstWhere( + (element) => element.id.toString() == event.attachmentModule); try { Map status = await AttachmentServices.instance .attachment( @@ -233,32 +236,44 @@ class EligibilityBloc extends Bloc { token: event.token, profileId: event.profileId); if (status['success']) { - emit(EligibilityAddedState(response: status)); + status['data'].forEach((element) { + Attachment newAttachment = Attachment.fromJson(element); + attachments.add(newAttachment); + }); + eligibityCert.attachments == null + ? eligibityCert.attachments = attachments + : eligibityCert.attachments = [ + ...eligibityCert.attachments!, + ...attachments + ]; + emit(EligibilityAttachmentAddedState(response: status)); } else { - emit(EligibilityAddedState(response: status)); + emit(EligibilityAttachmentAddedState(response: status)); } } catch (e) { emit(EligibilityErrorState(message: e.toString())); } }); - - on((event, emit) async { + + on((event, emit) async { + emit(EligibilityLoadingState()); // try { - final bool success = await EligibilityService.instance.deleteAttachment( - moduleId: int.parse(event.moduleId), + final bool success = await AttachmentServices.instance.deleteAttachment( attachment: event.attachment, - profileId: event.profileId, + moduleId: int.parse(event.moduleId), + profileId: event.profileId.toString(), token: event.token); if (success) { - EligibityCert eligibityCert = eligibilities.firstWhere((element) =>element.id.toString() == event.moduleId); - eligibityCert.attachments!.removeWhere((element){ - return event.attachment.id == element.id; - }); - emit(DeletedState( - success: success, - )); + final EligibityCert eligibityCert = eligibilities + .firstWhere((element) => element.id.toString() == event.moduleId); + eligibityCert.attachments + ?.removeWhere((element) => element.id == event.attachment.id); + eligibilities.removeWhere( + (element) => element.id.toString() == event.moduleId); + eligibilities.add(eligibityCert); + emit(EligibilitytAttachmentDeletedState(success: success)); } else { - emit(DeletedState(success: success)); + emit(EligibilitytAttachmentDeletedState(success: success)); } // } catch (e) { // emit(EligibilityErrorState(message: e.toString())); diff --git a/lib/bloc/profile/eligibility/eligibility_event.dart b/lib/bloc/profile/eligibility/eligibility_event.dart index 8d17236..a9b6ca8 100644 --- a/lib/bloc/profile/eligibility/eligibility_event.dart +++ b/lib/bloc/profile/eligibility/eligibility_event.dart @@ -68,24 +68,25 @@ class DeleteEligibility extends EligibilityEvent { class CallErrorState extends EligibilityEvent{ } - -class AddAttachment extends EligibilityEvent{ +////Add Attachment +class AddEligibiltyAttachment extends EligibilityEvent{ final String categoryId; final String attachmentModule; final List filePaths; final String token; final String profileId; - const AddAttachment({required this.attachmentModule, required this.filePaths, required this.categoryId, required this.profileId, required this.token}); + const AddEligibiltyAttachment({required this.attachmentModule, required this.filePaths, required this.categoryId, required this.profileId, required this.token}); @override List get props => [categoryId,attachmentModule,filePaths, token,profileId]; } -class DeleteAttachment extends EligibilityEvent{ +////Delete Attachment +class DeleteEligibyAttachment extends EligibilityEvent{ final String profileId; final String token; final Attachment attachment; final String moduleId; - const DeleteAttachment({required this.attachment,required this.moduleId, required this.profileId, required this.token}); + const DeleteEligibyAttachment({required this.attachment,required this.moduleId, required this.profileId, required this.token}); } diff --git a/lib/bloc/profile/eligibility/eligibility_state.dart b/lib/bloc/profile/eligibility/eligibility_state.dart index 3a1994a..7d746d9 100644 --- a/lib/bloc/profile/eligibility/eligibility_state.dart +++ b/lib/bloc/profile/eligibility/eligibility_state.dart @@ -42,9 +42,9 @@ class EditEligibilityState extends EligibilityState { [isOverseas, eligibityCert, eligibilities, regions, countries]; } -class DeletedState extends EligibilityState { +class EligibilityDeletedState extends EligibilityState { final bool success; - const DeletedState({required this.success}); + const EligibilityDeletedState({required this.success}); @override List get props => [success]; } @@ -93,3 +93,17 @@ class EligibilityLoaded extends EligibilityState { @override List get props => [eligibilities]; } + +////Attachment AddedState +class EligibilityAttachmentAddedState extends EligibilityState { + final Map response; + const EligibilityAttachmentAddedState({required this.response}); +} + +////Attachment Deleted State State +class EligibilitytAttachmentDeletedState extends EligibilityState { + final bool success; + const EligibilitytAttachmentDeletedState({required this.success}); + @override + List get props => [success]; +} diff --git a/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart b/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart index e7c145d..465cefb 100644 --- a/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart +++ b/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart @@ -10,7 +10,7 @@ import '../../../model/profile/attachment.dart'; import '../../../model/profile/learning_development.dart'; import '../../../model/utils/agency.dart'; import '../../../model/utils/category.dart'; -import '../../../utils/attachment_categories.dart'; +import '../../../utils/attachment_services.dart'; import '../../../utils/location_utilities.dart'; import '../../../utils/profile_utilities.dart'; part 'learning_development_event.dart'; @@ -273,8 +273,10 @@ class LearningDevelopmentBloc emit(LearningDevelopmentErrorState(message: event.message)); }); ////Add attachment - on((event, emit) async { + on((event, emit) async { emit(LearningDevelopmentLoadingState()); + List attachments = []; + LearningDevelopement learningDevelopement = learningsAndDevelopments.firstWhere((element) => element.conductedTraining!.id.toString () == event.attachmentModule); try { Map status = await AttachmentServices.instance .attachment( @@ -284,6 +286,11 @@ class LearningDevelopmentBloc token: event.token, profileId: event.profileId); if (status['success']) { + status['data'].forEach((element){ + Attachment newAttachment = Attachment.fromJson(element); + attachments.add(newAttachment); + }); + learningDevelopement.attachments == null? learningDevelopement.attachments = attachments:learningDevelopement.attachments = [...learningDevelopement.attachments!,...attachments]; emit(LearningDevelopmentAddedState(response: status)); } else { emit(LearningDevelopmentAddedState(response: status)); @@ -292,5 +299,31 @@ class LearningDevelopmentBloc emit(LearningDevelopmentErrorState(message: e.toString())); } }); + ////Delete Attachment + on((event, emit) async { + emit(LearningDevelopmentLoadingState()); + try { + final bool success = await AttachmentServices.instance.deleteAttachment( + attachment: event.attachment, + moduleId: event.moduleId, + profileId: event.profileId.toString(), + token: event.token); + if (success) { + final LearningDevelopement learningDevelopement = + learningsAndDevelopments + .firstWhere((element) => element.conductedTraining!.id == event.moduleId); + learningDevelopement.attachments + ?.removeWhere((element) => element.id == event.attachment.id); + learningsAndDevelopments + .removeWhere((element) => element.conductedTraining!.id == event.moduleId); + learningsAndDevelopments.add(learningDevelopement); + emit(LearningDevAttachmentDeletedState(success: success)); + } else { + emit(LearningDevAttachmentDeletedState(success: success)); + } + } catch (e) { + emit(LearningDevelopmentErrorState(message: e.toString())); + } + }); } } diff --git a/lib/bloc/profile/learningDevelopment/learning_development_event.dart b/lib/bloc/profile/learningDevelopment/learning_development_event.dart index cfde6bb..c8528ac 100644 --- a/lib/bloc/profile/learningDevelopment/learning_development_event.dart +++ b/lib/bloc/profile/learningDevelopment/learning_development_event.dart @@ -67,13 +67,24 @@ class CallErrorState extends LearningDevelopmentEvent{ const CallErrorState({required this.message}); } -class AddAttachment extends LearningDevelopmentEvent{ +////Add Attachment +class AddALearningDevttachment extends LearningDevelopmentEvent{ final String categoryId; final String attachmentModule; final List filePaths; final String token; final String profileId; - const AddAttachment({required this.attachmentModule, required this.filePaths, required this.categoryId, required this.profileId, required this.token}); + const AddALearningDevttachment({required this.attachmentModule, required this.filePaths, required this.categoryId, required this.profileId, required this.token}); @override List get props => [categoryId,attachmentModule,filePaths, token,profileId]; } + +//// Delete Attachment +class DeleteLearningDevAttachment extends LearningDevelopmentEvent{ + final int moduleId; + final Attachment attachment; + final String token; + final int profileId; + const DeleteLearningDevAttachment({required this.attachment, required this.moduleId, required this.profileId, required this.token}); +} + diff --git a/lib/bloc/profile/learningDevelopment/learning_development_state.dart b/lib/bloc/profile/learningDevelopment/learning_development_state.dart index 323d076..2699470 100644 --- a/lib/bloc/profile/learningDevelopment/learning_development_state.dart +++ b/lib/bloc/profile/learningDevelopment/learning_development_state.dart @@ -10,9 +10,11 @@ abstract class LearningDevelopmentState extends Equatable { class LearningDevelopmentInitial extends LearningDevelopmentState {} class LearningDevelopmentLoadedState extends LearningDevelopmentState { - final List< AttachmentCategory> attachmentCategory; + final List attachmentCategory; final List learningsAndDevelopment; - const LearningDevelopmentLoadedState({required this.learningsAndDevelopment, required this.attachmentCategory}); + const LearningDevelopmentLoadedState( + {required this.learningsAndDevelopment, + required this.attachmentCategory}); @override List get props => [learningsAndDevelopment]; } @@ -113,3 +115,21 @@ class LearningDevelopmentErrorState extends LearningDevelopmentState { final String message; const LearningDevelopmentErrorState({required this.message}); } + +////Attachment AddedState +class LearningDevAttachmentAddedState extends LearningDevelopmentState { + final Map response; + const LearningDevAttachmentAddedState({required this.response}); + @override + List get props => [response]; +} + + + +////Attachment Deleted State State +class LearningDevAttachmentDeletedState extends LearningDevelopmentState { + final bool success; + const LearningDevAttachmentDeletedState({required this.success}); + @override + List get props => [success]; +} diff --git a/lib/bloc/profile/voluntary_works/voluntary_work_state.dart b/lib/bloc/profile/voluntary_works/voluntary_work_state.dart index efa0fd7..4c3fc88 100644 --- a/lib/bloc/profile/voluntary_works/voluntary_work_state.dart +++ b/lib/bloc/profile/voluntary_works/voluntary_work_state.dart @@ -60,6 +60,7 @@ class EditVoluntaryWorks extends VoluntaryWorkState{ } + ////Adding State class AddVoluntaryWorkState extends VoluntaryWorkState{ final List positions; @@ -71,6 +72,11 @@ class AddVoluntaryWorkState extends VoluntaryWorkState{ @override List get props => [positions,agencies,countries,regions]; } +////Add Attachment +class AttachmentAddedState extends VoluntaryWorkState { + final Map response; + const AttachmentAddedState({required this.response}); +} //// Deleted State class VoluntaryWorkDeletedState extends VoluntaryWorkState{ final bool success; diff --git a/lib/bloc/profile/workHistory/workHistory_bloc.dart b/lib/bloc/profile/workHistory/workHistory_bloc.dart index 71f81ce..e9a44c8 100644 --- a/lib/bloc/profile/workHistory/workHistory_bloc.dart +++ b/lib/bloc/profile/workHistory/workHistory_bloc.dart @@ -10,7 +10,7 @@ import 'package:unit2/utils/profile_utilities.dart'; import '../../../model/profile/attachment.dart'; import '../../../model/utils/category.dart'; -import '../../../utils/attachment_categories.dart'; +import '../../../utils/attachment_services.dart'; part 'workHistory_event.dart'; part 'workHistory_state.dart'; @@ -21,13 +21,13 @@ class WorkHistoryBloc extends Bloc { List agencies = []; List appointmentStatus = []; List agencyCategory = []; - List attachmentCategories = []; + List attachmentCategories = []; WorkHistoryBloc() : super(EducationInitial()) { ////GET WORK HISTORIES on((event, emit) async { emit(WorkHistoryLoadingState()); try { - if (attachmentCategories.isEmpty) { + if (attachmentCategories.isEmpty) { attachmentCategories = await AttachmentServices.instance.getCategories(); } @@ -36,7 +36,9 @@ class WorkHistoryBloc extends Bloc { .getWorkExperiences(event.profileId, event.token); workExperiences = works; } - emit(WorkHistoryLoaded(workExperiences: workExperiences, attachmentCategory: attachmentCategories)); + emit(WorkHistoryLoaded( + workExperiences: workExperiences, + attachmentCategory: attachmentCategories)); } catch (e) { emit(WorkHistoryErrorState(message: e.toString())); } @@ -44,7 +46,9 @@ class WorkHistoryBloc extends Bloc { ///// LOAD WORK HISTORIES on((event, emit) { emit(WorkHistoryLoadingState()); - emit(WorkHistoryLoaded(workExperiences: workExperiences,attachmentCategory: attachmentCategories)); + emit(WorkHistoryLoaded( + workExperiences: workExperiences, + attachmentCategory: attachmentCategories)); }); ////DELETE on((event, emit) async { @@ -190,8 +194,11 @@ class WorkHistoryBloc extends Bloc { } }); ////Add Attachment - on((event, emit) async { + on((event, emit) async { emit(WorkHistoryLoadingState()); + List attachments = []; + WorkHistory workHistory = workExperiences.firstWhere( + (element) => element.id.toString() == event.attachmentModule); try { Map status = await AttachmentServices.instance .attachment( @@ -201,9 +208,45 @@ class WorkHistoryBloc extends Bloc { token: event.token, profileId: event.profileId); if (status['success']) { - emit(WorkHistoryAddedState(response: status)); + status['data'].forEach((element) { + Attachment newAttachment = Attachment.fromJson(element); + attachments.add(newAttachment); + }); + workHistory.attachments == null + ? workHistory.attachments = attachments + : workHistory.attachments = [ + ...workHistory.attachments!, + ...attachments + ]; + emit(WorkHistoryDevAttachmentAddedState(response: status)); } else { - emit(WorkHistoryAddedState(response: status)); + emit(WorkHistoryDevAttachmentAddedState(response: status)); + } + } catch (e) { + emit(WorkHistoryErrorState(message: e.toString())); + } + }); + ////Delete Attachment + on((event, emit) async { + emit(WorkHistoryLoadingState()); + try { + final bool success = await AttachmentServices.instance.deleteAttachment( + attachment: event.attachment, + moduleId: event.moduleId, + profileId: event.profileId.toString(), + token: event.token); + if (success) { + final WorkHistory workHistory = + workExperiences + .firstWhere((element) => element.id == event.moduleId); + workHistory.attachments + ?.removeWhere((element) => element.id == event.attachment.id); + workExperiences + .removeWhere((element) => element.id == event.moduleId); + workExperiences.add(workHistory); + emit(WorkHistoryDevAttachmentDeletedState(success: success)); + } else { + emit(WorkHistoryDevAttachmentDeletedState(success: success)); } } catch (e) { emit(WorkHistoryErrorState(message: e.toString())); diff --git a/lib/bloc/profile/workHistory/workHistory_event.dart b/lib/bloc/profile/workHistory/workHistory_event.dart index ebc1efd..1895bfe 100644 --- a/lib/bloc/profile/workHistory/workHistory_event.dart +++ b/lib/bloc/profile/workHistory/workHistory_event.dart @@ -60,15 +60,27 @@ class AddWorkHostory extends WorkHistorytEvent{ @override List get props => [workHistory,profileId,token,isPrivate]; } -class AddAttachment extends WorkHistorytEvent{ + +////Add Attachment +class AddWorkHistoryAttachment extends WorkHistorytEvent{ final String categoryId; final String attachmentModule; final List filePaths; final String token; final String profileId; - const AddAttachment({required this.attachmentModule, required this.filePaths, required this.categoryId, required this.profileId, required this.token}); + const AddWorkHistoryAttachment({required this.attachmentModule, required this.filePaths, required this.categoryId, required this.profileId, required this.token}); @override List get props => [categoryId,attachmentModule,filePaths, token,profileId]; } +////Delete Attachment +class DeleteWorkHistoryAttachment extends WorkHistorytEvent{ + final int moduleId; + final Attachment attachment; + final String token; + final int profileId; + const DeleteWorkHistoryAttachment({required this.attachment, required this.moduleId, required this.profileId, required this.token}); +} + + diff --git a/lib/bloc/profile/workHistory/workHistory_state.dart b/lib/bloc/profile/workHistory/workHistory_state.dart index d5905a8..117d984 100644 --- a/lib/bloc/profile/workHistory/workHistory_state.dart +++ b/lib/bloc/profile/workHistory/workHistory_state.dart @@ -73,3 +73,22 @@ class WorkHistoryAddedState extends WorkHistoryState{ @override List get props => [response]; } + + +////Attachment AddedState +class WorkHistoryDevAttachmentAddedState extends WorkHistoryState { + final Map response; + const WorkHistoryDevAttachmentAddedState({required this.response}); + @override + List get props => [response]; +} + + + +////Attachment Deleted State State +class WorkHistoryDevAttachmentDeletedState extends WorkHistoryState { + final bool success; + const WorkHistoryDevAttachmentDeletedState({required this.success}); + @override + List get props => [success]; +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 5144a73..d88fa6c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,24 +1,14 @@ -import 'dart:io'; - -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:device_preview/device_preview.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:hive/hive.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; -import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/app_router.dart'; import 'package:unit2/utils/global_context.dart'; -import 'package:unit2/utils/global_context.dart'; import 'package:path_provider/path_provider.dart' as path_provider; -import './utils/router.dart'; import './utils/global.dart'; - - Future main() async { WidgetsFlutterBinding.ensureInitialized(); var appDirectory = await path_provider.getApplicationDocumentsDirectory(); @@ -92,4 +82,3 @@ class MyApp extends StatelessWidget { ); } } - diff --git a/lib/model/profile/attachment.dart b/lib/model/profile/attachment.dart index 17169e2..f0676f8 100644 --- a/lib/model/profile/attachment.dart +++ b/lib/model/profile/attachment.dart @@ -1,8 +1,4 @@ -// To parse this JSON data, do -// -// final attachment = attachmentFromJson(jsonString); -import 'package:meta/meta.dart'; import 'dart:convert'; Attachment attachmentFromJson(String str) => @@ -53,9 +49,9 @@ class Attachment { } class AttachmentCategory { - final int id; - final Subclass subclass; - final String description; + final int? id; + final Subclass? subclass; + final String? description; AttachmentCategory({ required this.id, @@ -65,21 +61,21 @@ class AttachmentCategory { factory AttachmentCategory.fromJson(Map json) => AttachmentCategory( id: json["id"], - subclass: Subclass.fromJson(json["subclass"]), + subclass:json['subclass'] == null? null: Subclass.fromJson(json["subclass"]), description: json["description"], ); Map toJson() => { "id": id, - "subclass": subclass.toJson(), + "subclass": subclass?.toJson(), "description": description, }; } class Subclass { - final int id; - final String name; - final AttachmentClass attachmentClass; + final int? id; + final String? name; + final AttachmentClass? attachmentClass; Subclass({ required this.id, @@ -90,19 +86,19 @@ class Subclass { factory Subclass.fromJson(Map json) => Subclass( id: json["id"], name: json["name"], - attachmentClass: AttachmentClass.fromJson(json["attachment_class"]), + attachmentClass: json['attachment_class'] == null? null: AttachmentClass.fromJson(json["attachment_class"]), ); Map toJson() => { "id": id, "name": name, - "attachment_class": attachmentClass.toJson(), + "attachment_class": attachmentClass?.toJson(), }; } class AttachmentClass { - final int id; - final String name; + final int? id; + final String? name; AttachmentClass({ required this.id, diff --git a/lib/model/profile/educational_background.dart b/lib/model/profile/educational_background.dart index ef061a0..843aed0 100644 --- a/lib/model/profile/educational_background.dart +++ b/lib/model/profile/educational_background.dart @@ -26,7 +26,7 @@ class EducationalBackground { final List? honors; final Education? education; final String? periodTo; - final List? attachments; + List? attachments; final String? periodFrom; final int? unitsEarned; final String? yearGraduated; diff --git a/lib/model/profile/eligibility.dart b/lib/model/profile/eligibility.dart index 96dbb54..5a8f7c7 100644 --- a/lib/model/profile/eligibility.dart +++ b/lib/model/profile/eligibility.dart @@ -34,7 +34,7 @@ class EligibityCert { final int? id; final double? rating; final DateTime? examDate; - final List? attachments; + List? attachments; final Eligibility? eligibility; final ExamAddress? examAddress; diff --git a/lib/model/profile/learning_development.dart b/lib/model/profile/learning_development.dart index 17362f4..58fa2ed 100644 --- a/lib/model/profile/learning_development.dart +++ b/lib/model/profile/learning_development.dart @@ -24,7 +24,7 @@ class LearningDevelopement { this.totalHoursAttended, }); - final List? attachments; + List? attachments; final Agency? sponsoredBy; final ConductedTraining? conductedTraining; final double? totalHoursAttended; diff --git a/lib/model/profile/work_history.dart b/lib/model/profile/work_history.dart index 076cf47..b479a24 100644 --- a/lib/model/profile/work_history.dart +++ b/lib/model/profile/work_history.dart @@ -34,7 +34,7 @@ class WorkHistory { final DateTime? toDate; final Position? position; final DateTime? fromDate; - final List? attachments; + List? attachments; final int? salaryGrade; final double? monthlySalary; final String? appointmentStatus; diff --git a/lib/screens/profile/components/education_screen.dart b/lib/screens/profile/components/education_screen.dart index 8f8363f..fb32a9a 100644 --- a/lib/screens/profile/components/education_screen.dart +++ b/lib/screens/profile/components/education_screen.dart @@ -7,7 +7,10 @@ import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_svg/svg.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:unit2/bloc/profile/eligibility/eligibility_bloc.dart'; +import 'package:unit2/bloc/profile/learningDevelopment/learning_development_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/profile/workHistory/workHistory_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/educational_background.dart'; import 'package:unit2/screens/profile/components/education/add_modal.dart'; @@ -16,7 +19,6 @@ import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; - import '../../../bloc/profile/education/education_bloc.dart'; import '../../../model/profile/attachment.dart'; import '../../../theme-data.dart/btn-style.dart'; @@ -33,7 +35,8 @@ class EducationScreen extends StatelessWidget { @override Widget build(BuildContext context) { final parent = context; - FilePickerResult? result; + + List? results = []; AttachmentCategory? selectedAttachmentCategory; List attachmentCategories = []; int profileId; @@ -115,6 +118,28 @@ class EducationScreen extends StatelessWidget { }); } } + ////ATTACHMENT ADDED STATE + + if (state is EducationAttachmentAddedState) { + if (state.response['success']) { + successAlert(context, "Adding Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context + .read() + .add(LoadEducations()); + }); + } else { + errorAlert(context, "Adding Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context + .read() + .add(LoadEducations()); + }); + } + } ////EDITED STATE if (state is EditedEducationState) { if (state.response['success']) { @@ -157,11 +182,32 @@ class EducationScreen extends StatelessWidget { }); } } + ////ATTACHMENT DELETED STATE + if (state is EducationAttachmentDeletedState) { + if (state.success) { + successAlert(context, "Deletion Successfull", + "Attachment has been deleted successfully", + () { + Navigator.of(context).pop(); + context + .read() + .add(LoadEducations()); + }); + } else { + errorAlert(context, "Deletion Failed", + "Error deleting Attachment", () { + Navigator.of(context).pop(); + context + .read() + .add(LoadEducations()); + }); + } + } }, builder: (context, state) { if (state is EducationalBackgroundLoadedState) { for (var cat in state.attachmentCategory) { - if (cat.subclass.id == 1) { + if (cat.subclass!.id == 1) { attachmentCategories.add(cat); } } @@ -352,6 +398,7 @@ class EducationScreen extends StatelessWidget { index])); } if (value == 3) { + results.clear(); showDialog( context: context, builder: @@ -423,7 +470,7 @@ class EducationScreen extends StatelessWidget { selectedAttachmentCategory = value; }, items: attachmentCategories.map((e) { - return DropdownMenuItem(value: e, child: Text(e.description)); + return DropdownMenuItem(value: e, child: Text(e.description!)); }).toList()), const SizedBox( height: @@ -463,7 +510,9 @@ class EducationScreen extends StatelessWidget { ]); setState(() { if (newResult != null) { - result = newResult; + newResult.files.forEach((element) { + results.add(element); + }); } }); }, @@ -479,13 +528,13 @@ class EducationScreen extends StatelessWidget { SizedBox( height: 100, width: double.maxFinite, - child: result == null + child: results.isEmpty ? const SizedBox() : Expanded( child: ListView.builder( - itemCount: result!.files.length, + itemCount: results.length, itemBuilder: (BuildContext context, index) { - final kb = result!.files[index].size / 1024; + final kb = results[index].size / 1024; final mb = kb / 1024; final size = mb >= 1 ? '${mb.toStringAsFixed(2)}MB' : '${kb.toStringAsFixed(2)}KB'; return Column( @@ -497,19 +546,19 @@ class EducationScreen extends StatelessWidget { children: [ Flexible( child: SizedBox( - child: result!.files[index].extension!.toLowerCase() == 'pdf' + child: results[index].extension!.toLowerCase() == 'pdf' ? SvgPicture.asset( 'assets/svgs/pdf.svg', height: blockSizeVertical * 3, allowDrawingOutsideViewBox: true, ) - : result!.files[index].extension!.toLowerCase() == 'png' + : results[index].extension!.toLowerCase() == 'png' ? SvgPicture.asset( 'assets/svgs/png.svg', height: blockSizeVertical * 3, allowDrawingOutsideViewBox: true, ) - : result!.files[index].extension!.toLowerCase() == 'jpg' || result!.files[index].extension!.toLowerCase() == 'jpeg' + : results[index].extension!.toLowerCase() == 'jpg' || results[index].extension!.toLowerCase() == 'jpeg' ? SvgPicture.asset( 'assets/svgs/jpg.svg', height: blockSizeVertical * 3, @@ -519,18 +568,36 @@ class EducationScreen extends StatelessWidget { const SizedBox( width: 12, ), - Flexible( - flex: 4, - child: Text( - result!.files[index].name, - overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.headlineLarge!.copyWith(fontSize: blockSizeVertical * 2), - ), + Flexible( + flex: 6, + child: Text( + results[index].name, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.headlineLarge!.copyWith(fontSize: blockSizeVertical * 2), + ), + ), + const SizedBox( + width: 12, + ), + Flexible( + flex: 2, + child: Text( + size, + style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.grey), + )), + Flexible( + flex: 1, + child: IconButton( + icon: const Icon( + Icons.close, + color: Colors.grey, ), - const SizedBox(width: 12,), - Flexible( - flex: 2, - child: Text(size)) + onPressed: () { + setState(() { + results.removeAt(index); + }); + }, + )) ], )), const Divider() @@ -554,12 +621,15 @@ class EducationScreen extends StatelessWidget { onPressed: () { List paths = []; - if (selectedAttachmentCategory != null) { - for (var res in result!.files) { + if (selectedAttachmentCategory != null && results.isNotEmpty) { + for (var res in results) { paths.add(res.path!); } + setState(() { + results.clear(); + }); Navigator.pop(context); - parent.read().add(AddAttachment(attachmentModule: state.educationalBackground[index].id.toString(), filePaths: paths, categoryId: selectedAttachmentCategory!.id.toString(), token: token!, profileId: profileId.toString())); + parent.read().add(AddEducationAttachment(attachmentModule: state.educationalBackground[index].id.toString(), filePaths: paths, categoryId: selectedAttachmentCategory!.id.toString(), token: token!, profileId: profileId.toString())); } }, child: const Text("Submit")), @@ -594,6 +664,7 @@ class EducationScreen extends StatelessWidget { ) ], ), + ////Show Attachments SizedBox( child: state @@ -620,22 +691,45 @@ class EducationScreen extends StatelessWidget { 1 ? ////Single Attachment view - SingleAttachment( - onpressed: () { - confirmAlert( - context, - () {}, - "Delete?", - "Confirm Delete?"); - }, - attachment: state - .educationalBackground[ - index] - .attachments! - .first, + Column( + children: [ + const Divider(), + SingleAttachment( + onpressed: + () { + confirmAlert( + context, + () { + parent.read().add(DeleteEducationAttachment( + attachment: state + .educationalBackground[ + index] + .attachments! + .first, + moduleId: state + .educationalBackground[ + index] + .id!, + profileId: + profileId, + token: + token!)); + }, "Delete?", + "Confirm Delete?"); + }, + attachment: state + .educationalBackground[ + index] + .attachments! + .first, + ), + ], ) ////Multiple Attachments View : MultipleAttachments( + profileId: + profileId, + token: token!, eligibilityName: state .educationalBackground[ index] @@ -646,6 +740,21 @@ class EducationScreen extends StatelessWidget { .educationalBackground[ index] .attachments!, + educationBloc: + BlocProvider.of< + EducationBloc>( + parent), + eligibilityBloc: + null, + workHistoryBloc: + null, + learningDevelopmentBloc: + null, + blocId: 1, + moduleId: state + .educationalBackground[ + index] + .id!, )) ], ), diff --git a/lib/screens/profile/components/eligibility_screen.dart b/lib/screens/profile/components/eligibility_screen.dart index fc6b279..5b10940 100644 --- a/lib/screens/profile/components/eligibility_screen.dart +++ b/lib/screens/profile/components/eligibility_screen.dart @@ -1,5 +1,4 @@ import 'dart:io'; - import 'package:app_popup_menu/app_popup_menu.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; @@ -9,7 +8,10 @@ import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_svg/svg.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:unit2/bloc/profile/education/education_bloc.dart'; +import 'package:unit2/bloc/profile/learningDevelopment/learning_development_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/profile/workHistory/workHistory_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/attachment.dart'; import 'package:unit2/model/profile/eligibility.dart'; @@ -38,7 +40,7 @@ class EligibiltyScreen extends StatelessWidget { BuildContext parent = context; String? token; int? profileId; - FilePickerResult? result; + List? results = []; AttachmentCategory? selectedAttachmentCategory; List attachmentCategories = []; @@ -101,7 +103,7 @@ class EligibiltyScreen extends StatelessWidget { if (state is EligibilityLoaded || state is AddEligibilityState || state is EditEligibilityState || - state is DeletedState || + state is EligibilityDeletedState || state is EligibilityAddedState || state is EligibilityEditedState || state is EligibilityErrorState) { @@ -109,7 +111,7 @@ class EligibiltyScreen extends StatelessWidget { progress!.dismiss(); } ////DELETED STATE - if (state is DeletedState) { + if (state is EligibilityDeletedState) { if (state.success) { successAlert(context, "Deletion Successfull", "Eligibility has been deleted successfully", @@ -129,6 +131,28 @@ class EligibiltyScreen extends StatelessWidget { }); } } + ////ATTACHMENT ADDED STATE + + if (state is EligibilityAttachmentAddedState) { + if (state.response['success']) { + successAlert(context, "Adding Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context + .read() + .add(const LoadEligibility()); + }); + } else { + errorAlert(context, "Adding Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context + .read() + .add(const LoadEligibility()); + }); + } + } ////ADDED STATE if (state is EligibilityAddedState) { if (state.response['success']) { @@ -171,13 +195,34 @@ class EligibiltyScreen extends StatelessWidget { }); } } + ////ATTACHMENT DELETED STATE + if (state is EligibilitytAttachmentDeletedState) { + if (state.success) { + successAlert(context, "Deletion Successfull", + "Attachment has been deleted successfully", + () { + Navigator.of(context).pop(); + context + .read() + .add(const LoadEligibility()); + }); + } else { + errorAlert(context, "Deletion Failed", + "Error deleting Attachment", () { + Navigator.of(context).pop(); + context + .read() + .add(const LoadEligibility()); + }); + } + } }, builder: (context, state) { return BlocBuilder( builder: (context, state) { if (state is EligibilityLoaded) { for (var cat in state.attachmentCategory) { - if (cat.subclass.id == 3) { + if (cat.subclass!.id == 3) { attachmentCategories.add(cat); } } @@ -193,410 +238,460 @@ class EligibiltyScreen extends StatelessWidget { .eligibility! .title; return Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, children: [ Container( - width: screenWidth, padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 8), decoration: box1(), - child: Row( + child: Column( children: [ - Expanded( - child: Column( - mainAxisSize: - MainAxisSize.min, - mainAxisAlignment: - MainAxisAlignment - .start, - crossAxisAlignment: - CrossAxisAlignment - .start, - children: [ - Text( - title, - style: Theme.of( - context) - .textTheme - .titleMedium! - .copyWith( - fontWeight: - FontWeight - .w500, - color: - primary), - ), - const SizedBox( - height: 5, - ), - Text( - "$licenseNumber: ${state.eligibilities[index].licenseNumber == null ? 'N/A' : state.eligibilities[index].licenseNumber.toString()}", - style: Theme.of( - context) - .textTheme - .titleSmall), - const SizedBox( - height: 3, - ), - Text( - "Rating : ${state.eligibilities[index].rating ?? 'N/A'}", - style: Theme.of( - context) - .textTheme - .titleSmall), - const Divider(), - ////Show Attachments - SizedBox( - child: state.eligibilities[index].attachments == - null || - state + Row( + children: [ + Expanded( + child: Column( + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + Text( + title, + style: Theme.of( + context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight + .w500, + color: + primary), + ), + const SizedBox( + height: 5, + ), + Text( + "$licenseNumber: ${state.eligibilities[index].licenseNumber == null ? 'N/A' : state.eligibilities[index].licenseNumber.toString()}", + style: Theme.of( + context) + .textTheme + .titleSmall), + const SizedBox( + height: 3, + ), + Text( + "Rating : ${state.eligibilities[index].rating ?? 'N/A'}", + style: Theme.of( + context) + .textTheme + .titleSmall), + ]), + ), + AppPopupMenu( + offset: const Offset( + -10, -10), + elevation: 3, + onSelected: (value) { + ////delete eligibilty-= = = = = = = = =>> + if (value == 2) { + confirmAlert( + context, () { + final progress = + ProgressHUD.of( + context); + progress! + .showWithText( + "Loading..."); + BlocProvider + .of( + context) + .add(DeleteEligibility( + eligibilityId: state .eligibilities[ index] - .attachments! - .isEmpty - ? const SizedBox() - : state.eligibilities[index].attachments != - null && - state.eligibilities[index].attachments!.length == - 1 - ? - ////Single Attachment view - SingleAttachment( - onpressed: - () { - confirmAlert(context, - () { - parent.read().add(DeleteAttachment(attachment: state.eligibilities[index].attachments!.first, moduleId: state.eligibilities[index].attachments!.first.id.toString(), profileId: profileId.toString(), token: token!)); - }, "Delete?", - "Confirm Delete?"); - }, - attachment: state - .eligibilities[index] - .attachments! - .first, - ) - ////Multiple Attachments View - : MultipleAttachments( - eligibilityName: state - .eligibilities[index] - .eligibility! - .title, - attachments: state - .eligibilities[index] - .attachments!, - )) - ]), - ), - AppPopupMenu( - offset: - const Offset(-10, -10), - elevation: 3, - onSelected: (value) { - ////delete eligibilty-= = = = = = = = =>> - if (value == 2) { - confirmAlert(context, - () { - final progress = - ProgressHUD.of( - context); - progress! - .showWithText( - "Loading..."); - BlocProvider.of< - EligibilityBloc>( - context) - .add(DeleteEligibility( - eligibilityId: state - .eligibilities[ - index] - .id!, - profileId: - profileId - .toString(), - token: - token!)); - }, "Delete?", - "Confirm Delete?"); - } - if (value == 1) { - ////edit eligibilty-= = = = = = = = =>> - final progress = - ProgressHUD.of( - context); - progress!.showWithText( - "Loading..."); - EligibityCert - eligibityCert = - state.eligibilities[ - index]; - bool overseas = eligibityCert - .examAddress! - .country! - .id - .toString() == - '175' - ? false - : true; - eligibityCert.overseas = - overseas; + .id!, + profileId: + profileId + .toString(), + token: + token!)); + }, "Delete?", + "Confirm Delete?"); + } + if (value == 1) { + ////edit eligibilty-= = = = = = = = =>> + final progress = + ProgressHUD.of( + context); + progress! + .showWithText( + "Loading..."); + EligibityCert + eligibityCert = + state.eligibilities[ + index]; + bool overseas = eligibityCert + .examAddress! + .country! + .id + .toString() == + '175' + ? false + : true; + eligibityCert + .overseas = + overseas; - eligibityCert.overseas = - overseas; + eligibityCert + .overseas = + overseas; - context - .read< - EligibilityBloc>() - .add(ShowEditEligibilityForm( - eligibityCert: - eligibityCert)); - } - ////Attachment - if (value == 3) { - showDialog( - context: context, - builder: - (BuildContext - context) { - return AlertDialog( - contentPadding: - const EdgeInsets - .all(0), - backgroundColor: - Colors.grey - .shade100, - icon: - const Icon( - Icons - .file_copy, - size: 32, - color: - primary, - ), - title: const Text( - "File Attachment:"), - content: StatefulBuilder( - builder: + context + .read< + EligibilityBloc>() + .add(ShowEditEligibilityForm( + eligibityCert: + eligibityCert)); + } + ////Attachment + if (value == 3) { + results.clear(); + showDialog( + context: + context, + builder: + (BuildContext + context) { + return AlertDialog( + contentPadding: + const EdgeInsets + .all(0), + backgroundColor: + Colors + .grey + .shade100, + icon: + const Icon( + Icons + .file_copy, + size: 32, + color: + primary, + ), + title: const Text( + "File Attachment:"), + content: StatefulBuilder(builder: (context, setState) { - return Padding( - padding: const EdgeInsets - .all( - 16.0), - child: Column( - mainAxisSize: - MainAxisSize - .min, - mainAxisAlignment: - MainAxisAlignment - .start, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - const Divider(), - Text( - title, - style: - Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500, color: primary), - ), - const SizedBox( - height: - 5, - ), - Text( - "$licenseNumber: ${state.eligibilities[index].licenseNumber == null ? 'N/A' : state.eligibilities[index].licenseNumber.toString()}", - style: Theme.of(context).textTheme.titleSmall), - const SizedBox( - height: - 3, - ), - Text( - "Rating : ${state.eligibilities[index].rating ?? 'N/A'}", - style: Theme.of(context).textTheme.titleSmall), - const Divider(), - FormBuilderDropdown( - autovalidateMode: AutovalidateMode.always, - decoration: normalTextFieldStyle("attachment category", "attachment category"), - name: 'attachments_categorues', - validator: FormBuilderValidators.required(errorText: "This field is required"), - onChanged: (value) { - selectedAttachmentCategory = value; - }, - items: attachmentCategories.map((e) { - return DropdownMenuItem(value: e, child: Text(e.description)); - }).toList()), - const SizedBox( - height: - 8, - ), - Text( - "You may attach necessary documents such as Transcript of Records (TOR), diploma, and the likes.", - style: - Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.black), - ), - const SizedBox( - height: - 5, - ), - Text( - "Acceptable Files: PDF, JPEG, PNG", - style: - Theme.of(context).textTheme.bodySmall, - ), - Text( - "Max File Size (per attachment): 1MB", - style: - Theme.of(context).textTheme.bodySmall, - ), - const Divider(), - ElevatedButton( - style: ButtonStyle( - elevation: MaterialStateProperty.all(0), - backgroundColor: MaterialStateProperty.all(Colors.white), + return Padding( + padding: + const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: + MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Divider(), + Text( + title, + style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500, color: primary), ), - onPressed: () async { - FilePickerResult? newResult = await FilePicker.platform.pickFiles(allowMultiple: true, type: FileType.custom, allowedExtensions: [ - 'jpg', - 'png', - 'jpeg', - 'pdf' - ]); - setState(() { - if (newResult != null) { - result = newResult; - } - }); - }, - child: const Center( - child: Text( - "Select Files", - textAlign: TextAlign.center, - style: TextStyle(color: Colors.black), - ))), - const Divider(), - SingleChildScrollView( - child: - SizedBox( - height: 100, - width: double.maxFinite, - child: result == null - ? const SizedBox() - : Expanded( - child: ListView.builder( - itemCount: result!.files.length, - itemBuilder: (BuildContext context, index) { - final kb = result!.files[index].size / 1024; - final mb = kb / 1024; - final size = mb >= 1 ? '${mb.toStringAsFixed(2)}MB' : '${kb.toStringAsFixed(2)}KB'; - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox( - width: double.infinity, - child: Row( - children: [ - Flexible( - child: SizedBox( - child: result!.files[index].extension!.toLowerCase() == 'pdf' - ? SvgPicture.asset( - 'assets/svgs/pdf.svg', - height: blockSizeVertical * 3, - allowDrawingOutsideViewBox: true, - ) - : result!.files[index].extension!.toLowerCase() == 'png' + const SizedBox( + height: 5, + ), + Text("$licenseNumber: ${state.eligibilities[index].licenseNumber == null ? 'N/A' : state.eligibilities[index].licenseNumber.toString()}", style: Theme.of(context).textTheme.titleSmall), + const SizedBox( + height: 3, + ), + Text("Rating : ${state.eligibilities[index].rating ?? 'N/A'}", style: Theme.of(context).textTheme.titleSmall), + const Divider(), + FormBuilderDropdown( + autovalidateMode: AutovalidateMode.always, + decoration: normalTextFieldStyle("attachment category", "attachment category"), + name: 'attachments_categorues', + validator: FormBuilderValidators.required(errorText: "This field is required"), + onChanged: (value) { + selectedAttachmentCategory = value; + }, + items: attachmentCategories.map((e) { + return DropdownMenuItem(value: e, child: Text(e.description!)); + }).toList()), + const SizedBox( + height: 8, + ), + Text( + "You may attach necessary documents such as Transcript of Records (TOR), diploma, and the likes.", + style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.black), + ), + const SizedBox( + height: 5, + ), + Text( + "Acceptable Files: PDF, JPEG, PNG", + style: Theme.of(context).textTheme.bodySmall, + ), + Text( + "Max File Size (per attachment): 1MB", + style: Theme.of(context).textTheme.bodySmall, + ), + const Divider(), + ElevatedButton( + style: ButtonStyle( + elevation: MaterialStateProperty.all(0), + backgroundColor: MaterialStateProperty.all(Colors.white), + ), + onPressed: () async { + FilePickerResult? newResult = await FilePicker.platform.pickFiles(allowMultiple: true, type: FileType.custom, allowedExtensions: [ + 'jpg', + 'png', + 'jpeg', + 'pdf' + ]); + setState(() { + FilePickerResult? x; + if (newResult != null) { + newResult.files.forEach((element) { + results.add(element); + }); + } + }); + }, + child: const Center( + child: Text( + "Select Files", + textAlign: TextAlign.center, + style: TextStyle(color: Colors.black), + ))), + const Divider(), + SingleChildScrollView( + child: SizedBox( + height: 100, + width: double.maxFinite, + child: results.isEmpty + ? const SizedBox() + : Expanded( + child: ListView.builder( + itemCount: results.length, + itemBuilder: (BuildContext context, index) { + final kb = results[index].size / 1024; + final mb = kb / 1024; + final size = mb >= 1 ? '${mb.toStringAsFixed(2)}MB' : '${kb.toStringAsFixed(2)}KB'; + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: double.infinity, + child: Row( + children: [ + Flexible( + child: SizedBox( + child: results[index].extension!.toLowerCase() == 'pdf' ? SvgPicture.asset( - 'assets/svgs/png.svg', + 'assets/svgs/pdf.svg', height: blockSizeVertical * 3, allowDrawingOutsideViewBox: true, ) - : result!.files[index].extension!.toLowerCase() == 'jpg' || result!.files[index].extension!.toLowerCase() == 'jpeg' + : results[index].extension!.toLowerCase() == 'png' ? SvgPicture.asset( - 'assets/svgs/jpg.svg', + 'assets/svgs/png.svg', height: blockSizeVertical * 3, allowDrawingOutsideViewBox: true, ) - : const SizedBox())), - const SizedBox( - width: 12, - ), - Flexible( - flex: 4, - child: Text( - result!.files[index].name, - overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.headlineLarge!.copyWith(fontSize: blockSizeVertical * 2), - ), + : results[index].extension!.toLowerCase() == 'jpg' || results[index].extension!.toLowerCase() == 'jpeg' + ? SvgPicture.asset( + 'assets/svgs/jpg.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : const SizedBox())), + const SizedBox( + width: 12, + ), + Flexible( + flex: 6, + child: Text( + results[index].name, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.headlineLarge!.copyWith(fontSize: blockSizeVertical * 2), ), - const SizedBox(width: 8,), - Flexible( + ), + const SizedBox( + width: 8, + ), + Flexible( flex: 2, - child: Text(size)) - ], - )), - const Divider() - ], - ); - }), - ), - ), - ), - const SizedBox( - height: - 12, - ), - SizedBox( - width: - double.maxFinite, - height: - 50, - child: ElevatedButton( - style: mainBtnStyle(primary, Colors.transparent, second), - onPressed: () { - List paths = []; + child: Text( + size, + style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.grey), + )), + Flexible( + flex: 1, + child: IconButton( + icon: const Icon( + Icons.close, + color: Colors.grey, + ), + onPressed: () { + setState(() { + results.removeAt(index); + }); + }, + )) + ], + )), + const Divider() + ], + ); + }), + ), + ), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: double.maxFinite, + height: 50, + child: ElevatedButton( + style: mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + List paths = []; - if (selectedAttachmentCategory != null) { - for (var res in result!.files) { - paths.add(res.path!); - } - Navigator.pop(context); - parent.read().add(AddAttachment(attachmentModule: state.eligibilities[index].id.toString(), filePaths: paths, categoryId: selectedAttachmentCategory!.id.toString(), token: token!, profileId: profileId.toString())); - } - }, - child: const Text("Submit")), - ) - ]), + if (selectedAttachmentCategory != null && results.isNotEmpty) { + for (var res in results) { + paths.add(res.path!); + } + setState(() { + results.clear(); + }); + Navigator.pop(context); + parent.read().add(AddEligibiltyAttachment(attachmentModule: state.eligibilities[index].id.toString(), filePaths: paths, categoryId: selectedAttachmentCategory!.id.toString(), token: token!, profileId: profileId.toString())); + } + }, + child: const Text("Submit")), + ) + ]), + ); + }), ); - }), - ); - }); - } - }, - menuItems: [ - popMenuItem( - text: "Update", - value: 1, - icon: Icons.edit), - popMenuItem( - text: "Remove", - value: 2, - icon: Icons.delete), - popMenuItem( - text: "Attach", - value: 3, - icon: Icons - .attach_file), + }); + } + }, + menuItems: [ + popMenuItem( + text: "Update", + value: 1, + icon: Icons.edit), + popMenuItem( + text: "Remove", + value: 2, + icon: + Icons.delete), + popMenuItem( + text: "Attach", + value: 3, + icon: Icons + .attach_file), + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ) ], - icon: const Icon( - Icons.more_vert, - color: Colors.grey, - ), - tooltip: "Options", - ) + ), + const Divider(), + ////Show Attachments + SizedBox( + width: screenWidth, + child: state + .eligibilities[ + index] + .attachments == + null || + state + .eligibilities[ + index] + .attachments! + .isEmpty + ? const SizedBox() + : state.eligibilities[index].attachments != + null && + state + .eligibilities[ + index] + .attachments! + .length == + 1 + ? + ////Single Attachment view + SingleAttachment( + onpressed: + () { + confirmAlert( + context, + () { + parent.read().add(DeleteEligibyAttachment( + attachment: state + .eligibilities[ + index] + .attachments! + .first, + moduleId: state + .eligibilities[ + index] + .id + .toString(), + profileId: profileId + .toString(), + token: + token!)); + }, "Delete?", + "Confirm Delete?"); + }, + attachment: state + .eligibilities[ + index] + .attachments! + .first, + ) + ////Multiple Attachments View + : MultipleAttachments( + profileId: + profileId!, + token: token!, + moduleId: state + .eligibilities[ + index] + .eligibility! + .id, + educationBloc: + null, + learningDevelopmentBloc: + null, + workHistoryBloc: + null, + eligibilityBloc: + BlocProvider.of< + EligibilityBloc>( + context), + blocId: 2, + eligibilityName: state + .eligibilities[ + index] + .eligibility! + .title, + attachments: state + .eligibilities[ + index] + .attachments!, + )) ], ), ), diff --git a/lib/screens/profile/components/learning_and_development_screen.dart b/lib/screens/profile/components/learning_and_development_screen.dart index c7cea00..0e70a88 100644 --- a/lib/screens/profile/components/learning_and_development_screen.dart +++ b/lib/screens/profile/components/learning_and_development_screen.dart @@ -1,19 +1,18 @@ import 'package:app_popup_menu/app_popup_menu.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter/src/widgets/placeholder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:fluttericon/font_awesome_icons.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:intl/intl.dart'; +import 'package:unit2/bloc/profile/education/education_bloc.dart'; +import 'package:unit2/bloc/profile/eligibility/eligibility_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; +import 'package:unit2/bloc/profile/workHistory/workHistory_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; -import 'package:unit2/model/profile/learning_development.dart'; import 'package:unit2/screens/profile/components/learning_development/edit_modal.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; @@ -41,9 +40,9 @@ class LearningAndDevelopmentScreen extends StatelessWidget { Widget build(BuildContext context) { String token; int profileId; - BuildContext parent = context; + BuildContext parent = context; DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); - FilePickerResult? result; + List? results = []; AttachmentCategory? selectedAttachmentCategory; List attachmentCategories = []; return Scaffold( @@ -89,13 +88,11 @@ class LearningAndDevelopmentScreen extends StatelessWidget { backgroundColor: Colors.black87, child: BlocBuilder( builder: (context, state) { - if (state is UserLoggedIn) { token = state.userData!.user!.login!.token!; profileId = state.userData!.user!.login!.user!.profileId!; return BlocBuilder( builder: (context, state) { - if (state is ProfileLoaded) { return BlocConsumer( @@ -134,6 +131,27 @@ class LearningAndDevelopmentScreen extends StatelessWidget { }); } } + ////Attachment Added State + if (state is LearningDevAttachmentAddedState) { + if (state.response['success']) { + successAlert(context, "Adding Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context + .read() + .add(LoadLearniningDevelopment()); + }); + } else { + errorAlert(context, "Adding Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context + .read() + .add(LoadLearniningDevelopment()); + }); + } + } ////Updated State if (state is LearningDevelopmentUpdatedState) { if (state.response['success']) { @@ -177,16 +195,36 @@ class LearningAndDevelopmentScreen extends StatelessWidget { }); } } + ////ATTACHMENT DELETED STATE + if (state is LearningDevAttachmentDeletedState) { + if (state.success) { + successAlert(context, "Deletion Successfull", + "Attachment has been deleted successfully", + () { + Navigator.of(context).pop(); + context + .read() + .add(LoadLearniningDevelopment()); + }); + } else { + errorAlert(context, "Deletion Failed", + "Error deleting Attachment", () { + Navigator.of(context).pop(); + context + .read() + .add(LoadLearniningDevelopment()); + }); + } + } // TODO: implement listener }, builder: (context, state) { - if (state is LearningDevelopmentLoadedState) { - for (var cat in state.attachmentCategory) { - if (cat.subclass.id == 2) { - attachmentCategories.add(cat); - } - } + for (var cat in state.attachmentCategory) { + if (cat.subclass!.id == 2) { + attachmentCategories.add(cat); + } + } if (state.learningsAndDevelopment.isNotEmpty) { return ListView.builder( padding: const EdgeInsets.symmetric( @@ -232,28 +270,32 @@ class LearningAndDevelopmentScreen extends StatelessWidget { Expanded( child: Column( mainAxisAlignment: - MainAxisAlignment.start, + MainAxisAlignment + .start, crossAxisAlignment: CrossAxisAlignment .start, children: [ Text( training, - style: Theme.of(context) + style: Theme.of( + context) .textTheme .titleMedium! .copyWith( fontWeight: FontWeight .w600, - color: primary), + color: + primary), ), const SizedBox( height: 8, ), Text( provider, - style: Theme.of(context) + style: Theme.of( + context) .textTheme .titleSmall, ), @@ -262,7 +304,8 @@ class LearningAndDevelopmentScreen extends StatelessWidget { ), Text( "$duration: $start to $end", - style: Theme.of(context) + style: Theme.of( + context) .textTheme .labelMedium, ), @@ -272,39 +315,39 @@ class LearningAndDevelopmentScreen extends StatelessWidget { ]), ), AppPopupMenu( - offset: const Offset(-10, -10), + offset: + const Offset(-10, -10), elevation: 3, onSelected: (value) { ////delete -= = = = = = = = =>> if (value == 2) { - confirmAlert(context, () { + confirmAlert(context, + () { final progress = ProgressHUD.of( context); - progress!.showWithText( - "Loading..."); - BlocProvider.of< - LearningDevelopmentBloc>( - context) - .add(DeleteLearningDevelopment( - trainingId: state - .learningsAndDevelopment[ - index] - .conductedTraining! - .id!, - hours: state - .learningsAndDevelopment[ - index] - .conductedTraining! - .totalHours!, - sponsorId: state - .learningsAndDevelopment[ - index] - .sponsoredBy - ?.id, - profileId: - profileId, - token: token)); + progress! + .showWithText( + "Loading..."); + BlocProvider.of(context).add(DeleteLearningDevelopment( + trainingId: state + .learningsAndDevelopment[ + index] + .conductedTraining! + .id!, + hours: state + .learningsAndDevelopment[ + index] + .conductedTraining! + .totalHours!, + sponsorId: state + .learningsAndDevelopment[ + index] + .sponsoredBy + ?.id, + profileId: + profileId, + token: token)); }, "Delete?", "Confirm Delete?"); } @@ -312,7 +355,8 @@ class LearningAndDevelopmentScreen extends StatelessWidget { bool isOverseas; ////edit = = = = = = = =>> final progress = - ProgressHUD.of(context); + ProgressHUD.of( + context); progress!.showWithText( "Loading..."); @@ -340,225 +384,246 @@ class LearningAndDevelopmentScreen extends StatelessWidget { isOverseas: isOverseas)); } - if(value == 3){ - showDialog( - context: context, - builder: - (BuildContext - context) { - return AlertDialog( - contentPadding: - const EdgeInsets - .all(0), - backgroundColor: - Colors.grey - .shade100, - icon: - const Icon( - Icons - .file_copy, - size: 32, - color: - primary, - ), - title: const Text( - "File Attachment:"), - content: StatefulBuilder( - builder: - (context, - setState) { - return Padding( - padding: const EdgeInsets - .all( - 16.0), - child: Column( - mainAxisSize: - MainAxisSize - .min, - mainAxisAlignment: - MainAxisAlignment - .start, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - const Divider(), - Text( - training, - style: - Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500, color: primary), + if (value == 3) { + results.clear(); + showDialog( + context: context, + builder: + (BuildContext + context) { + return AlertDialog( + contentPadding: + const EdgeInsets + .all(0), + backgroundColor: + Colors.grey + .shade100, + icon: + const Icon( + Icons + .file_copy, + size: 32, + color: + primary, + ), + title: const Text( + "File Attachment:"), + content: StatefulBuilder( + builder: + (context, + setState) { + return Padding( + padding: const EdgeInsets + .all( + 16.0), + child: Column( + mainAxisSize: + MainAxisSize + .min, + mainAxisAlignment: + MainAxisAlignment + .start, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + const Divider(), + Text( + training, + style: + Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500, color: primary), + ), + const SizedBox( + height: + 5, + ), + Text( + provider, + style: Theme.of(context).textTheme.titleSmall), + const SizedBox( + height: + 3, + ), + Text( + "$start TO $end", + style: Theme.of(context).textTheme.titleSmall), + const Divider(), + FormBuilderDropdown( + autovalidateMode: AutovalidateMode.always, + decoration: normalTextFieldStyle("attachment category", "attachment category"), + name: 'attachments_categorues', + validator: FormBuilderValidators.required(errorText: "This field is required"), + onChanged: (value) { + selectedAttachmentCategory = value; + }, + items: attachmentCategories.map((e) { + return DropdownMenuItem(value: e, child: Text(e.description!)); + }).toList()), + const SizedBox( + height: + 8, + ), + Text( + "You may attach necessary documents such as Transcript of Records (TOR), diploma, and the likes.", + style: + Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.black), + ), + const SizedBox( + height: + 5, + ), + Text( + "Acceptable Files: PDF, JPEG, PNG", + style: + Theme.of(context).textTheme.bodySmall, + ), + Text( + "Max File Size (per attachment): 1MB", + style: + Theme.of(context).textTheme.bodySmall, + ), + const Divider(), + ElevatedButton( + style: ButtonStyle( + elevation: MaterialStateProperty.all(0), + backgroundColor: MaterialStateProperty.all(Colors.white), ), - const SizedBox( - height: - 5, - ), - Text( - provider, - style: Theme.of(context).textTheme.titleSmall), - const SizedBox( - height: - 3, - ), - Text( - "$start TO $end", - style: Theme.of(context).textTheme.titleSmall), - const Divider(), - FormBuilderDropdown( - autovalidateMode: AutovalidateMode.always, - decoration: normalTextFieldStyle("attachment category", "attachment category"), - name: 'attachments_categorues', - validator: FormBuilderValidators.required(errorText: "This field is required"), - onChanged: (value) { - selectedAttachmentCategory = value; - }, - items: attachmentCategories.map((e) { - return DropdownMenuItem(value: e, child: Text(e.description)); - }).toList()), - const SizedBox( - height: - 8, - ), - Text( - "You may attach necessary documents such as Transcript of Records (TOR), diploma, and the likes.", - style: - Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.black), - ), - const SizedBox( - height: - 5, - ), - Text( - "Acceptable Files: PDF, JPEG, PNG", - style: - Theme.of(context).textTheme.bodySmall, - ), - Text( - "Max File Size (per attachment): 1MB", - style: - Theme.of(context).textTheme.bodySmall, - ), - const Divider(), - ElevatedButton( - style: ButtonStyle( - elevation: MaterialStateProperty.all(0), - backgroundColor: MaterialStateProperty.all(Colors.white), - ), - onPressed: () async { - FilePickerResult? newResult = await FilePicker.platform.pickFiles(allowMultiple: true, type: FileType.custom, allowedExtensions: [ - 'jpg', - 'png', - 'jpeg', - 'pdf' - ]); - setState(() { - if (newResult != null) { - result = newResult; - } + onPressed: () async { + FilePickerResult? newResult = await FilePicker.platform.pickFiles(allowMultiple: true, type: FileType.custom, allowedExtensions: [ + 'jpg', + 'png', + 'jpeg', + 'pdf' + ]); + setState(() { + if (newResult != null) { + newResult.files.forEach((element) { + results.add(element); }); - }, - child: const Center( - child: Text( - "Select Files", - textAlign: TextAlign.center, - style: TextStyle(color: Colors.black), - ))), - const Divider(), - SingleChildScrollView( - child: - SizedBox( - height: 100, - width: double.maxFinite, - child: result == null - ? const SizedBox() - : Expanded( - child: ListView.builder( - itemCount: result!.files.length, - itemBuilder: (BuildContext context, index) { - final kb = result!.files[index].size / 1024; - final mb = kb / 1024; - final size = mb >= 1 ? '${mb.toStringAsFixed(2)}MB' : '${kb.toStringAsFixed(2)}KB'; - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox( - - width: double.infinity, - child: Row( - children: [ - Flexible( - flex: 1, - child: SizedBox( - child: result!.files[index].extension!.toLowerCase() == 'pdf' + } + }); + }, + child: const Center( + child: Text( + "Select Files", + textAlign: TextAlign.center, + style: TextStyle(color: Colors.black), + ))), + const Divider(), + SingleChildScrollView( + child: + SizedBox( + height: 100, + width: double.maxFinite, + child: results.isEmpty + ? const SizedBox() + : Expanded( + child: ListView.builder( + itemCount: results.length, + itemBuilder: (BuildContext context, index) { + final kb = results[index].size / 1024; + final mb = kb / 1024; + final size = mb >= 1 ? '${mb.toStringAsFixed(2)}MB' : '${kb.toStringAsFixed(2)}KB'; + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: double.infinity, + child: Row( + children: [ + Flexible( + flex: 1, + child: SizedBox( + child: results[index].extension!.toLowerCase() == 'pdf' + ? SvgPicture.asset( + 'assets/svgs/pdf.svg', + height: blockSizeVertical * 3, + allowDrawingOutsideViewBox: true, + ) + : results[index].extension!.toLowerCase() == 'png' ? SvgPicture.asset( - 'assets/svgs/pdf.svg', + 'assets/svgs/png.svg', height: blockSizeVertical * 3, allowDrawingOutsideViewBox: true, ) - : result!.files[index].extension!.toLowerCase() == 'png' + : results[index].extension!.toLowerCase() == 'jpg' || results[index].extension!.toLowerCase() == 'jpeg' ? SvgPicture.asset( - 'assets/svgs/png.svg', + 'assets/svgs/jpg.svg', height: blockSizeVertical * 3, allowDrawingOutsideViewBox: true, ) - : result!.files[index].extension!.toLowerCase() == 'jpg' || result!.files[index].extension!.toLowerCase() == 'jpeg' - ? SvgPicture.asset( - 'assets/svgs/jpg.svg', - height: blockSizeVertical * 3, - allowDrawingOutsideViewBox: true, - ) - : const SizedBox())), - const SizedBox( - width: 12, - ), - Flexible( - flex: 4, - child: Text( - result!.files[index].name, - overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.headlineLarge!.copyWith(fontSize: blockSizeVertical * 2), - ), - ), - const SizedBox(width: 12,), - Flexible( - flex: 2, - child: Text(size)) - ], - )), - const Divider() - ], - ); - }), - ), - ), - ), - const SizedBox( - height: - 12, - ), - SizedBox( - width: - double.maxFinite, - height: - 50, - child: ElevatedButton( - style: mainBtnStyle(primary, Colors.transparent, second), - onPressed: () { - List paths = []; + : const SizedBox())), + const SizedBox( + width: 12, + ), + Flexible( + flex: 6, + child: Text( + results[index].name, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.headlineLarge!.copyWith(fontSize: blockSizeVertical * 2), + ), + ), + const SizedBox( + width: 8, + ), + Flexible( + flex: 2, + child: Text( + size, + style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.grey), + )), + Flexible( + flex: 1, + child: IconButton( + icon: const Icon( + Icons.close, + color: Colors.grey, + ), + onPressed: () { + setState(() { + results.removeAt(index); + }); + }, + )) + ], + )), + const Divider() + ], + ); + }), + ), + ), + ), + const SizedBox( + height: + 12, + ), + SizedBox( + width: + double.maxFinite, + height: + 50, + child: ElevatedButton( + style: mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + List paths = []; - if (selectedAttachmentCategory != null) { - for (var res in result!.files) { - paths.add(res.path!); - } - Navigator.pop(context); - parent.read().add(AddAttachment(attachmentModule: state.learningsAndDevelopment[index].conductedTraining!.id.toString(), filePaths: paths, categoryId: selectedAttachmentCategory!.id.toString(), token: token!, profileId: profileId.toString())); - } - }, - child: const Text("Submit")), - ) - ]), - ); - }), + if (selectedAttachmentCategory != null && results.isNotEmpty) { + for (var res in results) { + paths.add(res.path!); + } + results.clear(); + Navigator.pop(context); + parent.read().add(AddALearningDevttachment(attachmentModule: state.learningsAndDevelopment[index].conductedTraining!.id.toString(), filePaths: paths, categoryId: selectedAttachmentCategory!.id.toString(), token: token, profileId: profileId.toString())); + } + }, + child: const Text("Submit")), + ) + ]), ); - }); + }), + ); + }); } }, menuItems: [ @@ -573,7 +638,8 @@ class LearningAndDevelopmentScreen extends StatelessWidget { popMenuItem( text: "Attach", value: 3, - icon: Icons.attach_file), + icon: Icons + .attach_file), ], icon: const Icon( Icons.more_vert, @@ -584,44 +650,92 @@ class LearningAndDevelopmentScreen extends StatelessWidget { ], ), SizedBox( - child: state.learningsAndDevelopment[index].attachments == - null || - state + child: state + .learningsAndDevelopment[ + index] + .attachments == + null || + state + .learningsAndDevelopment[ + index] + .attachments! + .isEmpty + ? const SizedBox() + : state + .learningsAndDevelopment[ + index] + .attachments != + null && + state + .learningsAndDevelopment[ + index] + .attachments! + .length == + 1 + ? + ////Single Attachment view + SingleAttachment( + onpressed: () { + confirmAlert( + context, + () { + parent.read().add(DeleteLearningDevAttachment( + attachment: state .learningsAndDevelopment[ index] .attachments! - .isEmpty - ? const SizedBox() - : state.learningsAndDevelopment[index].attachments != - null && - state.learningsAndDevelopment[index].attachments!.length == - 1 - ? - ////Single Attachment view - SingleAttachment( - onpressed: - () { - confirmAlert(context, - () { - - }, "Delete?", - "Confirm Delete?"); - }, - attachment: state - .learningsAndDevelopment[index] - .attachments! - .first, - ) - ////Multiple Attachments View - : MultipleAttachments( - eligibilityName: state - .learningsAndDevelopment[index] - .conductedTraining!.title!.title!, - - attachments: state - .learningsAndDevelopment[index] - .attachments!, - )) + .first, + moduleId: state + .learningsAndDevelopment[ + index] + .conductedTraining! + .id!, + profileId: + profileId, + token: + token)); + }, "Delete?", + "Confirm Delete?"); + }, + attachment: state + .learningsAndDevelopment[ + index] + .attachments! + .first, + ) + ////Multiple Attachments View + : MultipleAttachments( + profileId: + profileId, + token: token, + moduleId: state + .learningsAndDevelopment[ + index] + .conductedTraining! + .title! + .id!, + educationBloc: + null, + eligibilityBloc: + null, + learningDevelopmentBloc: + BlocProvider.of< + LearningDevelopmentBloc>( + context), + workHistoryBloc: + null, + blocId: 4, + eligibilityName: state + .learningsAndDevelopment[ + index] + .conductedTraining! + .title! + .title!, + attachments: state + .learningsAndDevelopment[ + index] + .attachments!, + )) ], ), ), diff --git a/lib/screens/profile/components/work_history_screen.dart b/lib/screens/profile/components/work_history_screen.dart index 153f851..b234467 100644 --- a/lib/screens/profile/components/work_history_screen.dart +++ b/lib/screens/profile/components/work_history_screen.dart @@ -7,9 +7,11 @@ import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:fluttericon/font_awesome_icons.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:intl/intl.dart'; +import 'package:unit2/bloc/profile/education/education_bloc.dart'; +import 'package:unit2/bloc/profile/eligibility/eligibility_bloc.dart'; +import 'package:unit2/bloc/profile/learningDevelopment/learning_development_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/screens/profile/components/work_history/add_modal.dart'; @@ -21,7 +23,6 @@ import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/Leadings/close_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; - import '../../../bloc/profile/workHistory/workHistory_bloc.dart'; import '../../../model/profile/attachment.dart'; import '../../../model/profile/work_history.dart'; @@ -38,10 +39,11 @@ class WorkHistoryScreen extends StatelessWidget { @override Widget build(BuildContext context) { DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); + BuildContext parent = context; String? token; int? profileId; - FilePickerResult? result; + List? results = []; AttachmentCategory? selectedAttachmentCategory; List attachmentCategories = []; return Scaffold( @@ -108,7 +110,7 @@ class WorkHistoryScreen extends StatelessWidget { final progress = ProgressHUD.of(context); progress!.dismiss(); } - //DELETED STATE + ////DELETED STATE if (state is DeletedState) { if (state.success) { successAlert(context, "Deletion Successfull", @@ -149,6 +151,48 @@ class WorkHistoryScreen extends StatelessWidget { }); } } + if (state is WorkHistoryDevAttachmentAddedState) { + if (state.response['success']) { + successAlert(context, "Adding Successfull!", + state.response['message'], () { + Navigator.of(context).pop(); + context + .read() + .add(LoadWorkHistories()); + }); + } else { + errorAlert(context, "Adding Failed", + "Something went wrong. Please try again.", + () { + Navigator.of(context).pop(); + context + .read() + .add(LoadWorkHistories()); + }); + } + } + ////ATTACHMENT DELETED STATE + if (state is WorkHistoryDevAttachmentDeletedState) { + if (state.success) { + successAlert(context, "Deletion Successfull", + "Attachment has been deleted successfully", + () { + Navigator.of(context).pop(); + context + .read() + .add(LoadWorkHistories()); + }); + } else { + errorAlert(context, "Deletion Failed", + "Error deleting Attachment", () { + Navigator.of(context).pop(); + context + .read() + .add(LoadWorkHistories()); + }); + } + } + //// EDITED STATE if (state is WorkHistoryEditedState) { if (state.response['success']) { @@ -174,7 +218,7 @@ class WorkHistoryScreen extends StatelessWidget { builder: (context, state) { if (state is WorkHistoryLoaded) { for (var cat in state.attachmentCategory) { - if (cat.subclass.id == 4) { + if (cat.subclass!.id == 4) { attachmentCategories.add(cat); } } @@ -201,14 +245,8 @@ class WorkHistoryScreen extends StatelessWidget { .workExperiences[index] .toDate!); return Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, children: [ Container( - width: screenWidth, decoration: box1(), padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 8), @@ -303,6 +341,7 @@ class WorkHistoryScreen extends StatelessWidget { } ////Attachment if (value == 3) { + results.clear(); showDialog( context: context, builder: (BuildContext @@ -383,7 +422,7 @@ class WorkHistoryScreen extends StatelessWidget { selectedAttachmentCategory = value; }, items: attachmentCategories.map((e) { - return DropdownMenuItem(value: e, child: Text(e.description)); + return DropdownMenuItem(value: e, child: Text(e.description!)); }).toList()), const SizedBox( height: @@ -429,7 +468,9 @@ class WorkHistoryScreen extends StatelessWidget { ]); setState(() { if (newResult != null) { - result = newResult; + newResult.files.forEach((element) { + results.add(element); + }); } }); }, @@ -447,13 +488,13 @@ class WorkHistoryScreen extends StatelessWidget { 100, width: double.maxFinite, - child: result == null + child: results.isEmpty ? const SizedBox() : Expanded( child: ListView.builder( - itemCount: result!.files.length, + itemCount: results.length, itemBuilder: (BuildContext context, index) { - final kb = result!.files[index].size / 1024; + final kb = results[index].size / 1024; final mb = kb / 1024; final size = mb >= 1 ? '${mb.toStringAsFixed(2)}MB' : '${kb.toStringAsFixed(2)}KB'; return Column( @@ -465,19 +506,19 @@ class WorkHistoryScreen extends StatelessWidget { children: [ Flexible( child: SizedBox( - child: result!.files[index].extension!.toLowerCase() == 'pdf' + child: results[index].extension!.toLowerCase() == 'pdf' ? SvgPicture.asset( 'assets/svgs/pdf.svg', height: blockSizeVertical * 3, allowDrawingOutsideViewBox: true, ) - : result!.files[index].extension!.toLowerCase() == 'png' + : results[index].extension!.toLowerCase() == 'png' ? SvgPicture.asset( 'assets/svgs/png.svg', height: blockSizeVertical * 3, allowDrawingOutsideViewBox: true, ) - : result!.files[index].extension!.toLowerCase() == 'jpg' || result!.files[index].extension!.toLowerCase() == 'jpeg' + : results[index].extension!.toLowerCase() == 'jpg' || results[index].extension!.toLowerCase() == 'jpeg' ? SvgPicture.asset( 'assets/svgs/jpg.svg', height: blockSizeVertical * 3, @@ -487,18 +528,36 @@ class WorkHistoryScreen extends StatelessWidget { const SizedBox( width: 12, ), - Flexible( - flex: 4, - child: Text( - result!.files[index].name, - overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.headlineLarge!.copyWith(fontSize: blockSizeVertical * 2), - ), + Flexible( + flex: 6, + child: Text( + results[index].name, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.headlineLarge!.copyWith(fontSize: blockSizeVertical * 2), + ), + ), + const SizedBox( + width: 6, + ), + Flexible( + flex: 2, + child: Text( + size, + style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.grey), + )), + Flexible( + flex: 1, + child: IconButton( + icon: const Icon( + Icons.close, + color: Colors.grey, ), - const SizedBox(width: 12,), - Flexible( - flex: 2, - child: Text(size)) + onPressed: () { + setState(() { + results.removeAt(index); + }); + }, + )) ], )), const Divider() @@ -522,12 +581,15 @@ class WorkHistoryScreen extends StatelessWidget { onPressed: () { List paths = []; - if (selectedAttachmentCategory != null) { - for (var res in result!.files) { + if (selectedAttachmentCategory != null && results.isNotEmpty) { + for (var res in results) { paths.add(res.path!); } + setState(() { + results.clear(); + }); Navigator.pop(context); - parent.read().add(AddAttachment(attachmentModule: state.workExperiences[index].id.toString(), filePaths: paths, categoryId: selectedAttachmentCategory!.id.toString(), token: token!, profileId: profileId.toString())); + parent.read().add(AddWorkHistoryAttachment(attachmentModule: state.workExperiences[index].id.toString(), filePaths: paths, categoryId: selectedAttachmentCategory!.id.toString(), token: token!, profileId: profileId.toString())); } }, child: const Text("Submit")), @@ -592,8 +654,22 @@ class WorkHistoryScreen extends StatelessWidget { onpressed: () { confirmAlert( context, - () {}, - "Delete?", + () { + parent.read().add(DeleteWorkHistoryAttachment( + attachment: state + .workExperiences[ + index] + .attachments! + .first, + moduleId: state + .workExperiences[ + index] + .id!, + profileId: + profileId!, + token: + token!)); + }, "Delete?", "Confirm Delete?"); }, attachment: state @@ -604,6 +680,22 @@ class WorkHistoryScreen extends StatelessWidget { ) ////Multiple Attachments View : MultipleAttachments( + profileId: + profileId!, + token: token!, + moduleId: state + .workExperiences[ + index] + .id!, + educationBloc: + null, + workHistoryBloc: + BlocProvider.of(context), + eligibilityBloc: + null, + learningDevelopmentBloc: + null, + blocId: 3, eligibilityName: state .workExperiences[ index] diff --git a/lib/screens/profile/shared/multiple_attachment.dart b/lib/screens/profile/shared/multiple_attachment.dart index a818bf7..22b04c3 100644 --- a/lib/screens/profile/shared/multiple_attachment.dart +++ b/lib/screens/profile/shared/multiple_attachment.dart @@ -1,150 +1,234 @@ +import 'package:auto_size_text/auto_size_text.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; import 'package:fluttericon/entypo_icons.dart'; +import 'package:unit2/bloc/profile/education/education_bloc.dart'; +import 'package:unit2/bloc/profile/eligibility/eligibility_bloc.dart'; +import 'package:unit2/bloc/profile/learningDevelopment/learning_development_bloc.dart'; +import 'package:unit2/bloc/profile/workHistory/workHistory_bloc.dart'; +import 'package:unit2/utils/global_context.dart'; import '../../../model/profile/attachment.dart'; import '../../../theme-data.dart/box_shadow.dart'; import '../../../theme-data.dart/colors.dart'; +import '../../../utils/alerts.dart'; import '../../../utils/global.dart'; class MultipleAttachments extends StatelessWidget { final List attachments; final String eligibilityName; - const MultipleAttachments({ - super.key, - required this.attachments, - required this.eligibilityName - }); + final EducationBloc? educationBloc; + final EligibilityBloc? eligibilityBloc; + final LearningDevelopmentBloc? learningDevelopmentBloc; + final WorkHistoryBloc? workHistoryBloc; + final int blocId; + final int moduleId; + final int profileId; + final String token; + const MultipleAttachments( + {super.key, + required this.blocId, + required this.educationBloc, + required this.eligibilityBloc, + required this.learningDevelopmentBloc, + required this.workHistoryBloc, + required this.attachments, + required this.eligibilityName, + required this.moduleId, + required this.profileId, + required this.token}); @override Widget build(BuildContext context) { return Row( - children: [ - Flexible( - flex: - 2, - child: Container( - padding: const EdgeInsets.all(5), - decoration: box1().copyWith(color: Colors.grey.shade300, boxShadow: []), - child: Text( - attachments.first.filename!, - overflow: TextOverflow.ellipsis, - ))), - const SizedBox( - width: - 8, - ), - Flexible( - child: - FittedBox( - child: - Container( - padding: - const EdgeInsets.all(3), - decoration: - box1().copyWith(color: Colors.grey.shade300, boxShadow: []), - child: - InkWell( - onTap: () { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: Text("$eligibilityName Attachments",textAlign: TextAlign.center,), - content: Column( - mainAxisSize: MainAxisSize.min, - children: attachments.map((e) { - String ext = e.filename!.substring(e.filename!.lastIndexOf(".")); - - return Column( - children: [ - Row( - children: [ - Flexible( - child: SizedBox( - child: ext == '.pdf' - ? SvgPicture.asset( - 'assets/svgs/pdf.svg', - height: blockSizeVertical * 5, - allowDrawingOutsideViewBox: true, - ) - : ext == '.png' - ? SvgPicture.asset( - 'assets/svgs/png.svg', - height: blockSizeVertical * 5.5, - allowDrawingOutsideViewBox: true, - ) - : ext == '.jpg' - ? SvgPicture.asset( - 'assets/svgs/jpg.svg', - height: blockSizeVertical * 5, - allowDrawingOutsideViewBox: true, - ) - : SvgPicture.asset( - 'assets/svgs/jpg.svg', - height: blockSizeVertical * 5, - allowDrawingOutsideViewBox: true, - ), - ), - ), - const SizedBox(width: 8,), - Flexible( - flex: 4, - child: Tooltip( - message: e.filename, - child: Text( - e.filename!, - overflow: TextOverflow.ellipsis, - )), - ), - const SizedBox(width: 8,), - Flexible( - child: Row( + children: [ + Flexible( + flex: 3, + child: Container( + padding: const EdgeInsets.all(5), + decoration: + box1().copyWith(color: Colors.grey.shade300, boxShadow: []), + child: AutoSizeText( + + attachments.first.filename!, + wrapWords: false, + maxLines: 1, + ))), + const SizedBox( + width: 8, + ), + Flexible( + child: FittedBox( + child: Container( + padding: const EdgeInsets.all(3), + decoration: + box1().copyWith(color: Colors.grey.shade300, boxShadow: []), + child: InkWell( + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text( + "$eligibilityName Attachments", + textAlign: TextAlign.center, + ), + content: Column( + mainAxisSize: MainAxisSize.min, + children: attachments.map((e) { + String ext = e.filename! + .substring(e.filename!.lastIndexOf(".")); + + return Column( + children: [ + Row( children: [ - Flexible( - child: IconButton( - icon: const Icon( - Entypo.right_open_mini, - color: Colors.black87, - ), - onPressed: () {}, - ), - ), - const SizedBox(width: 8,), Flexible( - child: IconButton( - icon: const Icon( - Icons.delete, - color: primary, - ), - onPressed: () {}, + child: SizedBox( + child: ext == '.pdf' + ? SvgPicture.asset( + 'assets/svgs/pdf.svg', + height: blockSizeVertical * 5, + allowDrawingOutsideViewBox: + true, + ) + : ext == '.png' + ? SvgPicture.asset( + 'assets/svgs/png.svg', + height: + blockSizeVertical * + 5.5, + allowDrawingOutsideViewBox: + true, + ) + : ext == '.jpg' + ? SvgPicture.asset( + 'assets/svgs/jpg.svg', + height: + blockSizeVertical * + 5, + allowDrawingOutsideViewBox: + true, + ) + : SvgPicture.asset( + 'assets/svgs/jpg.svg', + height: + blockSizeVertical * + 5, + allowDrawingOutsideViewBox: + true, + ), + ), + ), + const SizedBox( + width: 8, + ), + Flexible( + flex: 4, + child: Tooltip( + message: e.filename, + child: Text( + e.filename!, + overflow: TextOverflow.ellipsis, + )), + ), + const SizedBox( + width: 8, + ), + Flexible( + child: Row( + children: [ + Flexible( + child: IconButton( + icon: const Icon( + Icons.delete, + color: primary, + ), + onPressed: () { + confirmAlert(context, () { + if (blocId == 1) { + Navigator.pop( + NavigationService + .navigatorKey + .currentContext!); + educationBloc!.add( + DeleteEducationAttachment( + attachment: e, + moduleId: + moduleId, + profileId: + profileId, + token: token)); + } else if (blocId == 2) { + Navigator.pop( + NavigationService + .navigatorKey + .currentContext!); + eligibilityBloc!.add( + DeleteEligibyAttachment( + attachment: e, + moduleId: moduleId + .toString(), + profileId: profileId + .toString(), + token: token)); + } else if (blocId == 3) { + Navigator.pop( + NavigationService + .navigatorKey + .currentContext!); + workHistoryBloc!.add( + DeleteWorkHistoryAttachment( + attachment: e, + moduleId: + moduleId, + profileId: + profileId, + token: token)); + } else { + Navigator.pop( + NavigationService + .navigatorKey + .currentContext!); + learningDevelopmentBloc!.add( + DeleteLearningDevAttachment( + attachment: e, + moduleId: + moduleId, + profileId: + profileId, + token: token)); + } + }, "Delete?", + "Confirm Delete?"); + }), + ) + ], ), ), ], ), - ), - ], - ), - const Divider() - ], - ); - }).toList(), - )); - }); - }, - child: Row( - children: const [ - Text(" See more.."), - Icon( - Icons.keyboard_arrow_right, - color: Colors.black54, - ), - ], - ), + const Divider() + ], + ); + }).toList(), + )); + }); + }, + child: Row( + children: const [ + Text(" See more.."), + Icon( + Icons.keyboard_arrow_right, + color: Colors.black54, + ), + ], ), ), - )), - ], - ); + ), + )), + ], + ); } } diff --git a/lib/screens/profile/shared/single_attachment.dart b/lib/screens/profile/shared/single_attachment.dart index 92f46e7..64f9c91 100644 --- a/lib/screens/profile/shared/single_attachment.dart +++ b/lib/screens/profile/shared/single_attachment.dart @@ -1,3 +1,4 @@ +import 'package:auto_size_text/auto_size_text.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -31,9 +32,10 @@ class SingleAttachment extends StatelessWidget { children: [ Expanded( child: - Text( + AutoSizeText( attachment.filename!, - overflow: TextOverflow.ellipsis, + wrapWords: false, + maxLines: 1, ), ), const SizedBox( diff --git a/lib/sevices/profile/eligibility_services.dart b/lib/sevices/profile/eligibility_services.dart index 9738e8a..d9a94ea 100644 --- a/lib/sevices/profile/eligibility_services.dart +++ b/lib/sevices/profile/eligibility_services.dart @@ -165,11 +165,11 @@ class EligibilityService { "category": { "id": attachment.category?.id, "subclass": { - "id": attachment.category?.subclass.id, - "name": attachment.category?.subclass.name, + "id": attachment.category?.subclass?.id, + "name": attachment.category?.subclass?.name, "attachment_class": { - "id": attachment.category?.subclass.attachmentClass.id, - "name": attachment.category?.subclass.attachmentClass.name + "id": attachment.category?.subclass?.attachmentClass?.id, + "name": attachment.category?.subclass?.attachmentClass?.name } }, "description": attachment.category?.description diff --git a/lib/utils/attachment_categories.dart b/lib/utils/attachment_services.dart similarity index 58% rename from lib/utils/attachment_categories.dart rename to lib/utils/attachment_services.dart index cce37ff..83f7a1c 100644 --- a/lib/utils/attachment_categories.dart +++ b/lib/utils/attachment_services.dart @@ -32,7 +32,7 @@ class AttachmentServices { return attachmentCategories; } - Future> attachment( + Future> attachment( {required String categoryId, required String module, required List paths, @@ -74,4 +74,57 @@ class AttachmentServices { } return response; } + + Future deleteAttachment( + {required Attachment attachment, + required int moduleId, + required String profileId, + required String token}) async { + bool? success; + String authtoken = "Token $token"; + String path = "${Url.instance.attachments()}$profileId/"; + Map? body; + body = { + "attachment_module": moduleId, + "attachments": [ + { + "id": attachment.id, + "created_at": attachment.createdAt?.toString(), + "source": attachment.source, + "filename": attachment.filename, + "category": { + "id": attachment.category?.id, + "subclass": { + "id": attachment.category?.subclass?.id, + "name": attachment.category?.subclass?.name, + "attachment_class": { + "id": attachment.category?.subclass?.attachmentClass?.id, + "name": attachment.category?.subclass?.attachmentClass?.name + } + }, + "description": attachment.category?.description + } + } + ] + }; + + Map params = {"force_mode": "true"}; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authtoken + }; + // try { + http.Response response = await Request.instance.deleteRequest( + path: path, headers: headers, body: body, param: params); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + success = data['success']; + } else { + success = false; + } + // } catch (e) { + // throw (e.toString()); + // } + return success!; + } } diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index bd6932e..3201fd6 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -6,9 +6,9 @@ class Url { String host() { // return '192.168.10.183:3000'; // return 'agusandelnorte.gov.ph'; - // return "192.168.10.219:3000"; - // return "192.168.10.241"; - return "192.168.10.221:3004"; + return "192.168.10.219:3000"; + // // return "192.168.10.241"; + // return "192.168.10.221:3004"; // return "playweb.agusandelnorte.gov.ph"; // return 'devapi.agusandelnorte.gov.ph:3004'; } From c675be937460d86fc82e4dad52a79148da620f0c Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Mon, 7 Aug 2023 14:36:58 +0800 Subject: [PATCH 77/86] change http to https --- lib/utils/attachment_services.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/attachment_services.dart b/lib/utils/attachment_services.dart index 83f7a1c..be51a10 100644 --- a/lib/utils/attachment_services.dart +++ b/lib/utils/attachment_services.dart @@ -49,7 +49,7 @@ class AttachmentServices { try { var request = http.MultipartRequest( - 'POST', Uri.parse('http://${Url.instance.host()}$path$profileId/')); + 'POST', Uri.parse('https://${Url.instance.host()}$path$profileId/')); request.fields.addAll(body); request.headers.addAll(headers); paths.forEach((element) async { From 5a56eb0adfb74cd637c7223b6cbc06947d9bcf28 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Fri, 11 Aug 2023 16:38:26 +0800 Subject: [PATCH 78/86] commit before tree view --- .../rbac_operations/agency/agency_bloc.dart | 6 + .../rbac_operations/agency/agency_event.dart | 5 + .../rbac_operations/station/station_bloc.dart | 4 +- .../assign_area/assign_area_agency_bloc.dart | 31 + .../assign_area/assign_area_agency_event.dart | 18 + .../assign_area/assign_area_agency_state.dart | 34 + .../est_point_person_station_bloc.dart | 33 + .../est_point_person_station_event.dart | 13 + .../est_point_person_station_state.dart | 27 + lib/bloc/user/user_bloc.dart | 43 +- lib/bloc/user/user_state.dart | 3 +- lib/model/utils/agency.dart | 1 - .../superadmin/agency.dart/agency_screen.dart | 4 +- .../components/dashboard/dashboard.dart | 921 +++++++++++------- .../dashboard/superadmin_expanded_menu.dart | 2 +- .../unit2/homepage.dart/module-screen.dart | 1 + .../est_point_person_agecies.dart | 248 +++++ .../est_point_person_station.dart | 536 ++++++++++ .../rbac_operations/station_services.dart | 3 +- pubspec.lock | 16 + pubspec.yaml | 3 + 21 files changed, 1572 insertions(+), 380 deletions(-) create mode 100644 lib/bloc/role/pass_check/est_point_person/assign_area/assign_area_agency_bloc.dart create mode 100644 lib/bloc/role/pass_check/est_point_person/assign_area/assign_area_agency_event.dart create mode 100644 lib/bloc/role/pass_check/est_point_person/assign_area/assign_area_agency_state.dart create mode 100644 lib/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_bloc.dart create mode 100644 lib/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_event.dart create mode 100644 lib/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_state.dart create mode 100644 lib/screens/unit2/roles/establishment_point_person/est_point_person_agecies.dart create mode 100644 lib/screens/unit2/roles/establishment_point_person/est_point_person_station.dart diff --git a/lib/bloc/rbac/rbac_operations/agency/agency_bloc.dart b/lib/bloc/rbac/rbac_operations/agency/agency_bloc.dart index de3cc89..cf343cd 100644 --- a/lib/bloc/rbac/rbac_operations/agency/agency_bloc.dart +++ b/lib/bloc/rbac/rbac_operations/agency/agency_bloc.dart @@ -1,5 +1,6 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:unit2/model/login_data/user_info/assigned_area.dart'; import 'package:unit2/model/utils/agency.dart'; import 'package:unit2/utils/profile_utilities.dart'; import '../../../../model/utils/category.dart'; @@ -27,6 +28,11 @@ class AgencyBloc extends Bloc { emit(AgencyErrorState(message: e.toString())); } }); + on((event,emit)async{ + if (agencyCategory.isEmpty) { + agencyCategory = await ProfileUtilities.instance.agencyCategory(); + } + }); on((event, emit) async { emit(AgencyLoadingState()); try { diff --git a/lib/bloc/rbac/rbac_operations/agency/agency_event.dart b/lib/bloc/rbac/rbac_operations/agency/agency_event.dart index 8371eba..fece6bc 100644 --- a/lib/bloc/rbac/rbac_operations/agency/agency_event.dart +++ b/lib/bloc/rbac/rbac_operations/agency/agency_event.dart @@ -14,3 +14,8 @@ class AddAgency extends AgencyEvent{ final Agency agency; const AddAgency({required this.agency}); } + +class GetEstPointPersonAgencies extends AgencyEvent{ + final List? assignedAreas; + const GetEstPointPersonAgencies({required this.assignedAreas}); +} diff --git a/lib/bloc/rbac/rbac_operations/station/station_bloc.dart b/lib/bloc/rbac/rbac_operations/station/station_bloc.dart index 2dec814..3d1a12d 100644 --- a/lib/bloc/rbac/rbac_operations/station/station_bloc.dart +++ b/lib/bloc/rbac/rbac_operations/station/station_bloc.dart @@ -15,7 +15,7 @@ class StationBloc extends Bloc { emit(StationLoadingState()); try { stations = await RbacStationServices.instance - .getStations(agencyId: event.agencyId); + .getStations(agencyId: event.agencyId.toString()); if (agencies.isEmpty) { List newAgencies = @@ -31,7 +31,7 @@ class StationBloc extends Bloc { // emit(StationLoadingState()); try { stations = await RbacStationServices.instance - .getStations(agencyId: event.agencyId); + .getStations(agencyId: event.agencyId.toString()); if (agencies.isEmpty) { List newAgencies = diff --git a/lib/bloc/role/pass_check/est_point_person/assign_area/assign_area_agency_bloc.dart b/lib/bloc/role/pass_check/est_point_person/assign_area/assign_area_agency_bloc.dart new file mode 100644 index 0000000..0c1db96 --- /dev/null +++ b/lib/bloc/role/pass_check/est_point_person/assign_area/assign_area_agency_bloc.dart @@ -0,0 +1,31 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/model/login_data/user_info/assigned_area.dart'; +import 'package:unit2/model/utils/agency.dart'; +import 'package:unit2/model/utils/category.dart'; + +import '../../../../../utils/profile_utilities.dart'; + +part 'assign_area_agency_event.dart'; +part 'assign_area_agency_state.dart'; + +class AssignAreaAgencyBloc + extends Bloc { + AssignAreaAgencyBloc() : super(AssignAreaAgencyInitial()) { + List? agencies = []; + List agencyCategory = []; + on((event, emit) async { + emit(EstPointPersonAgencyLoadingState()); + try { + if (agencyCategory.isEmpty) { + agencyCategory = await ProfileUtilities.instance.agencyCategory(); + } + agencies = event.assignedAreas; + emit(EstPointPersonAgenciesLoaded( + agencies: agencies, agencyCategory: agencyCategory)); + } catch (e) { + emit(EstPointAgencyErrorState(message: e.toString())); + } + }); + } +} diff --git a/lib/bloc/role/pass_check/est_point_person/assign_area/assign_area_agency_event.dart b/lib/bloc/role/pass_check/est_point_person/assign_area/assign_area_agency_event.dart new file mode 100644 index 0000000..a1dcf49 --- /dev/null +++ b/lib/bloc/role/pass_check/est_point_person/assign_area/assign_area_agency_event.dart @@ -0,0 +1,18 @@ +part of 'assign_area_agency_bloc.dart'; + +abstract class AssignAreaAgencyEvent extends Equatable { + const AssignAreaAgencyEvent(); + + @override + List get props => []; +} + +class EstPointPersonGetAgencies extends AssignAreaAgencyEvent { + final List? assignedAreas; + const EstPointPersonGetAgencies({required this.assignedAreas}); +} + +class EstPointPersonAddAgency extends AssignAreaAgencyEvent { + final Agency agency; + const EstPointPersonAddAgency({required this.agency}); +} diff --git a/lib/bloc/role/pass_check/est_point_person/assign_area/assign_area_agency_state.dart b/lib/bloc/role/pass_check/est_point_person/assign_area/assign_area_agency_state.dart new file mode 100644 index 0000000..40d9ff3 --- /dev/null +++ b/lib/bloc/role/pass_check/est_point_person/assign_area/assign_area_agency_state.dart @@ -0,0 +1,34 @@ +part of 'assign_area_agency_bloc.dart'; + +abstract class AssignAreaAgencyState extends Equatable { + const AssignAreaAgencyState(); + + @override + List get props => []; + + get message => null; +} + +class AssignAreaAgencyInitial extends AssignAreaAgencyState {} +class EstPointPersonAgenciesLoaded extends AssignAreaAgencyState { + final List? agencies; + final List agencyCategory; + const EstPointPersonAgenciesLoaded({required this.agencies, required this.agencyCategory}); +} + +class EstPointAgencyErrorState extends AssignAreaAgencyState { + final String message; + const EstPointAgencyErrorState({required this.message}); +} + +class EstPointPersonAgencyLoadingState extends AssignAreaAgencyState {} + +class EstPointPersonAgencyAddesState extends AssignAreaAgencyState { + final Map response; + const EstPointPersonAgencyAddesState({required this.response}); +} + +class EstPointPersonAgencyDeletedState extends AssignAreaAgencyState { + final bool success; + const EstPointPersonAgencyDeletedState({required this.success}); +} diff --git a/lib/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_bloc.dart b/lib/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_bloc.dart new file mode 100644 index 0000000..104f2f3 --- /dev/null +++ b/lib/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_bloc.dart @@ -0,0 +1,33 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/model/login_data/user_info/assigned_area.dart'; + +import '../../../../../model/rbac/rbac_station.dart'; +import '../../../../../model/utils/agency.dart'; +import '../../../../../sevices/roles/rbac_operations/station_services.dart'; +import '../../../../../utils/profile_utilities.dart'; + +part 'est_point_person_station_event.dart'; +part 'est_point_person_station_state.dart'; + +class EstPointPersonStationBloc extends Bloc { + EstPointPersonStationBloc() : super(EstPointPersonStationInitial()) { + List stations = []; + List assignAreas = []; + + on((event, emit) async { + emit(EstPersonStationLoadingState()); + try { + if(stations.isEmpty){ + stations = await RbacStationServices.instance + .getStations(agencyId: event.agencyId); + } + assignAreas = event.assignedAreas; + emit(EstPersonStationLoadedState(stations: stations, assignedAreas: assignAreas)); + } catch (e) { + emit(EstPersonStationErrorState(message: e.toString())); + } + }); + + } +} diff --git a/lib/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_event.dart b/lib/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_event.dart new file mode 100644 index 0000000..2e34f2e --- /dev/null +++ b/lib/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_event.dart @@ -0,0 +1,13 @@ +part of 'est_point_person_station_bloc.dart'; + +abstract class EstPointPersonStationEvent extends Equatable { + const EstPointPersonStationEvent(); + + @override + List get props => []; +} +class EstPointPersonGetStations extends EstPointPersonStationEvent { + final String agencyId; + final List assignedAreas; + const EstPointPersonGetStations({required this.agencyId, required this.assignedAreas}); +} diff --git a/lib/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_state.dart b/lib/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_state.dart new file mode 100644 index 0000000..25a3128 --- /dev/null +++ b/lib/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_state.dart @@ -0,0 +1,27 @@ +part of 'est_point_person_station_bloc.dart'; + +abstract class EstPointPersonStationState extends Equatable { + const EstPointPersonStationState(); + + @override + List get props => []; +} + +class EstPointPersonStationInitial extends EstPointPersonStationState {} + +class EstPersonStationLoadedState extends EstPointPersonStationState { + final List assignedAreas; + final List stations; + const EstPersonStationLoadedState({required this.stations, required this.assignedAreas}); + @override + List get props => [assignedAreas,stations]; +} + +class EstPersonStationLoadingState extends EstPointPersonStationState {} + +class EstPersonStationErrorState extends EstPointPersonStationState { + final String message; + const EstPersonStationErrorState({required this.message}); + @override + List get props => [message]; +} \ No newline at end of file diff --git a/lib/bloc/user/user_bloc.dart b/lib/bloc/user/user_bloc.dart index 9fb227e..ff111b1 100644 --- a/lib/bloc/user/user_bloc.dart +++ b/lib/bloc/user/user_bloc.dart @@ -4,6 +4,8 @@ import 'package:barcode_scan2/barcode_scan2.dart'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; +import 'package:unit2/model/login_data/user_info/assigned_area.dart'; +import 'package:unit2/model/login_data/user_info/role.dart'; import 'package:unit2/model/login_data/user_info/user_data.dart'; import 'package:unit2/model/login_data/version_info.dart'; import 'package:unit2/screens/unit2/login/functions/get_app_version.dart'; @@ -22,6 +24,7 @@ class UserBloc extends Bloc { String? _apkVersion; bool save = false; String? uuid; + List establishmentPointPersonAssignedAreas = []; UserBloc() : super(UserInitial()) { //// this event is called when opening the app to check if //// there is new app version @@ -69,13 +72,24 @@ class UserBloc extends Bloc { .webLogin(username: event.username, password: event.password); if (response['status'] == true) { UserData userData = UserData.fromJson(response['data']); + Role? estPointPerson = userData.user?.login?.user?.roles?.firstWhere( + (element) => + element?.name?.toLowerCase() == "establishment point-person", + ); + if (estPointPerson != null) { + estPointPerson.assignedArea!.forEach((element) { + establishmentPointPersonAssignedAreas.add(element!); + }); + } emit(UserLoggedIn( + estPersonAssignedArea: establishmentPointPersonAssignedAreas, userData: userData, success: true, message: response['message'], savedCredentials: save)); } else { emit(UserLoggedIn( + estPersonAssignedArea: establishmentPointPersonAssignedAreas, userData: null, success: false, message: response['message'], @@ -85,39 +99,47 @@ class UserBloc extends Bloc { emit(InternetTimeout(message: timeoutError)); } on SocketException catch (_) { emit(InternetTimeout(message: timeoutError)); - }on Error catch(e){ -emit(LoginErrorState(message: e.toString())); + } on Error catch (e) { + emit(LoginErrorState(message: e.toString())); } }); on((event, emit) async { try { - Map response = await AuthService.instance + Map response = await AuthService.instance .qrLogin(uuid: event.uuid, password: event.password); - if (response['status'] == true) { + if (response['status'] == true) { UserData userData = UserData.fromJson(response['data']); emit(UserLoggedIn( + estPersonAssignedArea: establishmentPointPersonAssignedAreas, userData: userData, success: true, message: response['message'], savedCredentials: save)); } else { emit(UserLoggedIn( + estPersonAssignedArea: establishmentPointPersonAssignedAreas, userData: null, success: false, message: response['message'], savedCredentials: save)); } - emit(UserLoggedIn(userData: _userData, savedCredentials: save)); + emit(UserLoggedIn( + estPersonAssignedArea: establishmentPointPersonAssignedAreas, + userData: _userData, + savedCredentials: save)); } on TimeoutException catch (_) { emit(InternetTimeout(message: timeoutError)); } on SocketException catch (_) { emit(InternetTimeout(message: timeoutError)); - } on Error catch(e){ -emit(LoginErrorState(message: e.toString())); + } on Error catch (e) { + emit(LoginErrorState(message: e.toString())); } }); on((event, emit) { - emit(UserLoggedIn(userData: _userData, savedCredentials: save)); + emit(UserLoggedIn( + estPersonAssignedArea: establishmentPointPersonAssignedAreas, + userData: _userData, + savedCredentials: save)); }); on((event, emit) async { ScanResult result = await QRCodeBarCodeScanner.instance.scanner(); @@ -125,8 +147,9 @@ emit(LoginErrorState(message: e.toString())); uuid = result.rawContent.toString(); emit(UuidLoaded(uuid: uuid!)); } - });on((event,emit){ - emit(UuidLoaded(uuid: uuid!)); + }); + on((event, emit) { + emit(UuidLoaded(uuid: uuid!)); }); } } diff --git a/lib/bloc/user/user_state.dart b/lib/bloc/user/user_state.dart index 3bb0873..c63fbf7 100644 --- a/lib/bloc/user/user_state.dart +++ b/lib/bloc/user/user_state.dart @@ -32,11 +32,12 @@ class UserError extends UserState { List get props => []; } class UserLoggedIn extends UserState{ + final List? estPersonAssignedArea; final UserData? userData; final String? message; final bool? success; final bool? savedCredentials; - UserLoggedIn({this.userData,this.message,this.success,this.savedCredentials}); + UserLoggedIn({this.userData,this.message,this.success,this.savedCredentials, required this.estPersonAssignedArea}); } class VersionLoaded extends UserState { diff --git a/lib/model/utils/agency.dart b/lib/model/utils/agency.dart index 68056fb..b8def65 100644 --- a/lib/model/utils/agency.dart +++ b/lib/model/utils/agency.dart @@ -19,7 +19,6 @@ class Agency { category: json["category"] == null ? null : Category.fromJson(json["category"]), privateEntity: json["private_entity"], ); - Map toJson() => { "id": id, "name": name, diff --git a/lib/screens/superadmin/agency.dart/agency_screen.dart b/lib/screens/superadmin/agency.dart/agency_screen.dart index 909f387..391c006 100644 --- a/lib/screens/superadmin/agency.dart/agency_screen.dart +++ b/lib/screens/superadmin/agency.dart/agency_screen.dart @@ -25,8 +25,8 @@ import '../../../widgets/Leadings/add_leading.dart'; import '../../../widgets/empty_data.dart'; class RbacAgencyScreen extends StatelessWidget { - final int id; - const RbacAgencyScreen({super.key, required this.id}); + + const RbacAgencyScreen(); @override Widget build(BuildContext context) { diff --git a/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart b/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart index efeb14d..27cb9f0 100644 --- a/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart +++ b/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart @@ -1,17 +1,30 @@ -import 'package:auto_size_text/auto_size_text.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; +import 'package:unit2/model/login_data/user_info/assigned_area.dart'; import 'package:unit2/screens/unit2/homepage.dart/components/dashboard/dashboard_icon_generator.dart'; import 'package:unit2/screens/unit2/homepage.dart/components/dashboard/superadmin_expanded_menu.dart'; import 'package:unit2/screens/unit2/homepage.dart/module-screen.dart'; import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/theme-data.dart/form-style.dart'; +import '../../../../../bloc/rbac/rbac_operations/agency/agency_bloc.dart'; +import '../../../../../bloc/role/pass_check/est_point_person/assign_area/assign_area_agency_bloc.dart'; +import '../../../../../bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_bloc.dart'; +import '../../../../superadmin/agency.dart/agency_screen.dart'; +import '../../../roles/establishment_point_person/est_point_person_agecies.dart'; +import '../../../roles/establishment_point_person/est_point_person_station.dart'; import './shared_card_label.dart'; class DashBoard extends StatefulWidget { + final List? estPersonAssignedArea; final List cards; final int userId; - const DashBoard({super.key, required this.cards, required this.userId}); + const DashBoard( + {super.key, + required this.cards, + required this.userId, + required this.estPersonAssignedArea}); @override State createState() => _DashBoardState(); @@ -70,373 +83,561 @@ class _DashBoardState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ ////unit2 module operations - Container(child: unit2Cards.isEmpty?const SizedBox():Text( - "Unit2 module operations", - style: Theme.of(context).textTheme.displaySmall!.copyWith( - fontSize: 16, color: primary, fontWeight: FontWeight.w300), - ),), - SizedBox( - height: unit2Cards.isEmpty?0: 8, + Container( + child: unit2Cards.isEmpty + ? const SizedBox() + : Text( + "Unit2 module operations", + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith( + fontSize: 16, + color: primary, + fontWeight: FontWeight.w300), + ), ), - Container(child: unit2Cards.isEmpty?const SizedBox():GridView.count( - shrinkWrap: true, - crossAxisCount: 4, - crossAxisSpacing: 8, - mainAxisSpacing: 10, - physics: const BouncingScrollPhysics(), - padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 5), - children: unit2Cards.length > 3 - ? tempUnit2Cards.map(( - e, - ) { - int index = tempUnit2Cards.indexOf(e); - //// if unit2 cards is less then 3 - return Container( - child: index == 3 - ? CardLabel( - icon: FontAwesome5.chevron_circle_right, - title: "See More", - ontap: () { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: const Text( - "Unit2 Admin Module Operations", - textAlign: TextAlign.center, - ), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox( - height: 200, - width: double.maxFinite, - child: GridView.count( - shrinkWrap: true, - crossAxisCount: 3, - crossAxisSpacing: 8, - mainAxisSpacing: 10, - physics: - const BouncingScrollPhysics(), - padding: - const EdgeInsets - .symmetric( - vertical: 5, - horizontal: 5), - children: - unit2Cards.map(( - e, - ) { - int index = unit2Cards - .indexOf(e); - //// if unit2 cards is less then 3 - return AnimationConfiguration - .staggeredGrid( - position: index, - columnCount: 3, + SizedBox( + height: unit2Cards.isEmpty ? 0 : 8, + ), + Container( + child: unit2Cards.isEmpty + ? const SizedBox() + : GridView.count( + shrinkWrap: true, + crossAxisCount: 4, + crossAxisSpacing: 8, + mainAxisSpacing: 10, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.symmetric( + vertical: 5, horizontal: 5), + children: unit2Cards.length > 3 + ? tempUnit2Cards.map(( + e, + ) { + int index = tempUnit2Cards.indexOf(e); + //// if unit2 cards is greater then 3 + return Container( + child: index == 3 + ? CardLabel( + icon: FontAwesome5 + .chevron_circle_right, + title: "See More", + ontap: () { + showDialog( + context: context, + builder: + (BuildContext context) { + return AlertDialog( + title: const Text( + "Unit2 Admin Module Operations", + textAlign: + TextAlign.center, + ), + content: Column( + mainAxisSize: + MainAxisSize.min, + children: [ + SizedBox( + height: 300, + width: double + .maxFinite, child: - ScaleAnimation( - child: - FadeInAnimation( - child: Container( - child: (e.roleName == 'superadmin' || e.roleName == 'qr code scanner' || e.roleName == 'security guard' || e.roleName == 'establishment point-person' || e.roleName == 'registration in-charge') && e.moduleName == 'unit2' - ? CardLabel( - icon: iconGenerator(name: e.object.name!), - title: e.object.name!.toLowerCase() == 'role based access control' - ? "RBAC" - : e.object.name!.toLowerCase() == "person basic information" - ? "Basic Info" - : e.object.name!, - ontap: () { - if (e.object.name!.toLowerCase() == 'pass check') { - PassCheckArguments passCheckArguments = PassCheckArguments(roleId: 10, userId: widget.userId); - Navigator.pushNamed(context, '/pass-check', arguments: passCheckArguments); - } - if (e.object.name!.toLowerCase() == 'role based access control') { - Navigator.pushNamed(context, '/rbac'); - } - }) - : Container( - color: - Colors.black, - )), + GridView.count( + shrinkWrap: + true, + crossAxisCount: + 3, + crossAxisSpacing: + 8, + mainAxisSpacing: + 10, + physics: + const BouncingScrollPhysics(), + padding: const EdgeInsets + .symmetric( + vertical: + 5, + horizontal: + 5), + children: + unit2Cards + .map(( + e, + ) { + int index = + unit2Cards + .indexOf(e); + //// if unit2 cards is less then 3 + return AnimationConfiguration + .staggeredGrid( + position: + index, + columnCount: + 3, + child: + ScaleAnimation( + child: + FadeInAnimation( + child: Container( + child: (e.roleName == 'superadmin' || e.roleName == 'qr code scanner' || e.roleName == 'security guard' || e.roleName == 'establishment point-person' || e.roleName == 'registration in-charge') && e.moduleName == 'unit2' + ? CardLabel( + icon: iconGenerator(name: e.object.name!), + title: e.object.name!.toLowerCase() == 'role based access control' + ? "RBAC" + : e.object.name!.toLowerCase() == "person basic information" + ? "Basic Info" + : e.object.name!, + ontap: () { + if (e.object.name!.toLowerCase() == 'pass check') { + PassCheckArguments passCheckArguments = PassCheckArguments(roleId: 10, userId: widget.userId); + Navigator.pushNamed(context, '/pass-check', arguments: passCheckArguments); + } + if (e.object.name!.toLowerCase() == 'role based access control') { + Navigator.pushNamed(context, '/rbac'); + } + if (e.object.name!.toLowerCase() == 'agency') { + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) { + return BlocProvider( + create: (context) => AssignAreaAgencyBloc()..add((EstPointPersonGetAgencies(assignedAreas: widget.estPersonAssignedArea))), + child: const EstPorintPersonAgencyScreen(), + ); + })); + } + if (e.object.name == 'Station') { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text("Select Agency"), + content: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + DropdownButtonFormField( + isDense: true, + decoration: normalTextFieldStyle("select agency", "select agency"), + isExpanded: true, + items: widget.estPersonAssignedArea!.map((e) { + return DropdownMenuItem( + value: e, + child: FittedBox(child: Text(e.areaName!)), + ); + }).toList(), + onChanged: (value) { + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) { + return BlocProvider( + create: (context) => EstPointPersonStationBloc()..add( EstPointPersonGetStations(agencyId: value!.areaid!,assignedAreas: widget.estPersonAssignedArea!)), + child: const EstPointPersonStationScreen(), + ); + })); + }) + ], + ), + ); + }); + } + }) + : Container( + color: Colors.black, + )), + ), + ), + ); + }).toList()), + ), + ], + ), + ); + }); + }) + : (e.roleName == 'superadmin' || + e.roleName == + 'qr code scanner' || + e.roleName == + 'security guard' || + e.roleName == + 'establishment point-person' || + e.roleName == + 'registration in-charge') && + e.moduleName == 'unit2' + ? CardLabel( + icon: iconGenerator( + name: e.object.name!), + title: e.object.name! + .toLowerCase() == + 'role based access control' + ? "RBAC" + : e.object.name! + .toLowerCase() == + "person basic information" + ? "Basic Info" + : e.object.name!, + ontap: () { + if (e.object.name! + .toLowerCase() == + 'pass check') { + PassCheckArguments + passCheckArguments = + PassCheckArguments( + roleId: 10, + userId: + widget.userId); + Navigator.pushNamed( + context, '/pass-check', + arguments: + passCheckArguments); + } + if (e.object.name! + .toLowerCase() == + 'role based access control') { + Navigator.pushNamed( + context, '/rbac'); + } + + if (e.object.name! + .toLowerCase() == + 'agency') { + Navigator.push(context, + MaterialPageRoute( + builder: + (BuildContext + context) { + return BlocProvider( + create: (context) => + AssignAreaAgencyBloc() + ..add((EstPointPersonGetAgencies( + assignedAreas: + widget + .estPersonAssignedArea))), + child: + const EstPorintPersonAgencyScreen(), + ); + })); + } + }) + : Container( + color: Colors.black, + )); + }).toList() + : unit2Cards.map(( + e, + ) { + ////if unit2 cards is greater than 3 + return Container( + child: (e.roleName == 'superadmin' || + e.roleName == + 'qr code scanner' || + e.roleName == + 'security guard' || + e.roleName == + 'establishment point-person' || + e.roleName == + 'registration in-charge') && + e.moduleName == 'unit2' + ? CardLabel( + icon: iconGenerator( + name: e.object.name!), + title: e.object.name! + .toLowerCase() == + 'role based access control' + ? "RBAC" + : e.object.name! + .toLowerCase() == + "person basic information" + ? "Basic Info" + : e.object.name!, + ontap: () { + if (e.object.name! + .toLowerCase() == + 'pass check') { + PassCheckArguments + passCheckArguments = + PassCheckArguments( + roleId: 10, + userId: widget.userId); + Navigator.pushNamed( + context, '/pass-check', + arguments: + passCheckArguments); + } + if (e.object.name! + .toLowerCase() == + 'role based access control') { + Navigator.pushNamed( + context, '/rbac'); + } + + if (e.object.name! + .toLowerCase() == + 'agency') { + Navigator.push(context, + MaterialPageRoute(builder: + (BuildContext context) { + return BlocProvider( + create: (context) => + AssignAreaAgencyBloc() + ..add((EstPointPersonGetAgencies( + assignedAreas: widget + .estPersonAssignedArea))), + child: + const EstPorintPersonAgencyScreen(), + ); + })); + } + }) + : Container( + color: Colors.black, + )); + }).toList(), + ), + ), + SizedBox( + height: unit2Cards.isEmpty ? 0 : 24, + ), + Container( + child: superadminCards.isEmpty + ? const SizedBox() + : Text( + "Superadmin module operations", + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith( + fontSize: 16, + color: primary, + fontWeight: FontWeight.w300), + ), + ), + SizedBox( + height: superadminCards.isEmpty ? 0 : 8, + ), + Container( + child: superadminCards.isEmpty + ? const SizedBox() + : GridView.count( + shrinkWrap: true, + crossAxisCount: 4, + crossAxisSpacing: 8, + mainAxisSpacing: 10, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.symmetric( + vertical: 5, horizontal: 5), + children: superadminCards.length > 3 + //// in superadmincards lenght is greaterthan 3 + ? tempSuperAdminCards.map((e) { + int index = tempSuperAdminCards.indexOf(e); + return Container( + child: index == 3 + ? CardLabel( + icon: FontAwesome5 + .chevron_circle_right, + title: "See More", + ontap: () { + showDialog( + context: context, + builder: + (BuildContext context) { + return AlertDialog( + title: const Text( + "Super Admin Module Operations", + textAlign: + TextAlign.center, + ), + content: Column( + mainAxisSize: + MainAxisSize.min, + children: [ + SizedBox( + height: 480, + width: double + .maxFinite, + child: GridView + .count( + shrinkWrap: + true, + crossAxisCount: + 3, + crossAxisSpacing: + 8, + mainAxisSpacing: + 10, + physics: + const BouncingScrollPhysics(), + padding: const EdgeInsets + .symmetric( + vertical: 5, + horizontal: + 5), + children: + superadminCards + .map( + (e) { + int index = + superadminCards + .indexOf( + e); + return SuperAdminMenu( + id: widget + .userId, + columnCount: + 3, + index: + index, + object: e, + ); + }).toList(), ), ), - ); - }).toList()), - ), - ], - ), - ); - }); - }) - : (e.roleName == 'superadmin' || - e.roleName == 'qr code scanner' || - e.roleName == 'security guard' || - e.roleName == - 'establishment point-person' || - e.roleName == - 'registration in-charge') && - e.moduleName == 'unit2' - ? CardLabel( - icon: - iconGenerator(name: e.object.name!), - title: e.object.name!.toLowerCase() == - 'role based access control' - ? "RBAC" - : e.object.name!.toLowerCase() == - "person basic information" - ? "Basic Info" - : e.object.name!, - ontap: () { - if (e.object.name!.toLowerCase() == - 'pass check') { - PassCheckArguments - passCheckArguments = - PassCheckArguments( - roleId: 10, - userId: widget.userId); - Navigator.pushNamed( - context, '/pass-check', - arguments: passCheckArguments); - } - if (e.object.name!.toLowerCase() == - 'role based access control') { - Navigator.pushNamed( - context, '/rbac'); - } - }) - : Container( - color: Colors.black, - )); - }).toList() - : unit2Cards.map(( - e, - ) { - ////if unit2 cards is greater than 3 - return Container( - child: (e.roleName == 'superadmin' || - e.roleName == 'qr code scanner' || - e.roleName == 'security guard' || - e.roleName == - 'establishment point-person' || - e.roleName == - 'registration in-charge') && - e.moduleName == 'unit2' - ? CardLabel( - icon: iconGenerator(name: e.object.name!), - title: e.object.name!.toLowerCase() == - 'role based access control' - ? "RBAC" - : e.object.name!.toLowerCase() == - "person basic information" - ? "Basic Info" - : e.object.name!, - ontap: () { - if (e.object.name!.toLowerCase() == - 'pass check') { - PassCheckArguments passCheckArguments = - PassCheckArguments( - roleId: 10, - userId: widget.userId); - Navigator.pushNamed( - context, '/pass-check', - arguments: passCheckArguments); - } - if (e.object.name!.toLowerCase() == - 'role based access control') { - Navigator.pushNamed(context, '/rbac'); - } - }) - : Container( - color: Colors.black, - )); - }).toList(), - ),), - SizedBox( - height: unit2Cards.isEmpty?0:24, - ), - Container(child: superadminCards.isEmpty?const SizedBox(): Text( - "Superadmin module operations", - style: Theme.of(context).textTheme.displaySmall!.copyWith( - fontSize: 16, color: primary, fontWeight: FontWeight.w300), - ),), - SizedBox( - height: superadminCards.isEmpty? 0: 8, - ), - Container(child: superadminCards.isEmpty?const SizedBox():GridView.count( - shrinkWrap: true, - crossAxisCount: 4, - crossAxisSpacing: 8, - mainAxisSpacing: 10, - physics: const BouncingScrollPhysics(), - padding: - const EdgeInsets.symmetric(vertical: 5, horizontal: 5), - children: superadminCards.length > 3 - //// in superadmincards lenght is greaterthan 3 - ? tempSuperAdminCards.map((e) { - int index = tempSuperAdminCards.indexOf(e); - return Container( - child: index == 3 - ? CardLabel( - icon: FontAwesome5.chevron_circle_right, - title: "See More", - ontap: () { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: const Text( - "Super Admin Module Operations", - textAlign: TextAlign.center, - ), - content: Column( - mainAxisSize: - MainAxisSize.min, - children: [ - SizedBox( - height: 480, - width: double.maxFinite, - child: GridView.count( - shrinkWrap: true, - crossAxisCount: 3, - crossAxisSpacing: 8, - mainAxisSpacing: 10, - physics: - const BouncingScrollPhysics(), - padding: - const EdgeInsets - .symmetric( - vertical: 5, - horizontal: 5), - children: - superadminCards - .map((e) { - int index = - superadminCards - .indexOf(e); - return SuperAdminMenu( - id: widget.userId, - columnCount: 3, - index: index, - object: e, - ); - }).toList(), - ), - ), - ], - ), - ); - }); - }) - : SuperAdminMenu( - id: widget.userId, - object: e, - index: index, - columnCount: 3, - )); - }).toList() - //// in superadmincards lenght is lessthan 3 - : superadminCards.map((e) { - int index = tempSuperAdminCards.indexOf(e); - return Container( - child: index == 3 - ? CardLabel( - icon: FontAwesome5.chevron_circle_right, - title: "See More", - ontap: () { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: const Text( - "Super Admin Module Operations", - textAlign: TextAlign.center, - ), - content: Column( - mainAxisSize: - MainAxisSize.min, - children: [ - SizedBox( - height: 480, - width: double.maxFinite, - child: GridView.count( - shrinkWrap: true, - crossAxisCount: 3, - crossAxisSpacing: 8, - mainAxisSpacing: 10, - physics: - const BouncingScrollPhysics(), - padding: - const EdgeInsets - .symmetric( - vertical: 5, - horizontal: 5), - children: - superadminCards - .map((e) { - return SuperAdminMenu( - id: widget.userId, - columnCount: 4, - index: index, - object: e, - ); - }).toList(), - ), - ), - ], - ), - ); - }); - }) - : SuperAdminMenu( - id: widget.userId, - object: e, - index: index, - columnCount: 4, - )); - }).toList())), + ], + ), + ); + }); + }) + : SuperAdminMenu( + id: widget.userId, + object: e, + index: index, + columnCount: 3, + )); + }).toList() + //// in superadmincards lenght is lessthan 3 + : superadminCards.map((e) { + int index = tempSuperAdminCards.indexOf(e); + return Container( + child: index == 3 + ? CardLabel( + icon: FontAwesome5 + .chevron_circle_right, + title: "See More", + ontap: () { + showDialog( + context: context, + builder: + (BuildContext context) { + return AlertDialog( + title: const Text( + "Super Admin Module Operations", + textAlign: + TextAlign.center, + ), + content: Column( + mainAxisSize: + MainAxisSize.min, + children: [ + SizedBox( + height: 480, + width: double + .maxFinite, + child: GridView + .count( + shrinkWrap: + true, + crossAxisCount: + 3, + crossAxisSpacing: + 8, + mainAxisSpacing: + 10, + physics: + const BouncingScrollPhysics(), + padding: const EdgeInsets + .symmetric( + vertical: 5, + horizontal: + 5), + children: + superadminCards + .map( + (e) { + return SuperAdminMenu( + id: widget + .userId, + columnCount: + 4, + index: + index, + object: e, + ); + }).toList(), + ), + ), + ], + ), + ); + }); + }) + : SuperAdminMenu( + id: widget.userId, + object: e, + index: index, + columnCount: 4, + )); + }).toList())), const SizedBox( height: 24, ), - Container(child: rpassCards.isEmpty?const SizedBox(): Text( - "RPAss module operations", - style: Theme.of(context).textTheme.displaySmall!.copyWith( - fontSize: 16, color: primary, fontWeight: FontWeight.w300), - ),) -, SizedBox( - height:rpassCards.isEmpty?0: 8, + Container( + child: rpassCards.isEmpty + ? const SizedBox() + : Text( + "RPAss module operations", + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith( + fontSize: 16, + color: primary, + fontWeight: FontWeight.w300), + ), + ), + SizedBox( + height: rpassCards.isEmpty ? 0 : 8, + ), + Container( + child: rpassCards.isEmpty + ? const SizedBox() + : GridView.count( + shrinkWrap: true, + crossAxisCount: 4, + crossAxisSpacing: 8, + mainAxisSpacing: 10, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.symmetric( + vertical: 5, horizontal: 5), + children: rpassCards.map((e) { + return Container( + child: (e.roleName == 'field surveyor') && + e.moduleName == 'rpass' + ? CardLabel( + icon: iconGenerator(name: e.object.name!), + title: e.object.name == 'Real Property' + ? "Field Surveyor" + : e.object.name!, + ontap: () {}) + : Container( + color: Colors.black, + )); + }).toList(), + ), ), - Container(child: rpassCards.isEmpty?const SizedBox():GridView.count( - shrinkWrap: true, - crossAxisCount: 4, - crossAxisSpacing: 8, - mainAxisSpacing: 10, - physics: const BouncingScrollPhysics(), - padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 5), - children: rpassCards.map((e) { - return Container( - child: (e.roleName == 'field surveyor') && - e.moduleName == 'rpass' - ? CardLabel( - icon: iconGenerator(name: e.object.name!), - title: e.object.name == 'Real Property' - ? "Field Surveyor" - : e.object.name!, - ontap: () {}) - : Container( - color: Colors.black, - )); - }).toList(), - ),), const SizedBox( height: 24, ), - Container(child: docSmsCards.isEmpty?const SizedBox(): Text( - "DocSMS module operations", - style: Theme.of(context).textTheme.displaySmall!.copyWith( - fontSize: 16, color: primary, fontWeight: FontWeight.w300), - ),), + Container( + child: docSmsCards.isEmpty + ? const SizedBox() + : Text( + "DocSMS module operations", + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith( + fontSize: 16, + color: primary, + fontWeight: FontWeight.w300), + ), + ), const SizedBox( height: 8, ), @@ -456,9 +657,7 @@ class _DashBoardState extends State { title: e.object.name == "Document" ? "Process Server" : e.object.name!, - ontap: () { - - }) + ontap: () {}) : Container( color: Colors.black, )); diff --git a/lib/screens/unit2/homepage.dart/components/dashboard/superadmin_expanded_menu.dart b/lib/screens/unit2/homepage.dart/components/dashboard/superadmin_expanded_menu.dart index 23d42ca..ffcd01b 100644 --- a/lib/screens/unit2/homepage.dart/components/dashboard/superadmin_expanded_menu.dart +++ b/lib/screens/unit2/homepage.dart/components/dashboard/superadmin_expanded_menu.dart @@ -157,7 +157,7 @@ class SuperAdminMenu extends StatelessWidget { create: (context) => AgencyBloc()..add(GetAgencies()), child: RbacAgencyScreen( - id: id, + ), ); })); diff --git a/lib/screens/unit2/homepage.dart/module-screen.dart b/lib/screens/unit2/homepage.dart/module-screen.dart index a41389a..26762d5 100644 --- a/lib/screens/unit2/homepage.dart/module-screen.dart +++ b/lib/screens/unit2/homepage.dart/module-screen.dart @@ -74,6 +74,7 @@ class _MainScreenState extends State { ), body: state.userData!.user!.login!.user!.roles!.isNotEmpty ? DashBoard( + estPersonAssignedArea: state.estPersonAssignedArea, userId: userId!, cards: cards, ) diff --git a/lib/screens/unit2/roles/establishment_point_person/est_point_person_agecies.dart b/lib/screens/unit2/roles/establishment_point_person/est_point_person_agecies.dart new file mode 100644 index 0000000..a58c61f --- /dev/null +++ b/lib/screens/unit2/roles/establishment_point_person/est_point_person_agecies.dart @@ -0,0 +1,248 @@ +import 'package:app_popup_menu/app_popup_menu.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:fluttericon/font_awesome_icons.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:searchfield/searchfield.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/agency/agency_bloc.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/object/object_bloc.dart'; +import 'package:unit2/bloc/role/pass_check/est_point_person/assign_area/assign_area_agency_bloc.dart'; +import 'package:unit2/model/utils/category.dart'; +import 'package:unit2/widgets/error_state.dart'; +import '../../../../model/utils/agency.dart'; +import '../../../../theme-data.dart/box_shadow.dart'; +import '../../../../theme-data.dart/btn-style.dart'; +import '../../../../theme-data.dart/colors.dart'; +import '../../../../theme-data.dart/form-style.dart'; +import '../../../../utils/global.dart'; +import '../../../../utils/global_context.dart'; +import '../../../../widgets/Leadings/add_leading.dart'; +import '../../../../widgets/empty_data.dart'; + +class EstPorintPersonAgencyScreen extends StatelessWidget { + const EstPorintPersonAgencyScreen({super.key}); + + @override + Widget build(BuildContext context) { + final formKey = GlobalKey(); + List agencyCategory = []; + Category? selectedAgencyCategory; + bool? isPrivate; + final agencyCategoryFocusNode = FocusNode(); + BuildContext parent; + return Scaffold( + appBar: AppBar( + backgroundColor: primary, + title: const Text("Agencies"), + actions: [ + AddLeading(onPressed: () { + parent = context; + showDialog( + context: NavigationService.navigatorKey.currentContext!, + builder: (BuildContext context) { + return AlertDialog( + title: const Text("Add Agency"), + content: FormBuilder( + key: formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "name", + decoration: normalTextFieldStyle( + "Agency name ", "Agency name"), + ), + const SizedBox( + height: 12, + ), + SearchField( + focusNode: agencyCategoryFocusNode, + itemHeight: 80, + suggestions: agencyCategory + .map((Category category) => + SearchFieldListItem(category.name!, + item: category, + child: ListTile( + title: Text(category.name!), + subtitle: Text( + category.industryClass!.name!), + ))) + .toList(), + emptyWidget: Container( + height: 100, + decoration: box1(), + child: const Center( + child: Text("No result found ...")), + ), + onSuggestionTap: (agencyCategory) { + selectedAgencyCategory = agencyCategory.item; + agencyCategoryFocusNode.unfocus(); + }, + searchInputDecoration: + normalTextFieldStyle("Category *", "") + .copyWith( + suffixIcon: IconButton( + icon: const Icon(Icons.arrow_drop_down), + onPressed: () { + agencyCategoryFocusNode.unfocus(); + }, + )), + validator: (value) { + if (value!.isEmpty) { + return "This field is required"; + } + return null; + }, + ), + FormBuilderRadioGroup( + decoration: InputDecoration( + border: InputBorder.none, + label: Row( + children: [ + Text( + "Is this private sector? ", + style: Theme.of(context) + .textTheme + .headlineSmall! + .copyWith(fontSize: 24), + ), + const Icon(FontAwesome.help_circled) + ], + ), + ), + + ////onvhange private sector + onChanged: (value) { + if (value.toString() == "YES") { + isPrivate = true; + } else { + isPrivate = false; + } + }, + + name: 'isPrivate', + validator: FormBuilderValidators.required(), + options: ["YES", "NO"] + .map((lang) => + FormBuilderFieldOption(value: lang)) + .toList(growable: false), + ), + const SizedBox( + height: 12, + ), + SizedBox( + height: 50, + width: double.maxFinite, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState! + .saveAndValidate()) { + String name = + formKey.currentState!.value['name']; + parent.read().add( + EstPointPersonAddAgency( + agency: Agency( + category: + selectedAgencyCategory, + id: null, + name: name, + privateEntity: isPrivate))); + Navigator.pop(context); + } + }, + child: const Text("Add")), + ) + ], + )), + ); + }); + }) + ], + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocConsumer( + listener: (context, state) { + if (state is EstPointPersonAgencyLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is EstPointPersonAgenciesLoaded || + state is EstPointPersonAgencyAddesState || + state is EstPointAgencyErrorState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + }, + builder: (context, state) { + final parent = context; + if (state is EstPointPersonAgenciesLoaded) { + agencyCategory = state.agencyCategory; + if (state.agencies != null && state.agencies!.isNotEmpty) { + return ListView.builder( + padding: + const EdgeInsets.symmetric(vertical: 8, horizontal: 10), + itemCount: state.agencies!.length, + itemBuilder: (BuildContext context, int index) { + return Column( + children: [ + Container( + width: screenWidth, + decoration: box1(), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + child: Expanded( + child: Row( + children: [ + CircleAvatar( + child: Text('${index + 1}'), + ), + const SizedBox( + width: 12, + ), + Flexible( + child: Text(state.agencies![index].areaName!, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: FontWeight.w500, + color: primary)), + ), + ], + )), + ), + const SizedBox( + height: 5, + ) + ], + ); + }); + } else { + return const EmptyData( + message: "No Object available. Please click + to add."); + } + } + if (state is EstPointAgencyErrorState) { + return SomethingWentWrong( + message: state.message, + onpressed: () { + context.read().add(GetObjects()); + }); + } + return Container(); + }, + ), + ), + ); + } +} diff --git a/lib/screens/unit2/roles/establishment_point_person/est_point_person_station.dart b/lib/screens/unit2/roles/establishment_point_person/est_point_person_station.dart new file mode 100644 index 0000000..b467e33 --- /dev/null +++ b/lib/screens/unit2/roles/establishment_point_person/est_point_person_station.dart @@ -0,0 +1,536 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:searchfield/searchfield.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/role/role_bloc.dart'; +import 'package:unit2/model/login_data/user_info/assigned_area.dart'; +import 'package:unit2/model/rbac/rbac_station.dart'; +import 'package:unit2/widgets/Leadings/add_leading.dart'; +import 'package:unit2/widgets/error_state.dart'; +import '../../../../bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_bloc.dart'; +import '../../../../model/utils/agency.dart'; +import '../../../../theme-data.dart/box_shadow.dart'; +import '../../../../theme-data.dart/colors.dart'; +import '../../../../theme-data.dart/form-style.dart'; +import '../../../../utils/formatters.dart'; +import '../../../../utils/global.dart'; +import '../../../../widgets/empty_data.dart'; + +class EstPointPersonStationScreen extends StatelessWidget { + const EstPointPersonStationScreen({ + super.key, + }); + + @override + Widget build(BuildContext context) { + final agencyFocusNode = FocusNode(); + List stations = []; + final formKey = GlobalKey(); + Agency selectedAgency; + List> hierarchy = []; + return Scaffold( + appBar: AppBar( + centerTitle: true, + backgroundColor: primary, + title: const Text("Station Screen"), + actions: [ + AddLeading(onPressed: () { + BuildContext parent = context; + // showDialog( + // context: context, + // builder: (BuildContext context) { + // return AlertDialog( + // title: const Text("Add New Station"), + // content: FormBuilder( + // key: formKey, + // child: Column( + // mainAxisSize: MainAxisSize.min, + // children: [ + // FormBuilderTextField( + // name: "object_name", + // decoration: normalTextFieldStyle( + // "Role name *", "Role name "), + // validator: FormBuilderValidators.required( + // errorText: "This field is required"), + // ), + // const SizedBox( + // height: 8, + // ), + // FormBuilderTextField( + // name: "slug", + // decoration: normalTextFieldStyle("Slug ", "Slug"), + // ), + // const SizedBox( + // height: 8, + // ), + // FormBuilderTextField( + // validator: FormBuilderValidators.maxLength(50, + // errorText: "Max characters only 50"), + // name: "shorthand", + // decoration: + // normalTextFieldStyle("Shorthand ", "Shorthand"), + // ), + // const SizedBox( + // height: 12, + // ), + // SizedBox( + // width: double.infinity, + // height: 50, + // child: ElevatedButton( + // style: mainBtnStyle( + // primary, Colors.transparent, second), + // onPressed: () { + // if (formKey.currentState! + // .saveAndValidate()) { + // String name = formKey + // .currentState!.value['object_name']; + // String? slug = + // formKey.currentState!.value['slug']; + // String? short = formKey + // .currentState!.value['shorthand']; + // parent.read().add(AddRbacRole( + // id: id, + // name: name, + // shorthand: short, + // slug: slug)); + // Navigator.pop(context); + // } + // }, + // child: const Text("Add"))), + // ], + // ), + // ), + // ); + // }); + }) + ], + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: + BlocConsumer( + listener: (context, state) { + if (state is EstPersonStationLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is EstPersonStationLoadedState || + state is EstPersonStationErrorState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + }, + builder: (context, state) { + final parent = context; + if (state is EstPersonStationLoadedState) { + stations = state.stations; + int max = 0; + for (RbacStation station in stations) { + if (station.hierarchyOrderNo != null) { + if (max < station.hierarchyOrderNo!) { + max = station.hierarchyOrderNo!; + } + } + } + for (int i = 1; i <= max; i++) { + hierarchy.add({i: []}); + } + for (var station in stations) { + if (station.hierarchyOrderNo != null) { + for (int i = 0; i <= max; i++) { + if (station.hierarchyOrderNo == i + 1) { + hierarchy[i][i + 1].add(station); + } + } + } + } + + if (hierarchy[0][1].isNotEmpty) { + return Column( + children: [ + Padding( + padding: const EdgeInsets.all(8), + child: SearchField( + inputFormatters: [UpperCaseTextFormatter()], + itemHeight: 70, + focusNode: agencyFocusNode, + suggestions: state.assignedAreas + .map((AssignedArea agency) => + SearchFieldListItem(agency.areaName!, + item: agency, + child: ListTile( + title: Text( + agency.areaName!, + overflow: TextOverflow.visible, + ), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle("Filter", "").copyWith( + prefixIcon: const Icon(Icons.filter_list), + suffixIcon: IconButton( + icon: const Icon(Icons.arrow_drop_down), + onPressed: () { + agencyFocusNode.unfocus(); + }, + )), + onSuggestionTap: (agency) { + agencyFocusNode.unfocus(); + }, + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + emptyWidget: const Center( + child: Text("No result found..."), + )), + ), + Expanded( + child: ListView.builder( + padding: const EdgeInsets.symmetric( + vertical: 8, horizontal: 10), + itemCount: hierarchy[0][1].length, + itemBuilder: (BuildContext context, int index) { + List second = []; + for (var rbacStation in hierarchy[1][2]) { + if (rbacStation.parentStation == + hierarchy[0][1][index].id) { + second.add(rbacStation); + } + } + return Column( + children: [ + Container( + width: screenWidth, + decoration: box1(), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + child: Row( + children: [ + Expanded( + child: Row( + children: [ + const CircleAvatar( + child: Text('1'), + ), + const SizedBox( + width: 12, + ), + Flexible( + child: Text( + hierarchy[0][1][index] + .stationName!, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: + FontWeight.w500, + color: primary)), + ), + ], + )), + ], + ), + ), + ////SECOND + SizedBox( + child: second.isNotEmpty + ? Column( + mainAxisSize: MainAxisSize.min, + children: second.map((e) { + List childs = []; + if (max >= 3) { + for (RbacStation station + in hierarchy[2][3]) { + if (station.parentStation == + e.id) { + childs.add(station); + } + } + } else { + childs = []; + } + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + Expanded( + child: Container( + width: screenWidth, + decoration: box1(), + padding: + const EdgeInsets + .only( + left: 30), + child: Row( + children: [ + Expanded( + child: Row( + children: [ + const CircleAvatar( + child: Text( + '2'), + ), + const SizedBox( + width: 12, + ), + Flexible( + child: Text( + e + .stationName!, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: FontWeight.w500, + color: primary)), + ), + ], + )), + ], + ), + ), + ), + ], + ), + ////THIRD + SizedBox( + child: childs.isNotEmpty + ? Column( + mainAxisSize: + MainAxisSize + .min, + children: childs + .map((e) { + List + childs = []; + if (max >= 4) { + for (RbacStation station + in hierarchy[3][4]) { + if (station.parentStation == + e.id) { + childs.add(station); + } + } + } else { + childs = []; + } + return Column( + children: [ + Container( + width: + screenWidth, + decoration: + box1(), + padding: const EdgeInsets + .only( + left: + 50), + child: + Row( + children: [ + Expanded( + child: Row( + children: [ + const CircleAvatar( + child: Text('3'), + ), + const SizedBox( + width: 12, + ), + Flexible( + child: Text(e.stationName!, style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500, color: primary)), + ), + ], + )), + ], + ), + ), + ////Fourth + SizedBox( + child: childs.isNotEmpty + ? Column( + mainAxisSize: MainAxisSize.min, + children: childs.map((e) { + List childs = []; + if (max > 4) { + for (RbacStation station in hierarchy[4][5]) { + if (station.parentStation == e.id) { + childs.add(station); + } + } + } else { + childs = []; + } + return Column( + children: [ + Container( + width: screenWidth, + decoration: box1(), + padding: const EdgeInsets.only(left: 80), + child: Row( + children: [ + Expanded( + child: Row( + children: [ + const CircleAvatar( + child: Text('4'), + ), + const SizedBox( + width: 12, + ), + Flexible( + child: Text(e.stationName!, style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500, color: primary)), + ), + ], + )), + ], + ), + ), + ////Fifth + SizedBox( + child: childs.isNotEmpty + ? Column( + mainAxisSize: MainAxisSize.min, + children: childs.map((e) { + List childs = []; + if (max > 5) { + for (RbacStation station in hierarchy[5][6]) { + if (station.parentStation == e.id) { + childs.add(station); + } + } + } else { + childs = []; + } + + return Column( + children: [ + Container( + width: screenWidth, + decoration: box1(), + padding: const EdgeInsets.only(left: 80), + child: Row( + children: [ + Expanded( + child: Row( + children: [ + const CircleAvatar( + child: Text('5'), + ), + const SizedBox( + width: 12, + ), + Flexible( + child: Text(e.stationName!, style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500, color: primary)), + ), + ], + )), + ], + ), + ), + ], + ); + }).toList(), + ) + : const SizedBox()), + ], + ); + }).toList(), + ) + : const SizedBox()), + ], + ); + }).toList(), + ) + : const SizedBox + .shrink()), + ], + ); + }).toList(), + ) + : const SizedBox()), + const Divider( + height: 5, + ), + ], + ); + }), + ), + ], + ); + } else { + return Column( + children: [ + Padding( + padding: const EdgeInsets.all(8), + child: SearchField( + inputFormatters: [UpperCaseTextFormatter()], + itemHeight: 70, + focusNode: agencyFocusNode, + suggestions: state.assignedAreas + .map((AssignedArea agency) => + SearchFieldListItem(agency.areaName!, + item: agency, + child: ListTile( + title: Text( + agency.areaName!, + overflow: TextOverflow.visible, + ), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle("Filter", "").copyWith( + prefixIcon: const Icon(Icons.filter_list), + suffixIcon: IconButton( + icon: const Icon(Icons.arrow_drop_down), + onPressed: () { + agencyFocusNode.unfocus(); + }, + )), + onSuggestionTap: (agency) { + agencyFocusNode.unfocus(); + }, + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + emptyWidget: const Center( + child: Text("No result found..."), + )), + ), + const SizedBox( + height: 20, + ), + const EmptyData( + message: + "No Station available. Please click + to add."), + ], + ); + } + } + if (state is EstPersonStationErrorState) { + return SomethingWentWrong( + message: state.message, + onpressed: () { + context.read().add(GetRoles()); + }); + } + + return Container(); + }, + ), + ), + ); + } +} diff --git a/lib/sevices/roles/rbac_operations/station_services.dart b/lib/sevices/roles/rbac_operations/station_services.dart index 9170583..0bfeec2 100644 --- a/lib/sevices/roles/rbac_operations/station_services.dart +++ b/lib/sevices/roles/rbac_operations/station_services.dart @@ -2,7 +2,6 @@ import 'dart:convert'; import 'package:unit2/utils/request.dart'; import 'package:unit2/utils/urls.dart'; import 'package:http/http.dart' as http; - import '../../../model/rbac/rbac_station.dart'; import '../../../model/roles/pass_check/station_assign_area.dart'; class RbacStationServices{ @@ -11,7 +10,7 @@ class RbacStationServices{ String xClientKey = "unitK3CQaXiWlPReDsBzmmwBZPd9Re1z"; String xClientKeySecret = "unitcYqAN7GGalyz"; - Future> getStations({required int agencyId})async{ + Future> getStations({required String agencyId})async{ List stations = []; String path = Url.instance.getStation(); Map param = {"government_agency_id":agencyId.toString()}; diff --git a/pubspec.lock b/pubspec.lock index fd4f759..3544957 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -417,6 +417,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.5" + expandable: + dependency: "direct main" + description: + name: expandable + sha256: "9604d612d4d1146dafa96c6d8eec9c2ff0994658d6d09fed720ab788c7f5afc2" + url: "https://pub.dev" + source: hosted + version: "5.0.1" expandable_group: dependency: "direct main" description: @@ -571,6 +579,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.2" + flutter_simple_treeview: + dependency: "direct main" + description: + name: flutter_simple_treeview + sha256: ad4978d2668dd078d3a09966832da111bef9102dd636e572c50c80133b7ff4d9 + url: "https://pub.dev" + source: hosted + version: "3.0.2" flutter_spinkit: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index c5a2cc0..ce54884 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -87,6 +87,9 @@ dependencies: group_list_view: ^1.1.1 search_page: ^2.3.0 file_picker: ^5.3.1 + expandable: ^5.0.1 + flutter_simple_treeview: ^3.0.2 + dev_dependencies: From de4107e4e83b4670d235b77ee4bdbcb98d49d340 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Tue, 15 Aug 2023 14:32:21 +0800 Subject: [PATCH 79/86] add station to Establishment Point Person Role --- .../voluntary_works/voluntary_work_bloc.dart | 8 +- .../voluntary_works/voluntary_work_state.dart | 6 +- .../profile/workHistory/workHistory_bloc.dart | 6 +- .../workHistory/workHistory_state.dart | 4 +- .../est_point_person_station_bloc.dart | 47 +- .../est_point_person_station_event.dart | 10 +- .../est_point_person_station_state.dart | 12 +- lib/bloc/user/user_bloc.dart | 58 +- lib/model/profile/family_backround.dart | 4 +- lib/model/profile/voluntary_works.dart | 4 +- lib/model/profile/work_history.dart | 4 +- lib/model/rbac/rbac_station.dart | 20 +- lib/model/rbac/station_type.dart | 0 .../roles/pass_check/station_assign_area.dart | 49 +- lib/model/utils/position.dart | 6 +- .../family/child_add_modal.dart | 2 +- .../family/child_edit_modal.dart | 2 +- .../family/father_add_modal.dart | 2 +- .../family/father_edit_modal.dart | 2 +- .../family/mother_add_modal.dart | 2 +- .../family/mother_edit_modal.dart | 2 +- .../family/related_add_modal.dart | 2 +- .../family/related_edit_modal.dart | 2 +- .../family/spouse_add_modal.dart | 12 +- .../family/spouse_edit_modal.dart | 12 +- .../components/family_background_screen.dart | 2 +- .../components/voluntary_works/add_modal.dart | 6 +- .../voluntary_works/edit_modal.dart | 6 +- .../components/work_history/add_modal.dart | 6 +- .../components/work_history/edit_modal.dart | 6 +- .../superadmin/stations/stations_screen.dart | 132 ++-- .../components/dashboard/dashboard.dart | 4 +- .../est_point_person_station.dart | 636 +++++++++++++----- .../profile/work_history_services.dart | 6 +- .../rbac_operations/station_services.dart | 125 +++- lib/utils/profile_utilities.dart | 6 +- lib/utils/urls.dart | 13 +- 37 files changed, 810 insertions(+), 416 deletions(-) create mode 100644 lib/model/rbac/station_type.dart diff --git a/lib/bloc/profile/voluntary_works/voluntary_work_bloc.dart b/lib/bloc/profile/voluntary_works/voluntary_work_bloc.dart index 9d519eb..5d32742 100644 --- a/lib/bloc/profile/voluntary_works/voluntary_work_bloc.dart +++ b/lib/bloc/profile/voluntary_works/voluntary_work_bloc.dart @@ -23,12 +23,12 @@ class VoluntaryWorkBloc extends Bloc { List globalCountries = []; List agencyCategory = []; List globalRegions = []; - List agencyPositions = []; + List agencyPositions = []; List agencies = []; List provinces = []; List cities = []; ///// current - Position currentPosition; + PositionTitle currentPosition; Agency currentAgency; Region? currentRegion; Country currentCountry; @@ -58,7 +58,7 @@ class VoluntaryWorkBloc extends Bloc { emit(VoluntaryWorkLoadingState()); //// POSITIONS if (agencyPositions.isEmpty) { - List positions = + List positions = await ProfileUtilities.instance.getAgencyPosition(); agencyPositions = positions; } @@ -151,7 +151,7 @@ class VoluntaryWorkBloc extends Bloc { try { //// POSITIONS if (agencyPositions.isEmpty) { - List positions = + List positions = await ProfileUtilities.instance.getAgencyPosition(); agencyPositions = positions; } diff --git a/lib/bloc/profile/voluntary_works/voluntary_work_state.dart b/lib/bloc/profile/voluntary_works/voluntary_work_state.dart index 4c3fc88..b489c79 100644 --- a/lib/bloc/profile/voluntary_works/voluntary_work_state.dart +++ b/lib/bloc/profile/voluntary_works/voluntary_work_state.dart @@ -42,14 +42,14 @@ class VoluntaryWorkEditedState extends VoluntaryWorkState{ } class EditVoluntaryWorks extends VoluntaryWorkState{ final VoluntaryWork work; - final List positions; + final List positions; final List agencies; final List agencyCategory; final List countries; final List regions; final List provinces; final List cities; - final Position currentPosition; + final PositionTitle currentPosition; final Agency currentAgency; final Region? currentRegion; final Country currentCountry; @@ -63,7 +63,7 @@ class EditVoluntaryWorks extends VoluntaryWorkState{ ////Adding State class AddVoluntaryWorkState extends VoluntaryWorkState{ - final List positions; + final List positions; final List agencies; final List agencyCategory; final List countries; diff --git a/lib/bloc/profile/workHistory/workHistory_bloc.dart b/lib/bloc/profile/workHistory/workHistory_bloc.dart index e9a44c8..176954a 100644 --- a/lib/bloc/profile/workHistory/workHistory_bloc.dart +++ b/lib/bloc/profile/workHistory/workHistory_bloc.dart @@ -17,7 +17,7 @@ part 'workHistory_state.dart'; class WorkHistoryBloc extends Bloc { List workExperiences = []; - List agencyPositions = []; + List agencyPositions = []; List agencies = []; List appointmentStatus = []; List agencyCategory = []; @@ -118,7 +118,7 @@ class WorkHistoryBloc extends Bloc { try { /////POSITIONS------------------------------------------ if (agencyPositions.isEmpty) { - List positions = + List positions = await WorkHistoryService.instance.getAgencyPosition(); agencyPositions = positions; } @@ -159,7 +159,7 @@ class WorkHistoryBloc extends Bloc { try { /////POSITIONS------------------------------------------ if (agencyPositions.isEmpty) { - List positions = + List positions = await WorkHistoryService.instance.getAgencyPosition(); agencyPositions = positions; } diff --git a/lib/bloc/profile/workHistory/workHistory_state.dart b/lib/bloc/profile/workHistory/workHistory_state.dart index 117d984..478dc2f 100644 --- a/lib/bloc/profile/workHistory/workHistory_state.dart +++ b/lib/bloc/profile/workHistory/workHistory_state.dart @@ -31,7 +31,7 @@ class WorkHistoryErrorState extends WorkHistoryState{ class AddWorkHistoryState extends WorkHistoryState{ - final List agencyPositions; + final List agencyPositions; final List agencies; final List agencyCategory; final List appointmentStatus; @@ -44,7 +44,7 @@ class AddWorkHistoryState extends WorkHistoryState{ class EditWorkHistoryState extends WorkHistoryState{ final WorkHistory workHistory; - final List agencyPositions; + final List agencyPositions; final List agencies; final List agencyCategory; final List appointmentStatus; diff --git a/lib/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_bloc.dart b/lib/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_bloc.dart index 104f2f3..1e1a4f9 100644 --- a/lib/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_bloc.dart +++ b/lib/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_bloc.dart @@ -1,6 +1,8 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:unit2/model/login_data/user_info/assigned_area.dart'; +import 'package:unit2/model/roles/pass_check/station_assign_area.dart'; +import 'package:unit2/model/utils/position.dart'; import '../../../../../model/rbac/rbac_station.dart'; import '../../../../../model/utils/agency.dart'; @@ -10,24 +12,51 @@ import '../../../../../utils/profile_utilities.dart'; part 'est_point_person_station_event.dart'; part 'est_point_person_station_state.dart'; -class EstPointPersonStationBloc extends Bloc { +class EstPointPersonStationBloc + extends Bloc { EstPointPersonStationBloc() : super(EstPointPersonStationInitial()) { - List stations = []; + List stations = []; List assignAreas = []; - + List stationTypes = []; + List positions = []; on((event, emit) async { emit(EstPersonStationLoadingState()); try { - if(stations.isEmpty){ + if (stations.isEmpty) { stations = await RbacStationServices.instance - .getStations(agencyId: event.agencyId); - } - assignAreas = event.assignedAreas; - emit(EstPersonStationLoadedState(stations: stations, assignedAreas: assignAreas)); + .getStations(agencyId: event.agencyId); + } + if (stationTypes.isEmpty) { + stationTypes = await RbacStationServices.instance.getStationTypes(); + } + if (positions.isEmpty) { + positions = await RbacStationServices.instance.getPositionTitle(); + } + + emit(EstPersonStationLoadedState( + stations: stations, + stationTypes: stationTypes, + positions: positions)); } catch (e) { emit(EstPersonStationErrorState(message: e.toString())); } }); - + on((event, emit) async { + emit(EstPersonStationLoadingState()); + // try { + Map statusResponse = await RbacStationServices + .instance + .addStation(station: event.station); + if (statusResponse['success']) { + RbacStation newStation = RbacStation.fromJson(statusResponse['data']); + stations.add(newStation); + emit(EstPointPersonAddedState(response: statusResponse)); + } else { + emit(EstPointPersonAddedState(response: statusResponse)); + } + // } catch (e) { + // emit(EstPersonStationErrorState(message: e.toString())); + // } + }); } } diff --git a/lib/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_event.dart b/lib/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_event.dart index 2e34f2e..adc933e 100644 --- a/lib/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_event.dart +++ b/lib/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_event.dart @@ -6,8 +6,14 @@ abstract class EstPointPersonStationEvent extends Equatable { @override List get props => []; } + class EstPointPersonGetStations extends EstPointPersonStationEvent { final String agencyId; - final List assignedAreas; - const EstPointPersonGetStations({required this.agencyId, required this.assignedAreas}); + const EstPointPersonGetStations( + {required this.agencyId}); +} + +class AddEstPointPersonStation extends EstPointPersonStationEvent { + final RbacStation station; + const AddEstPointPersonStation({required this.station}); } diff --git a/lib/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_state.dart b/lib/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_state.dart index 25a3128..7b092cd 100644 --- a/lib/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_state.dart +++ b/lib/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_state.dart @@ -10,11 +10,12 @@ abstract class EstPointPersonStationState extends Equatable { class EstPointPersonStationInitial extends EstPointPersonStationState {} class EstPersonStationLoadedState extends EstPointPersonStationState { - final List assignedAreas; final List stations; - const EstPersonStationLoadedState({required this.stations, required this.assignedAreas}); + final List stationTypes; + final List positions; + const EstPersonStationLoadedState({required this.stations, required this.stationTypes, required this.positions}); @override - List get props => [assignedAreas,stations]; + List get props => [stations]; } class EstPersonStationLoadingState extends EstPointPersonStationState {} @@ -24,4 +25,9 @@ class EstPersonStationErrorState extends EstPointPersonStationState { const EstPersonStationErrorState({required this.message}); @override List get props => [message]; +} + +class EstPointPersonAddedState extends EstPointPersonStationState{ + final Map response; + const EstPointPersonAddedState({required this.response}); } \ No newline at end of file diff --git a/lib/bloc/user/user_bloc.dart b/lib/bloc/user/user_bloc.dart index ff111b1..16ff38f 100644 --- a/lib/bloc/user/user_bloc.dart +++ b/lib/bloc/user/user_bloc.dart @@ -11,10 +11,8 @@ import 'package:unit2/model/login_data/version_info.dart'; import 'package:unit2/screens/unit2/login/functions/get_app_version.dart'; import 'package:unit2/sevices/login_service/auth_service.dart'; import 'package:unit2/utils/global.dart'; - import '../../utils/scanner.dart'; import '../../utils/text_container.dart'; - part 'user_event.dart'; part 'user_state.dart'; @@ -68,34 +66,42 @@ class UserBloc extends Bloc { ////userlogin on((event, emit) async { try { - Map response = await AuthService.instance - .webLogin(username: event.username, password: event.password); - if (response['status'] == true) { - UserData userData = UserData.fromJson(response['data']); - Role? estPointPerson = userData.user?.login?.user?.roles?.firstWhere( - (element) => - element?.name?.toLowerCase() == "establishment point-person", - ); - if (estPointPerson != null) { - estPointPerson.assignedArea!.forEach((element) { + Map response = await AuthService.instance + .webLogin(username: event.username, password: event.password); + if (response['status'] == true) { + UserData userData = UserData.fromJson(response['data']); + Role? estPointPerson; + if (userData.user?.login?.user?.roles != null && + userData.user!.login!.user!.roles!.isNotEmpty) { + userData.user!.login!.user!.roles!.forEach((element) { + if (element!.name!.toLowerCase() == 'establishment point-person') { + estPointPerson = element; + } + }); + if (estPointPerson != null && + estPointPerson!.assignedArea!.isNotEmpty) { + estPointPerson!.assignedArea!.forEach((element) { establishmentPointPersonAssignedAreas.add(element!); }); } - emit(UserLoggedIn( - estPersonAssignedArea: establishmentPointPersonAssignedAreas, - userData: userData, - success: true, - message: response['message'], - savedCredentials: save)); - } else { - emit(UserLoggedIn( - estPersonAssignedArea: establishmentPointPersonAssignedAreas, - userData: null, - success: false, - message: response['message'], - savedCredentials: save)); } - } on TimeoutException catch (_) { + + emit(UserLoggedIn( + estPersonAssignedArea: establishmentPointPersonAssignedAreas, + userData: userData, + success: true, + message: response['message'], + savedCredentials: save)); + } else { + emit(UserLoggedIn( + estPersonAssignedArea: establishmentPointPersonAssignedAreas, + userData: null, + success: false, + message: response['message'], + savedCredentials: save)); + } + } + on TimeoutException catch (_) { emit(InternetTimeout(message: timeoutError)); } on SocketException catch (_) { emit(InternetTimeout(message: timeoutError)); diff --git a/lib/model/profile/family_backround.dart b/lib/model/profile/family_backround.dart index 3afb910..fb8a397 100644 --- a/lib/model/profile/family_backround.dart +++ b/lib/model/profile/family_backround.dart @@ -27,7 +27,7 @@ class FamilyBackground { }); final Company? company; - final Position? position; + final PositionTitle? position; final Relationship? relationship; final RelatedPerson? relatedPerson; final String? companyAddress; @@ -41,7 +41,7 @@ class FamilyBackground { json["company"] == null ? null : Company.fromJson(json["company"]), position: json["position"] == null ? null - : Position.fromJson(json["position"]), + : PositionTitle.fromJson(json["position"]), relationship: json["relationship"] == null ? null : Relationship.fromJson(json["relationship"]), diff --git a/lib/model/profile/voluntary_works.dart b/lib/model/profile/voluntary_works.dart index c86d35b..491c776 100644 --- a/lib/model/profile/voluntary_works.dart +++ b/lib/model/profile/voluntary_works.dart @@ -27,7 +27,7 @@ class VoluntaryWork { final Agency? agency; final Address? address; final DateTime? toDate; - final Position? position; + final PositionTitle? position; final DateTime? fromDate; final double? totalHours; @@ -35,7 +35,7 @@ class VoluntaryWork { agency: json["agency"] == null ? null : Agency.fromJson(json["agency"]), address: json["address"] == null ? null : Address.fromJson(json["address"]), toDate: json["to_date"] == null? null : DateTime.parse(json['to_date']), - position: json["position"] == null ? null : Position.fromJson(json["position"]), + position: json["position"] == null ? null : PositionTitle.fromJson(json["position"]), fromDate: json["from_date"] == null ? null : DateTime.parse(json["from_date"]), totalHours: json["total_hours"], ); diff --git a/lib/model/profile/work_history.dart b/lib/model/profile/work_history.dart index b479a24..5e01642 100644 --- a/lib/model/profile/work_history.dart +++ b/lib/model/profile/work_history.dart @@ -32,7 +32,7 @@ class WorkHistory { final Agency? agency; final int? sgStep; final DateTime? toDate; - final Position? position; + final PositionTitle? position; final DateTime? fromDate; List? attachments; final int? salaryGrade; @@ -44,7 +44,7 @@ class WorkHistory { agency: json["agency"] == null ? null : Agency.fromJson(json["agency"]), sgStep: json["sg_step"], toDate: json["to_date"] == null ? null : DateTime.parse(json["to_date"]), - position: json["position"] == null ? null : Position.fromJson(json["position"]), + position: json["position"] == null ? null : PositionTitle.fromJson(json["position"]), fromDate: json["from_date"] == null ? null : DateTime.parse(json["from_date"]), attachments: json['attachments'] ==null?null: List.from(json["attachments"].map((x) => Attachment.fromJson(x))), salaryGrade: json["salary_grade"], diff --git a/lib/model/rbac/rbac_station.dart b/lib/model/rbac/rbac_station.dart index 2a73bd5..fa345c3 100644 --- a/lib/model/rbac/rbac_station.dart +++ b/lib/model/rbac/rbac_station.dart @@ -4,6 +4,8 @@ +import 'package:unit2/model/roles/pass_check/station_assign_area.dart'; + class RbacStation { final int? id; final String? stationName; @@ -143,22 +145,4 @@ class GovernmentAgency { }; } -class StationType { - final int? id; - final String? typeName; - StationType({ - required this.id, - required this.typeName, - }); - - factory StationType.fromJson(Map json) => StationType( - id: json["id"], - typeName: json["type_name"], - ); - - Map toJson() => { - "id": id, - "type_name": typeName, - }; -} diff --git a/lib/model/rbac/station_type.dart b/lib/model/rbac/station_type.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/model/roles/pass_check/station_assign_area.dart b/lib/model/roles/pass_check/station_assign_area.dart index cbece06..f620307 100644 --- a/lib/model/roles/pass_check/station_assign_area.dart +++ b/lib/model/roles/pass_check/station_assign_area.dart @@ -2,6 +2,8 @@ // // final assignArea = assignAreaFromJson(jsonString); +import '../../rbac/rbac_station.dart'; + class StationAssignArea { final bool? isactive; final Station? area; @@ -129,54 +131,39 @@ class ChildStationInfo { }; } -class GovernmentAgency { - final int? agencyid; - final String? agencyname; - final int? agencycatid; - final bool? privateEntity; - final int? contactinfoid; - - GovernmentAgency({ - required this.agencyid, - required this.agencyname, - required this.agencycatid, - required this.privateEntity, - required this.contactinfoid, - }); - - factory GovernmentAgency.fromJson(Map json) => GovernmentAgency( - agencyid: json["agencyid"], - agencyname: json["agencyname"], - agencycatid: json["agencycatid"], - privateEntity: json["private_entity"], - contactinfoid: json["contactinfoid"], - ); - - Map toJson() => { - "agencyid": agencyid, - "agencyname": agencyname, - "agencycatid": agencycatid, - "private_entity": privateEntity, - "contactinfoid": contactinfoid, - }; -} class StationType { final int? id; final String? typeName; + final String? color; + final int? order; + final bool? isActive; + final String? group; StationType({ required this.id, required this.typeName, + required this.color, + required this.order, + required this.isActive, + required this.group, }); factory StationType.fromJson(Map json) => StationType( id: json["id"], typeName: json["type_name"], + color: json["color"], + order: json["order"], + isActive: json["is_active"], + group: json["group"], ); Map toJson() => { "id": id, "type_name": typeName, + "color": color, + "order": order, + "is_active": isActive, + "group": group, }; } diff --git a/lib/model/utils/position.dart b/lib/model/utils/position.dart index ff7ba26..7051fae 100644 --- a/lib/model/utils/position.dart +++ b/lib/model/utils/position.dart @@ -1,5 +1,5 @@ -class Position { - Position({ +class PositionTitle { + PositionTitle({ this.id, this.title, }); @@ -7,7 +7,7 @@ class Position { final int? id; final String? title; - factory Position.fromJson(Map json) => Position( + factory PositionTitle.fromJson(Map json) => PositionTitle( id: json["id"], title: json["title"], ); diff --git a/lib/screens/profile/components/basic_information/family/child_add_modal.dart b/lib/screens/profile/components/basic_information/family/child_add_modal.dart index 29247b9..3c1afe2 100644 --- a/lib/screens/profile/components/basic_information/family/child_add_modal.dart +++ b/lib/screens/profile/components/basic_information/family/child_add_modal.dart @@ -299,7 +299,7 @@ class _ChildAlertState extends State { String? civilStatus = selectedCivilStatus =="NONE"?null:selectedCivilStatus; String? sex = selectedSex; Company? company; - Position? position; + PositionTitle? position; double? height = _formKey.currentState?.value['height']==null? null: double.tryParse( diff --git a/lib/screens/profile/components/basic_information/family/child_edit_modal.dart b/lib/screens/profile/components/basic_information/family/child_edit_modal.dart index 1321d9b..768f596 100644 --- a/lib/screens/profile/components/basic_information/family/child_edit_modal.dart +++ b/lib/screens/profile/components/basic_information/family/child_edit_modal.dart @@ -324,7 +324,7 @@ class _ChildEditAlertState extends State { String? civilStatus = selectedCivilStatus =="NONE"?null:selectedCivilStatus; String? sex = selectedSex; Company? company; - Position? position; + PositionTitle? position; double? height = _formKey.currentState?.value['height']==null? null: double.tryParse( diff --git a/lib/screens/profile/components/basic_information/family/father_add_modal.dart b/lib/screens/profile/components/basic_information/family/father_add_modal.dart index 9dbcc41..afd0032 100644 --- a/lib/screens/profile/components/basic_information/family/father_add_modal.dart +++ b/lib/screens/profile/components/basic_information/family/father_add_modal.dart @@ -306,7 +306,7 @@ class _FatherAlertState extends State { : selectedCivilStatus; String? sex = selectedSex; Company? company; - Position? position; + PositionTitle? position; double? height = _formKey.currentState?.value['height'] == null ? null diff --git a/lib/screens/profile/components/basic_information/family/father_edit_modal.dart b/lib/screens/profile/components/basic_information/family/father_edit_modal.dart index 9ae8848..939e014 100644 --- a/lib/screens/profile/components/basic_information/family/father_edit_modal.dart +++ b/lib/screens/profile/components/basic_information/family/father_edit_modal.dart @@ -341,7 +341,7 @@ class _FatherEditAlertState extends State { : selectedCivilStatus; String? sex = selectedSex; Company? company; - Position? position; + PositionTitle? position; double? height = _formKey.currentState?.value['height'] == null ? null diff --git a/lib/screens/profile/components/basic_information/family/mother_add_modal.dart b/lib/screens/profile/components/basic_information/family/mother_add_modal.dart index 7b56e20..19295cd 100644 --- a/lib/screens/profile/components/basic_information/family/mother_add_modal.dart +++ b/lib/screens/profile/components/basic_information/family/mother_add_modal.dart @@ -340,7 +340,7 @@ class _MotherAlertState extends State { : selectedCivilStatus; String? sex = selectedSex; Company? company; - Position? position; + PositionTitle? position; double? height = _formKey.currentState?.value['height'] == null ? null diff --git a/lib/screens/profile/components/basic_information/family/mother_edit_modal.dart b/lib/screens/profile/components/basic_information/family/mother_edit_modal.dart index 22c0ce6..73a74ed 100644 --- a/lib/screens/profile/components/basic_information/family/mother_edit_modal.dart +++ b/lib/screens/profile/components/basic_information/family/mother_edit_modal.dart @@ -344,7 +344,7 @@ class _MotherEditAlertState extends State { String? civilStatus = selectedCivilStatus =="NONE"?null:selectedCivilStatus; String? sex = selectedSex; Company? company; - Position? position; + PositionTitle? position; double? height = _formKey.currentState?.value['height']==null? null: double.tryParse( diff --git a/lib/screens/profile/components/basic_information/family/related_add_modal.dart b/lib/screens/profile/components/basic_information/family/related_add_modal.dart index 3738114..12f0170 100644 --- a/lib/screens/profile/components/basic_information/family/related_add_modal.dart +++ b/lib/screens/profile/components/basic_information/family/related_add_modal.dart @@ -335,7 +335,7 @@ bdayController.dispose(); : selectedCivilStatus; String? sex = selectedSex; Company? company; - Position? position; + PositionTitle? position; double? height = _formKey.currentState?.value['height'] == null ? null diff --git a/lib/screens/profile/components/basic_information/family/related_edit_modal.dart b/lib/screens/profile/components/basic_information/family/related_edit_modal.dart index b3e808a..97edaf4 100644 --- a/lib/screens/profile/components/basic_information/family/related_edit_modal.dart +++ b/lib/screens/profile/components/basic_information/family/related_edit_modal.dart @@ -362,7 +362,7 @@ class _RelatedEditAlertState extends State { : selectedCivilStatus; String? sex = selectedSex; Company? company; - Position? position; + PositionTitle? position; double? height = _formKey.currentState?.value['height'] == null ? null diff --git a/lib/screens/profile/components/basic_information/family/spouse_add_modal.dart b/lib/screens/profile/components/basic_information/family/spouse_add_modal.dart index 5abe49e..dca9326 100644 --- a/lib/screens/profile/components/basic_information/family/spouse_add_modal.dart +++ b/lib/screens/profile/components/basic_information/family/spouse_add_modal.dart @@ -26,7 +26,7 @@ class SpouseAlert extends StatefulWidget { final List sexes; final List bloodType; final List civilStatus; - final List positions; + final List positions; final List agencies; final List category; final FamilyBloc familyBloc; @@ -61,7 +61,7 @@ class _SpouseAlertState extends State { String? selectedSex; String? selectedBloodType; String? selectedCivilStatus; - Position? selectedPosition; + PositionTitle? selectedPosition; Category? selectedAgencyCategory; Agency? selectedAgency; bool deceased = false; @@ -351,7 +351,7 @@ class _SpouseAlertState extends State { itemHeight: 100, suggestionsDecoration: box1(), suggestions: widget.positions - .map((Position position) => + .map((PositionTitle position) => SearchFieldListItem( position.title!, item: position, @@ -394,8 +394,8 @@ class _SpouseAlertState extends State { controller: addPositionController, onpressed: () { setState(() { - Position newAgencyPosition = - Position( + PositionTitle newAgencyPosition = + PositionTitle( id: null, title: addPositionController @@ -726,7 +726,7 @@ class _SpouseAlertState extends State { name: selectedAgency?.name, category: selectedAgencyCategory, privateEntity: isPrivate); - Position? position = selectedPosition; + PositionTitle? position = selectedPosition; double? height = _formKey .currentState?.value['height'] == null diff --git a/lib/screens/profile/components/basic_information/family/spouse_edit_modal.dart b/lib/screens/profile/components/basic_information/family/spouse_edit_modal.dart index df5e3d3..5526016 100644 --- a/lib/screens/profile/components/basic_information/family/spouse_edit_modal.dart +++ b/lib/screens/profile/components/basic_information/family/spouse_edit_modal.dart @@ -23,7 +23,7 @@ class SpouseEditAlert extends StatefulWidget { final List sexes; final List bloodType; final List civilStatus; - final List positions; + final List positions; final List agencies; final List category; final FamilyBloc familyBloc; @@ -60,7 +60,7 @@ class _SpouseEditAlertState extends State { String? selectedSex; String? selectedBloodType; String? selectedCivilStatus; - Position? selectedPosition; + PositionTitle? selectedPosition; Category? selectedAgencyCategory; Agency? selectedAgency; bool deceased = false; @@ -385,7 +385,7 @@ class _SpouseEditAlertState extends State { itemHeight: 100, suggestionsDecoration: box1(), suggestions: widget.positions - .map((Position position) => + .map((PositionTitle position) => SearchFieldListItem( position.title!, item: position, @@ -427,8 +427,8 @@ class _SpouseEditAlertState extends State { controller: addPositionController, onpressed: () { setState(() { - Position newAgencyPosition = - Position( + PositionTitle newAgencyPosition = + PositionTitle( id: null, title: addPositionController @@ -767,7 +767,7 @@ class _SpouseEditAlertState extends State { name: selectedAgency?.name, category: selectedAgencyCategory, privateEntity: isPrivate); - Position? position = selectedPosition; + PositionTitle? position = selectedPosition; double? height = _formKey .currentState?.value['height'] == null diff --git a/lib/screens/profile/components/family_background_screen.dart b/lib/screens/profile/components/family_background_screen.dart index e680896..16c5280 100644 --- a/lib/screens/profile/components/family_background_screen.dart +++ b/lib/screens/profile/components/family_background_screen.dart @@ -98,7 +98,7 @@ class _FamilyBackgroundScreenState extends State { "TRANSGENDER", "OTHERS" ]; - List positions = []; + List positions = []; List agencices = []; List categories = []; bool fatherIncaseOfEmergency = false; diff --git a/lib/screens/profile/components/voluntary_works/add_modal.dart b/lib/screens/profile/components/voluntary_works/add_modal.dart index 808364e..88c5198 100644 --- a/lib/screens/profile/components/voluntary_works/add_modal.dart +++ b/lib/screens/profile/components/voluntary_works/add_modal.dart @@ -58,7 +58,7 @@ class _AddVoluntaryWorkScreenState extends State { List? provinces; List? citymuns; ////Selected - Position? selectedPosition; + PositionTitle? selectedPosition; Agency? selectedAgency; Category? selectedCategoty; Region? selectedRegion; @@ -97,7 +97,7 @@ class _AddVoluntaryWorkScreenState extends State { itemHeight: 70, suggestionsDecoration: box1(), suggestions: state.positions - .map((Position position) => + .map((PositionTitle position) => SearchFieldListItem(position.title!, item: position, child: Padding( @@ -132,7 +132,7 @@ class _AddVoluntaryWorkScreenState extends State { controller: addPositionController, onpressed: () { setState(() { - Position newAgencyPosition = Position( + PositionTitle newAgencyPosition = PositionTitle( id: null, title: addPositionController.text .toUpperCase()); diff --git a/lib/screens/profile/components/voluntary_works/edit_modal.dart b/lib/screens/profile/components/voluntary_works/edit_modal.dart index b841bda..520a478 100644 --- a/lib/screens/profile/components/voluntary_works/edit_modal.dart +++ b/lib/screens/profile/components/voluntary_works/edit_modal.dart @@ -65,7 +65,7 @@ class _EditVoluntaryWorkScreenState extends State { List? citymuns; ////Selected - Position? selectedPosition; + PositionTitle? selectedPosition; Agency? selectedAgency; Category? selectedCategoty; Region? selectedRegion; @@ -130,7 +130,7 @@ class _EditVoluntaryWorkScreenState extends State { itemHeight: 70, suggestionsDecoration: box1(), suggestions: state.positions - .map((Position position) => + .map((PositionTitle position) => SearchFieldListItem(position.title!, item: position, child: Padding( @@ -165,7 +165,7 @@ class _EditVoluntaryWorkScreenState extends State { controller: addPositionController, onpressed: () { setState(() { - Position newAgencyPosition = Position( + PositionTitle newAgencyPosition = PositionTitle( id: null, title: addPositionController.text .toUpperCase()); diff --git a/lib/screens/profile/components/work_history/add_modal.dart b/lib/screens/profile/components/work_history/add_modal.dart index f7ee97d..6fd2f33 100644 --- a/lib/screens/profile/components/work_history/add_modal.dart +++ b/lib/screens/profile/components/work_history/add_modal.dart @@ -38,7 +38,7 @@ class _AddWorkHistoryScreenState extends State { final toDateController = TextEditingController(); final fromDateController = TextEditingController(); final _formKey = GlobalKey(); - Position? selectedPosition; + PositionTitle? selectedPosition; Agency? selectedAgency; AppoinemtStatus? selectedStatus; Category? selectedAgencyCategory; @@ -93,7 +93,7 @@ class _AddWorkHistoryScreenState extends State { itemHeight: 100, suggestionsDecoration: box1(), suggestions: state.agencyPositions - .map((Position position) => SearchFieldListItem( + .map((PositionTitle position) => SearchFieldListItem( position.title!, item: position, child: Padding( @@ -127,7 +127,7 @@ class _AddWorkHistoryScreenState extends State { controller: addPositionController, onpressed: () { setState(() { - Position newAgencyPosition = Position( + PositionTitle newAgencyPosition = PositionTitle( id: null, title: addPositionController.text .toUpperCase()); diff --git a/lib/screens/profile/components/work_history/edit_modal.dart b/lib/screens/profile/components/work_history/edit_modal.dart index 4a3a0cd..1045946 100644 --- a/lib/screens/profile/components/work_history/edit_modal.dart +++ b/lib/screens/profile/components/work_history/edit_modal.dart @@ -41,7 +41,7 @@ class _EditWorkHistoryScreenState extends State { final oldAppointmentStatusController = TextEditingController(); final oldAgencyController = TextEditingController(); final _formKey = GlobalKey(); - Position? selectedPosition; + PositionTitle? selectedPosition; Agency? selectedAgency; AppoinemtStatus? selectedStatus; Category? selectedAgencyCategory; @@ -111,7 +111,7 @@ class _EditWorkHistoryScreenState extends State { itemHeight: 100, suggestionsDecoration: box1(), suggestions: state.agencyPositions - .map((Position position) => + .map((PositionTitle position) => SearchFieldListItem(position.title!, item: position, child: Padding( @@ -146,7 +146,7 @@ class _EditWorkHistoryScreenState extends State { controller: addPositionController, onpressed: () { setState(() { - Position newAgencyPosition = Position( + PositionTitle newAgencyPosition = PositionTitle( id: null, title: addPositionController.text .toUpperCase()); diff --git a/lib/screens/superadmin/stations/stations_screen.dart b/lib/screens/superadmin/stations/stations_screen.dart index 41f8ca3..6d21170 100644 --- a/lib/screens/superadmin/stations/stations_screen.dart +++ b/lib/screens/superadmin/stations/stations_screen.dart @@ -40,72 +40,72 @@ class RbacStationScreen extends StatelessWidget { actions: [ AddLeading(onPressed: () { BuildContext parent = context; - // showDialog( - // context: context, - // builder: (BuildContext context) { - // return AlertDialog( - // title: const Text("Add New Station"), - // content: FormBuilder( - // key: formKey, - // child: Column( - // mainAxisSize: MainAxisSize.min, - // children: [ - // FormBuilderTextField( - // name: "object_name", - // decoration: normalTextFieldStyle( - // "Role name *", "Role name "), - // validator: FormBuilderValidators.required( - // errorText: "This field is required"), - // ), - // const SizedBox( - // height: 8, - // ), - // FormBuilderTextField( - // name: "slug", - // decoration: normalTextFieldStyle("Slug ", "Slug"), - // ), - // const SizedBox( - // height: 8, - // ), - // FormBuilderTextField( - // validator: FormBuilderValidators.maxLength(50, - // errorText: "Max characters only 50"), - // name: "shorthand", - // decoration: - // normalTextFieldStyle("Shorthand ", "Shorthand"), - // ), - // const SizedBox( - // height: 12, - // ), - // SizedBox( - // width: double.infinity, - // height: 50, - // child: ElevatedButton( - // style: mainBtnStyle( - // primary, Colors.transparent, second), - // onPressed: () { - // if (formKey.currentState! - // .saveAndValidate()) { - // String name = formKey - // .currentState!.value['object_name']; - // String? slug = - // formKey.currentState!.value['slug']; - // String? short = formKey - // .currentState!.value['shorthand']; - // parent.read().add(AddRbacRole( - // id: id, - // name: name, - // shorthand: short, - // slug: slug)); - // Navigator.pop(context); - // } - // }, - // child: const Text("Add"))), - // ], - // ), - // ), - // ); - // }); + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text("Add New Station"), + content: FormBuilder( + key: formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + FormBuilderTextField( + name: "object_name", + decoration: normalTextFieldStyle( + "Role name *", "Role name "), + validator: FormBuilderValidators.required( + errorText: "This field is required"), + ), + const SizedBox( + height: 8, + ), + FormBuilderTextField( + name: "slug", + decoration: normalTextFieldStyle("Slug ", "Slug"), + ), + const SizedBox( + height: 8, + ), + FormBuilderTextField( + validator: FormBuilderValidators.maxLength(50, + errorText: "Max characters only 50"), + name: "shorthand", + decoration: + normalTextFieldStyle("Shorthand ", "Shorthand"), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState! + .saveAndValidate()) { + String name = formKey + .currentState!.value['object_name']; + String? slug = + formKey.currentState!.value['slug']; + String? short = formKey + .currentState!.value['shorthand']; + parent.read().add(AddRbacRole( + id: id, + name: name, + shorthand: short, + slug: slug)); + Navigator.pop(context); + } + }, + child: const Text("Add"))), + ], + ), + ), + ); + }); }) ], ), diff --git a/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart b/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart index 27cb9f0..d345097 100644 --- a/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart +++ b/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart @@ -227,8 +227,8 @@ class _DashBoardState extends State { onChanged: (value) { Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) { return BlocProvider( - create: (context) => EstPointPersonStationBloc()..add( EstPointPersonGetStations(agencyId: value!.areaid!,assignedAreas: widget.estPersonAssignedArea!)), - child: const EstPointPersonStationScreen(), + create: (context) => EstPointPersonStationBloc()..add( EstPointPersonGetStations(agencyId: value.areaid!,)), + child: EstPointPersonStationScreen(agencyId: value!.areaid!,), ); })); }) diff --git a/lib/screens/unit2/roles/establishment_point_person/est_point_person_station.dart b/lib/screens/unit2/roles/establishment_point_person/est_point_person_station.dart index b467e33..52cd05e 100644 --- a/lib/screens/unit2/roles/establishment_point_person/est_point_person_station.dart +++ b/lib/screens/unit2/roles/establishment_point_person/est_point_person_station.dart @@ -3,33 +3,51 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:searchfield/searchfield.dart'; import 'package:unit2/bloc/rbac/rbac_operations/role/role_bloc.dart'; -import 'package:unit2/model/login_data/user_info/assigned_area.dart'; import 'package:unit2/model/rbac/rbac_station.dart'; +import 'package:unit2/model/roles/pass_check/station_assign_area.dart'; +import 'package:unit2/model/utils/position.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/error_state.dart'; import '../../../../bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_bloc.dart'; -import '../../../../model/utils/agency.dart'; import '../../../../theme-data.dart/box_shadow.dart'; +import '../../../../theme-data.dart/btn-style.dart'; import '../../../../theme-data.dart/colors.dart'; import '../../../../theme-data.dart/form-style.dart'; -import '../../../../utils/formatters.dart'; +import '../../../../utils/alerts.dart'; import '../../../../utils/global.dart'; import '../../../../widgets/empty_data.dart'; +import '../../../profile/shared/add_for_empty_search.dart'; class EstPointPersonStationScreen extends StatelessWidget { + final String agencyId; const EstPointPersonStationScreen({ + required this.agencyId, super.key, }); @override Widget build(BuildContext context) { - final agencyFocusNode = FocusNode(); + final estPointPersonBloc = + BlocProvider.of(context); List stations = []; final formKey = GlobalKey(); - Agency selectedAgency; List> hierarchy = []; + bool mainParent = false; + bool isWithinParent = true; + bool isHospital = false; + List stationTypes = []; + List positions = []; + List mainParentStations = []; + List parentStations = []; + RbacStation? selectedMainParentStation; + RbacStation? selectedParentStation; + StationType? selectedStationType; + PositionTitle? selectedPositiontitle; + final addStationTypeController = TextEditingController(); + final stationTypeFocusNode = FocusNode(); return Scaffold( appBar: AppBar( centerTitle: true, @@ -38,72 +56,363 @@ class EstPointPersonStationScreen extends StatelessWidget { actions: [ AddLeading(onPressed: () { BuildContext parent = context; - // showDialog( - // context: context, - // builder: (BuildContext context) { - // return AlertDialog( - // title: const Text("Add New Station"), - // content: FormBuilder( - // key: formKey, - // child: Column( - // mainAxisSize: MainAxisSize.min, - // children: [ - // FormBuilderTextField( - // name: "object_name", - // decoration: normalTextFieldStyle( - // "Role name *", "Role name "), - // validator: FormBuilderValidators.required( - // errorText: "This field is required"), - // ), - // const SizedBox( - // height: 8, - // ), - // FormBuilderTextField( - // name: "slug", - // decoration: normalTextFieldStyle("Slug ", "Slug"), - // ), - // const SizedBox( - // height: 8, - // ), - // FormBuilderTextField( - // validator: FormBuilderValidators.maxLength(50, - // errorText: "Max characters only 50"), - // name: "shorthand", - // decoration: - // normalTextFieldStyle("Shorthand ", "Shorthand"), - // ), - // const SizedBox( - // height: 12, - // ), - // SizedBox( - // width: double.infinity, - // height: 50, - // child: ElevatedButton( - // style: mainBtnStyle( - // primary, Colors.transparent, second), - // onPressed: () { - // if (formKey.currentState! - // .saveAndValidate()) { - // String name = formKey - // .currentState!.value['object_name']; - // String? slug = - // formKey.currentState!.value['slug']; - // String? short = formKey - // .currentState!.value['shorthand']; - // parent.read().add(AddRbacRole( - // id: id, - // name: name, - // shorthand: short, - // slug: slug)); - // Navigator.pop(context); - // } - // }, - // child: const Text("Add"))), - // ], - // ), - // ), - // ); - // }); + mainParentStations = []; + for (RbacStation station in stations) { + if (station.hierarchyOrderNo == 1) { + mainParentStations.add(station); + } + } + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text("Add New Station"), + content: SingleChildScrollView( + child: FormBuilder( + key: formKey, + child: StatefulBuilder(builder: (context, setState) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + ////is main parent + FormBuilderSwitch( + initialValue: mainParent, + activeColor: second, + onChanged: (value) { + setState(() { + mainParent = !mainParent; + }); + }, + decoration: normalTextFieldStyle( + "is Main Parent?", 'is Main Parent?'), + name: 'main-parent', + title: Text(mainParent ? "YES" : "NO"), + validator: FormBuilderValidators.required( + errorText: "This field is required"), + ), + const SizedBox( + height: 8, + ), + //// selected main parent + SizedBox( + child: mainParent == true + ? const SizedBox.shrink() + : FormBuilderDropdown( + decoration: normalTextFieldStyle( + "Main Parent Station", + "Main Parent Station"), + name: "parent-stations", + items: mainParentStations.isEmpty + ? [] + : mainParentStations.map((e) { + return DropdownMenuItem( + value: e, + child: Text(e.stationName!), + ); + }).toList(), + onChanged: (RbacStation? e) { + setState(() { + selectedMainParentStation = e; + parentStations = []; + for (RbacStation station + in stations) { + if (station.mainParentStation == + selectedMainParentStation! + .id) { + parentStations.add(station); + } + } + parentStations.add( + selectedMainParentStation!); + }); + }, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + ), + ), + const SizedBox( + height: 12, + ), + ////parent station + SizedBox( + child: mainParent == true + ? const SizedBox.shrink() + : FormBuilderDropdown( + decoration: normalTextFieldStyle( + "Parent Station", "Parent Station"), + name: "parent-stations", + onChanged: (RbacStation? e) { + setState(() { + selectedParentStation = e; + }); + }, + items: parentStations.isEmpty + ? [] + : parentStations.map((e) { + return DropdownMenuItem( + value: e, + child: Text(e.stationName!), + ); + }).toList(), + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + ), + ), + const SizedBox( + height: 12, + ), + ////Station Type + SearchField( + itemHeight: 50, + suggestionsDecoration: box1(), + suggestions: stationTypes + .map((StationType stationType) => + SearchFieldListItem( + stationType.typeName!, + item: stationType, + child: Padding( + padding: + const EdgeInsets.symmetric( + horizontal: 10), + child: ListTile( + title: Text( + stationType.typeName!, + overflow: TextOverflow.visible, + )), + ))) + .toList(), + validator: (station) { + if (station!.isEmpty) { + return "This field is required"; + } + return null; + }, + focusNode: stationTypeFocusNode, + searchInputDecoration: + normalTextFieldStyle("Station Type *", "") + .copyWith( + suffixIcon: GestureDetector( + onTap: () => stationTypeFocusNode.unfocus(), + child: const Icon(Icons.arrow_drop_down), + )), + onSuggestionTap: (position) { + setState(() { + selectedStationType = position.item!; + stationTypeFocusNode.unfocus(); + }); + }, + emptyWidget: EmptyWidget( + title: "Add StationType", + controller: addStationTypeController, + onpressed: () { + setState(() { + StationType stationType = StationType( + id: null, + typeName: + addStationTypeController.text, + color: null, + order: null, + isActive: null, + group: null); + stationTypes.add(stationType); + Navigator.pop(context); + }); + }), + ), + const SizedBox( + height: 12, + ), + ////Position title + FormBuilderDropdown( + decoration: normalTextFieldStyle( + "Head Position", "Head Position"), + name: "head-position", + items: positions.map((e) { + return DropdownMenuItem( + value: e, + child: Text(e.title!), + ); + }).toList(), + onChanged: (title) { + selectedPositiontitle = title; + }, + ), + const SizedBox( + height: 12, + ), + ////is within parent + FormBuilderSwitch( + initialValue: true, + activeColor: second, + onChanged: (value) { + setState(() { + isWithinParent = value!; + }); + }, + decoration: normalTextFieldStyle( + "Location of the station within this parent?", + 'Location of the station within this parent?'), + name: 'isWithinParent', + title: Text(isWithinParent ? "YES" : "NO"), + ), + const SizedBox( + height: 12, + ), + Row( + //// Station Name + children: [ + Flexible( + child: FormBuilderTextField( + validator: + FormBuilderValidators.required( + errorText: + "This Field is required"), + decoration: normalTextFieldStyle( + "Station name", "Station name"), + name: "station-name"), + ), + const SizedBox( + width: 12, + ), + //// Acronym + Flexible( + child: FormBuilderTextField( + validator: + FormBuilderValidators.required( + errorText: + "This Field is required"), + decoration: normalTextFieldStyle( + "Acronym", "Acronym"), + name: "acronym"), + ), + ], + ), + const SizedBox( + height: 12, + ), + FormBuilderTextField( + ////Description + decoration: normalTextFieldStyle( + "Station description", + "Station description"), + name: "station-description"), + const SizedBox( + height: 12, + ), + Row( + children: [ + Flexible( + ////Code + child: FormBuilderTextField( + decoration: normalTextFieldStyle( + "Code", "Code"), + name: "code"), + ), + const SizedBox( + width: 12, + ), + Flexible( + //// Full Code + child: FormBuilderTextField( + decoration: normalTextFieldStyle( + "Full Code", "Full Code"), + name: "fullcode"), + ), + ], + ), + const SizedBox( + height: 12, + ), + ////is Hospital + FormBuilderSwitch( + initialValue: isHospital, + activeColor: second, + onChanged: (value) { + setState(() { + isHospital = !isHospital; + }); + }, + decoration: + normalTextFieldStyle("Is Hospital", ''), + name: 'isHospital', + title: Text(isHospital == true ? "YES" : "NO"), + ), + const SizedBox( + height: 20, + ), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + RbacStation? newStation; + if (formKey.currentState! + .saveAndValidate()) { + String? stationName = formKey + .currentState! + .value['station-name']; + String? acronym = formKey + .currentState!.value['acronym']; + String? code = formKey + .currentState!.value['code']; + String? fullcode = formKey + .currentState!.value['fullcode']; + String? description = formKey + .currentState! + .value['station-description']; + newStation = RbacStation( + id: null, + stationName: stationName, + stationType: selectedStationType, + hierarchyOrderNo: + selectedParentStation! + .hierarchyOrderNo! + + 1, + headPosition: + selectedPositiontitle?.title, + governmentAgency: + GovernmentAgency( + agencyid: int.tryParse( + agencyId), + agencyname: null, + agencycatid: null, + privateEntity: null, + contactinfoid: null), + acronym: acronym, + parentStation: + selectedParentStation!.id!, + code: code, + fullcode: fullcode, + childStationInfo: null, + islocationUnderParent: + isWithinParent, + mainParentStation: + selectedMainParentStation! + .id!, + description: description, + ishospital: isHospital, + isactive: true, + sellingStation: null); + Navigator.pop(context); + estPointPersonBloc.add( + AddEstPointPersonStation( + station: newStation)); + } + }, + child: const Text("Add"))), + ], + ); + }), + ), + ), + ); + }); }) ], ), @@ -118,7 +427,26 @@ class EstPointPersonStationScreen extends StatelessWidget { final progress = ProgressHUD.of(context); progress!.showWithText("Please wait..."); } + if (state is EstPointPersonAddedState) { + if (state.response['success']) { + successAlert( + context, "Adding Successfull!", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add( + EstPointPersonGetStations(agencyId: agencyId.toString())); + }); + } else { + errorAlert(context, "Adding Failed", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add( + EstPointPersonGetStations(agencyId: agencyId.toString())); + }); + } + } if (state is EstPersonStationLoadedState || + state is EstPointPersonAddedState || state is EstPersonStationErrorState) { final progress = ProgressHUD.of(context); progress!.dismiss(); @@ -128,6 +456,8 @@ class EstPointPersonStationScreen extends StatelessWidget { final parent = context; if (state is EstPersonStationLoadedState) { stations = state.stations; + stationTypes = state.stationTypes; + positions = state.positions; int max = 0; for (RbacStation station in stations) { if (station.hierarchyOrderNo != null) { @@ -149,48 +479,9 @@ class EstPointPersonStationScreen extends StatelessWidget { } } - if (hierarchy[0][1].isNotEmpty) { + if (stations.isNotEmpty && hierarchy[0][1].isNotEmpty) { return Column( children: [ - Padding( - padding: const EdgeInsets.all(8), - child: SearchField( - inputFormatters: [UpperCaseTextFormatter()], - itemHeight: 70, - focusNode: agencyFocusNode, - suggestions: state.assignedAreas - .map((AssignedArea agency) => - SearchFieldListItem(agency.areaName!, - item: agency, - child: ListTile( - title: Text( - agency.areaName!, - overflow: TextOverflow.visible, - ), - ))) - .toList(), - searchInputDecoration: - normalTextFieldStyle("Filter", "").copyWith( - prefixIcon: const Icon(Icons.filter_list), - suffixIcon: IconButton( - icon: const Icon(Icons.arrow_drop_down), - onPressed: () { - agencyFocusNode.unfocus(); - }, - )), - onSuggestionTap: (agency) { - agencyFocusNode.unfocus(); - }, - validator: (agency) { - if (agency!.isEmpty) { - return "This field is required"; - } - return null; - }, - emptyWidget: const Center( - child: Text("No result found..."), - )), - ), Expanded( child: ListView.builder( padding: const EdgeInsets.symmetric( @@ -265,7 +556,9 @@ class EstPointPersonStationScreen extends StatelessWidget { Expanded( child: Container( width: screenWidth, - decoration: box1(), + decoration: box1() + .copyWith( + boxShadow: []), padding: const EdgeInsets .only( @@ -275,10 +568,19 @@ class EstPointPersonStationScreen extends StatelessWidget { Expanded( child: Row( children: [ - const CircleAvatar( - child: Text( - '2'), - ), + Padding( + padding: + const EdgeInsets.all( + 6), + child: + Text( + "2", + style: Theme.of(context) + .textTheme + .bodyLarge, + selectionColor: + Colors.redAccent, + )), const SizedBox( width: 12, ), @@ -312,24 +614,29 @@ class EstPointPersonStationScreen extends StatelessWidget { .map((e) { List childs = []; - if (max >= 4) { - for (RbacStation station - in hierarchy[3][4]) { - if (station.parentStation == - e.id) { - childs.add(station); - } - } - } else { - childs = []; - } + if (max >= 4) { + for (RbacStation station + in hierarchy[ + 3] + [4]) { + if (station + .parentStation == + e.id) { + childs.add( + station); + } + } + } else { + childs = []; + } return Column( children: [ Container( width: screenWidth, decoration: - box1(), + box1() + .copyWith(boxShadow: []), padding: const EdgeInsets .only( left: @@ -340,9 +647,13 @@ class EstPointPersonStationScreen extends StatelessWidget { Expanded( child: Row( children: [ - const CircleAvatar( - child: Text('3'), - ), + Padding( + padding: const EdgeInsets.all(6), + child: Text( + "3", + style: Theme.of(context).textTheme.bodyLarge, + selectionColor: Colors.redAccent, + )), const SizedBox( width: 12, ), @@ -374,16 +685,20 @@ class EstPointPersonStationScreen extends StatelessWidget { children: [ Container( width: screenWidth, - decoration: box1(), + decoration: box1().copyWith(boxShadow: []), padding: const EdgeInsets.only(left: 80), child: Row( children: [ Expanded( child: Row( children: [ - const CircleAvatar( - child: Text('4'), - ), + Padding( + padding: const EdgeInsets.all(6), + child: Text( + "4", + style: Theme.of(context).textTheme.bodyLarge, + selectionColor: Colors.redAccent, + )), const SizedBox( width: 12, ), @@ -468,55 +783,8 @@ class EstPointPersonStationScreen extends StatelessWidget { ], ); } else { - return Column( - children: [ - Padding( - padding: const EdgeInsets.all(8), - child: SearchField( - inputFormatters: [UpperCaseTextFormatter()], - itemHeight: 70, - focusNode: agencyFocusNode, - suggestions: state.assignedAreas - .map((AssignedArea agency) => - SearchFieldListItem(agency.areaName!, - item: agency, - child: ListTile( - title: Text( - agency.areaName!, - overflow: TextOverflow.visible, - ), - ))) - .toList(), - searchInputDecoration: - normalTextFieldStyle("Filter", "").copyWith( - prefixIcon: const Icon(Icons.filter_list), - suffixIcon: IconButton( - icon: const Icon(Icons.arrow_drop_down), - onPressed: () { - agencyFocusNode.unfocus(); - }, - )), - onSuggestionTap: (agency) { - agencyFocusNode.unfocus(); - }, - validator: (agency) { - if (agency!.isEmpty) { - return "This field is required"; - } - return null; - }, - emptyWidget: const Center( - child: Text("No result found..."), - )), - ), - const SizedBox( - height: 20, - ), - const EmptyData( - message: - "No Station available. Please click + to add."), - ], - ); + return const EmptyData( + message: "No Station available. Please click + to add."); } } if (state is EstPersonStationErrorState) { diff --git a/lib/sevices/profile/work_history_services.dart b/lib/sevices/profile/work_history_services.dart index cd85b9e..068f20c 100644 --- a/lib/sevices/profile/work_history_services.dart +++ b/lib/sevices/profile/work_history_services.dart @@ -162,8 +162,8 @@ class WorkHistoryService { } ////get agency position - Future> getAgencyPosition() async { - List agencyPositions = []; + Future> getAgencyPosition() async { + List agencyPositions = []; String path = Url.instance.getPositions(); Map headers = { 'Content-Type': 'application/json; charset=UTF-8', @@ -175,7 +175,7 @@ class WorkHistoryService { Map data = jsonDecode(response.body); if (data['data'] != null) { data['data'].forEach((var agencyPosition) { - Position position = Position.fromJson(agencyPosition); + PositionTitle position = PositionTitle.fromJson(agencyPosition); agencyPositions.add(position); }); } diff --git a/lib/sevices/roles/rbac_operations/station_services.dart b/lib/sevices/roles/rbac_operations/station_services.dart index 0bfeec2..75dc6c3 100644 --- a/lib/sevices/roles/rbac_operations/station_services.dart +++ b/lib/sevices/roles/rbac_operations/station_services.dart @@ -1,38 +1,137 @@ import 'dart:convert'; +import 'package:unit2/model/utils/position.dart'; import 'package:unit2/utils/request.dart'; import 'package:unit2/utils/urls.dart'; import 'package:http/http.dart' as http; import '../../../model/rbac/rbac_station.dart'; -import '../../../model/roles/pass_check/station_assign_area.dart'; -class RbacStationServices{ +import '../../../model/roles/pass_check/station_assign_area.dart'; + +class RbacStationServices { static final RbacStationServices _instance = RbacStationServices(); static RbacStationServices get instance => _instance; - String xClientKey = "unitK3CQaXiWlPReDsBzmmwBZPd9Re1z"; + String xClientKey = "unitK3CQaXiWlPReDsBzmmwBZPd9Re1z"; String xClientKeySecret = "unitcYqAN7GGalyz"; - Future> getStations({required String agencyId})async{ + Future> getStations({required String agencyId}) async { List stations = []; String path = Url.instance.getStation(); - Map param = {"government_agency_id":agencyId.toString()}; - Map headers = { + Map param = {"government_agency_id": agencyId.toString()}; + Map headers = { 'Content-Type': 'application/json; charset=UTF-8', 'X-Client-Key': xClientKey, 'X-Client-Secret': xClientKeySecret }; - try{ - http.Response response = await Request.instance.getRequest(param: param,path: path,headers: headers); - if(response.statusCode == 200){ + try { + http.Response response = await Request.instance + .getRequest(param: param, path: path, headers: headers); + if (response.statusCode == 200) { Map data = jsonDecode(response.body); - if(data['data'] != null){ - for(var station in data['data']){ + if (data['data'] != null) { + for (var station in data['data']) { RbacStation area = RbacStation.fromJson(station); stations.add(area); } } } - }catch(e){ + } catch (e) { throw e.toString(); } return stations; } -} \ No newline at end of file + + Future> getStationTypes() async { + String path = Url.instance.getStationType(); + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + List stationTypes = []; + + try { + http.Response response = await Request.instance + .getRequest(path: path, param: {}, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + for (var st in data['data']) { + StationType stationType = StationType.fromJson(st); + stationTypes.add(stationType); + } + } + } + } catch (e) { + throw e.toString(); + } + return stationTypes; + } + + Future> getPositionTitle() async { + String path = Url.instance.getPositionTitle(); + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + List positions = []; + + try { + http.Response response = await Request.instance + .getRequest(path: path, param: {}, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + for (var pos in data['data']) { + PositionTitle posTitle = PositionTitle.fromJson(pos); + positions.add(posTitle); + } + } + } + } catch (e) { + throw e.toString(); + } + return positions; + } + + Future> addStation( + {required RbacStation station}) async { + Map statusResponse = {}; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + String path = Url.instance.postStation(); + + // try { + Map body = { + "station_name": station.stationName, + "acronym": station.acronym, + "station_type_id": station.stationType?.id, + "_station_type_name": station.stationType?.typeName, + "parent_station_id": station.parentStation, + "hierarchy_order_no": station.hierarchyOrderNo, + "agency_id": station.governmentAgency!.agencyid, + "is_location_under_parent": station.islocationUnderParent, + "head_position": station.headPosition, + "code": station.code, + "full-code": station.fullcode, + "main_parent_station_id": station.mainParentStation, + "description": station.description, + "ishospital": station.ishospital, + }; + http.Response response = await Request.instance + .postRequest(param: {}, body: body, headers: headers,path: path); + if (response.statusCode == 201) { + Map data = jsonDecode(response.body); + statusResponse = data; + } else { + Map data = jsonDecode(response.body); + String message = data['message']; + statusResponse.addAll({'message': message}); + statusResponse.addAll( + {'success': false}, + ); + } + // } catch (e) { + // throw e.toString(); + // } + return statusResponse; + } +} diff --git a/lib/utils/profile_utilities.dart b/lib/utils/profile_utilities.dart index c5c0dab..929ac1a 100644 --- a/lib/utils/profile_utilities.dart +++ b/lib/utils/profile_utilities.dart @@ -43,8 +43,8 @@ class ProfileUtilities { } ////get agency position - Future> getAgencyPosition() async { - List agencyPositions = []; + Future> getAgencyPosition() async { + List agencyPositions = []; String path = Url.instance.getPositions(); Map headers = { 'Content-Type': 'application/json; charset=UTF-8', @@ -56,7 +56,7 @@ class ProfileUtilities { Map data = jsonDecode(response.body); if (data['data'] != null) { data['data'].forEach((var agencyPosition) { - Position position = Position.fromJson(agencyPosition); + PositionTitle position = PositionTitle.fromJson(agencyPosition); agencyPositions.add(position); }); } diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index 3201fd6..cc22b4f 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -7,7 +7,7 @@ class Url { // return '192.168.10.183:3000'; // return 'agusandelnorte.gov.ph'; return "192.168.10.219:3000"; - // // return "192.168.10.241"; + // return "192.168.10.241"; // return "192.168.10.221:3004"; // return "playweb.agusandelnorte.gov.ph"; // return 'devapi.agusandelnorte.gov.ph:3004'; @@ -241,7 +241,6 @@ class Url { } ////rbac operations - String getRbacOperations() { return "/api/account/auth/operations/"; } @@ -293,10 +292,20 @@ class Url { String getStation() { return "/api/hrms_app/station/"; } + String postStation() { + return "/api/hrms_app/stations/"; + } String getRoleAssignment(){ return "api/account/auth/role_assignment/"; } + String getStationType(){ + return "/api/hrms_app/station_type/"; + } + + String getPositionTitle(){ + return "/api/hrms_app/position_title/"; + } //// location utils path String getCounties() { From 14dd524c717515432ad14a8bbcd74e421cfe28b6 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Thu, 17 Aug 2023 10:49:33 +0800 Subject: [PATCH 80/86] add station for establishment point person and superadmin --- .../rbac_operations/station/station_bloc.dart | 48 +- .../station/station_event.dart | 8 + .../station/station_state.dart | 21 +- .../est_point_person_station_bloc.dart | 14 +- lib/model/rbac/rbac_station.dart | 1 + lib/model/rbac/station_type.dart | 40 + .../roles/pass_check/station_assign_area.dart | 35 +- .../superadmin/stations/stations_screen.dart | 960 ++++++++++++++---- .../components/dashboard/dashboard.dart | 10 +- .../dashboard/superadmin_expanded_menu.dart | 17 +- .../est_point_person_station.dart | 23 +- .../rbac_operations/station_services.dart | 11 +- 12 files changed, 925 insertions(+), 263 deletions(-) diff --git a/lib/bloc/rbac/rbac_operations/station/station_bloc.dart b/lib/bloc/rbac/rbac_operations/station/station_bloc.dart index 3d1a12d..79a9597 100644 --- a/lib/bloc/rbac/rbac_operations/station/station_bloc.dart +++ b/lib/bloc/rbac/rbac_operations/station/station_bloc.dart @@ -2,7 +2,9 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:unit2/model/rbac/rbac_station.dart'; import 'package:unit2/sevices/roles/rbac_operations/station_services.dart'; +import '../../../../model/rbac/station_type.dart'; import '../../../../model/utils/agency.dart'; +import '../../../../model/utils/position.dart'; import '../../../../utils/profile_utilities.dart'; part 'station_event.dart'; part 'station_state.dart'; @@ -11,6 +13,8 @@ class StationBloc extends Bloc { StationBloc() : super(StationInitial()) { List stations = []; List agencies = []; + List stationTypes = []; + List positions = []; on((event, emit) async { emit(StationLoadingState()); try { @@ -22,14 +26,42 @@ class StationBloc extends Bloc { await ProfileUtilities.instance.getAgecies(); agencies = newAgencies; } - emit(StationLoadedState(stations: stations, agencies: agencies)); + if (stationTypes.isEmpty) { + stationTypes = await RbacStationServices.instance.getStationTypes(); + } + if (positions.isEmpty) { + positions = await RbacStationServices.instance.getPositionTitle(); + } + emit(StationLoadedState( + stations: stations, + agencies: agencies, + stationTypes: stationTypes, + positions: positions)); } catch (e) { emit(StationErrorState(message: e.toString())); } }); - on((event, emit)async { - // emit(StationLoadingState()); - try { + on((event, emit) async { + emit(StationLoadingState()); + try { + Map statusResponse = await RbacStationServices + .instance + .addStation(station: event.station); + if (statusResponse['success']) { + RbacStation newStation = RbacStation.fromJson(statusResponse['data']); + stations.add(newStation); + emit(RbacStationAddedState(response: statusResponse)); + } else { + emit(RbacStationAddedState(response: statusResponse)); + } + } catch (e) { + emit(StationErrorState(message: e.toString())); + } + }); + on((event, emit) async { + emit(StationLoadingState()); + + try { stations = await RbacStationServices.instance .getStations(agencyId: event.agencyId.toString()); @@ -38,10 +70,16 @@ class StationBloc extends Bloc { await ProfileUtilities.instance.getAgecies(); agencies = newAgencies; } - emit(StationLoadedState(stations: stations, agencies: agencies)); + emit(StationLoadedState( + stations: stations, + agencies: agencies, + positions: positions, + stationTypes: stationTypes)); } catch (e) { emit(StationErrorState(message: e.toString())); } }); + + } } diff --git a/lib/bloc/rbac/rbac_operations/station/station_event.dart b/lib/bloc/rbac/rbac_operations/station/station_event.dart index ea2c989..bf26ec8 100644 --- a/lib/bloc/rbac/rbac_operations/station/station_event.dart +++ b/lib/bloc/rbac/rbac_operations/station/station_event.dart @@ -10,9 +10,17 @@ abstract class StationEvent extends Equatable { class GetStations extends StationEvent { final int agencyId; const GetStations({required this.agencyId}); + @override + List get props => [agencyId]; } class FilterStation extends StationEvent { final int agencyId; const FilterStation({required this.agencyId}); + @override + List get props => [agencyId]; +} +class AddRbacStation extends StationEvent { + final RbacStation station; + const AddRbacStation({required this.station}); } diff --git a/lib/bloc/rbac/rbac_operations/station/station_state.dart b/lib/bloc/rbac/rbac_operations/station/station_state.dart index 7ab511f..0494c90 100644 --- a/lib/bloc/rbac/rbac_operations/station/station_state.dart +++ b/lib/bloc/rbac/rbac_operations/station/station_state.dart @@ -12,9 +12,11 @@ class StationInitial extends StationState {} class StationLoadedState extends StationState { final List agencies; final List stations; - const StationLoadedState({required this.stations, required this.agencies}); + final List stationTypes; + final List positions; + const StationLoadedState({required this.stations, required this.agencies,required this.positions, required this.stationTypes}); @override - List get props => [agencies,stations]; + List get props => [agencies,stations,stationTypes,positions]; } class StationLoadingState extends StationState {} @@ -27,6 +29,17 @@ class StationErrorState extends StationState { } class FilterStationState extends StationState { - final int agencyId; - const FilterStationState({required this.agencyId}); + final List agencies; + final List stations; + final List stationTypes; + final List positions; + const FilterStationState({required this.stations, required this.agencies,required this.positions, required this.stationTypes}); + @override + List get props => [agencies,stations,stationTypes,positions]; } + +class RbacStationAddedState extends StationState{ + final Map response; + const RbacStationAddedState({required this.response}); +} + diff --git a/lib/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_bloc.dart b/lib/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_bloc.dart index 1e1a4f9..ea55a22 100644 --- a/lib/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_bloc.dart +++ b/lib/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_bloc.dart @@ -1,14 +1,10 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:unit2/model/login_data/user_info/assigned_area.dart'; -import 'package:unit2/model/roles/pass_check/station_assign_area.dart'; import 'package:unit2/model/utils/position.dart'; - import '../../../../../model/rbac/rbac_station.dart'; -import '../../../../../model/utils/agency.dart'; +import '../../../../../model/rbac/station_type.dart'; import '../../../../../sevices/roles/rbac_operations/station_services.dart'; -import '../../../../../utils/profile_utilities.dart'; - part 'est_point_person_station_event.dart'; part 'est_point_person_station_state.dart'; @@ -43,7 +39,7 @@ class EstPointPersonStationBloc }); on((event, emit) async { emit(EstPersonStationLoadingState()); - // try { + try { Map statusResponse = await RbacStationServices .instance .addStation(station: event.station); @@ -54,9 +50,9 @@ class EstPointPersonStationBloc } else { emit(EstPointPersonAddedState(response: statusResponse)); } - // } catch (e) { - // emit(EstPersonStationErrorState(message: e.toString())); - // } + } catch (e) { + emit(EstPersonStationErrorState(message: e.toString())); + } }); } } diff --git a/lib/model/rbac/rbac_station.dart b/lib/model/rbac/rbac_station.dart index fa345c3..9fa9272 100644 --- a/lib/model/rbac/rbac_station.dart +++ b/lib/model/rbac/rbac_station.dart @@ -4,6 +4,7 @@ +import 'package:unit2/model/rbac/station_type.dart'; import 'package:unit2/model/roles/pass_check/station_assign_area.dart'; class RbacStation { diff --git a/lib/model/rbac/station_type.dart b/lib/model/rbac/station_type.dart index e69de29..6254bd8 100644 --- a/lib/model/rbac/station_type.dart +++ b/lib/model/rbac/station_type.dart @@ -0,0 +1,40 @@ +// To parse this JSON data, do +// +// final rbacStationType = rbacStationTypeFromJson(jsonString); + + +class StationType { + final int? id; + final String? typeName; + final String? color; + final int? order; + final bool? isActive; + final String? group; + + StationType({ + required this.id, + required this.typeName, + required this.color, + required this.order, + required this.isActive, + required this.group, + }); + + factory StationType.fromJson(Map json) => StationType( + id: json["id"], + typeName: json["type_name"], + color: json["color"], + order: json["order"], + isActive: json["is_active"], + group: json["group"], + ); + + Map toJson() => { + "id": id, + "type_name": typeName, + "color": color, + "order": order, + "is_active": isActive, + "group": group, + }; +} diff --git a/lib/model/roles/pass_check/station_assign_area.dart b/lib/model/roles/pass_check/station_assign_area.dart index f620307..5b2c099 100644 --- a/lib/model/roles/pass_check/station_assign_area.dart +++ b/lib/model/roles/pass_check/station_assign_area.dart @@ -3,6 +3,7 @@ // final assignArea = assignAreaFromJson(jsonString); import '../../rbac/rbac_station.dart'; +import '../../rbac/station_type.dart'; class StationAssignArea { final bool? isactive; @@ -132,38 +133,4 @@ class ChildStationInfo { } -class StationType { - final int? id; - final String? typeName; - final String? color; - final int? order; - final bool? isActive; - final String? group; - StationType({ - required this.id, - required this.typeName, - required this.color, - required this.order, - required this.isActive, - required this.group, - }); - - factory StationType.fromJson(Map json) => StationType( - id: json["id"], - typeName: json["type_name"], - color: json["color"], - order: json["order"], - isActive: json["is_active"], - group: json["group"], - ); - - Map toJson() => { - "id": id, - "type_name": typeName, - "color": color, - "order": order, - "is_active": isActive, - "group": group, - }; -} diff --git a/lib/screens/superadmin/stations/stations_screen.dart b/lib/screens/superadmin/stations/stations_screen.dart index 6d21170..822feec 100644 --- a/lib/screens/superadmin/stations/stations_screen.dart +++ b/lib/screens/superadmin/stations/stations_screen.dart @@ -1,4 +1,3 @@ -import 'package:app_popup_menu/app_popup_menu.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; @@ -9,29 +8,57 @@ import 'package:searchfield/searchfield.dart'; import 'package:unit2/bloc/rbac/rbac_operations/role/role_bloc.dart'; import 'package:unit2/bloc/rbac/rbac_operations/station/station_bloc.dart'; import 'package:unit2/model/rbac/rbac_station.dart'; -import 'package:unit2/screens/superadmin/role/shared_pop_up_menu.dart'; +import 'package:unit2/model/roles/pass_check/station_assign_area.dart'; +import 'package:unit2/model/utils/agency.dart'; +import 'package:unit2/model/utils/position.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/error_state.dart'; -import '../../../model/utils/agency.dart'; -import '../../../theme-data.dart/box_shadow.dart'; -import '../../../theme-data.dart/btn-style.dart'; -import '../../../theme-data.dart/colors.dart'; -import '../../../theme-data.dart/form-style.dart'; -import '../../../utils/alerts.dart'; +import '../../../../bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_bloc.dart'; +import '../../../../model/rbac/station_type.dart'; +import '../../../../theme-data.dart/box_shadow.dart'; +import '../../../../theme-data.dart/btn-style.dart'; +import '../../../../theme-data.dart/colors.dart'; +import '../../../../theme-data.dart/form-style.dart'; +import '../../../../utils/alerts.dart'; +import '../../../../utils/global.dart'; +import '../../../../widgets/empty_data.dart'; import '../../../utils/formatters.dart'; -import '../../../utils/global.dart'; -import '../../../widgets/empty_data.dart'; +import '../../profile/shared/add_for_empty_search.dart'; -class RbacStationScreen extends StatelessWidget { - final int id; - const RbacStationScreen({super.key, required this.id}); +class RbacStationScreen extends StatefulWidget { + final int agencyId; + const RbacStationScreen({ + required this.agencyId, + super.key, + }); + @override + State createState() => _RbacStationScreenState(); +} + +class _RbacStationScreenState extends State { @override Widget build(BuildContext context) { - final agencyFocusNode = FocusNode(); + final rbacStationBloc = BlocProvider.of(context); List stations = []; final formKey = GlobalKey(); - Agency selectedAgency; + List> hierarchy = []; + bool mainParent = false; + bool isWithinParent = true; + bool isHospital = false; + List stationTypes = []; + List positions = []; + List mainParentStations = []; + List parentStations = []; + List agencies = []; + RbacStation? selectedMainParentStation; + RbacStation? selectedParentStation; + StationType? selectedStationType; + PositionTitle? selectedPositiontitle; + int selectedAgencyId = widget.agencyId; + final addStationTypeController = TextEditingController(); + final stationTypeFocusNode = FocusNode(); + final agencyFocusNode = FocusNode(); return Scaffold( appBar: AppBar( centerTitle: true, @@ -40,73 +67,431 @@ class RbacStationScreen extends StatelessWidget { actions: [ AddLeading(onPressed: () { BuildContext parent = context; + mainParentStations = []; + mainParent = stations.isEmpty ? true : false; + for (RbacStation station in stations) { + if (station.hierarchyOrderNo == 1) { + mainParentStations.add(station); + } + } + + /////Add new tation showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: const Text("Add New Station"), - content: FormBuilder( - key: formKey, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - FormBuilderTextField( - name: "object_name", - decoration: normalTextFieldStyle( - "Role name *", "Role name "), - validator: FormBuilderValidators.required( - errorText: "This field is required"), - ), - const SizedBox( - height: 8, - ), - FormBuilderTextField( - name: "slug", - decoration: normalTextFieldStyle("Slug ", "Slug"), - ), - const SizedBox( - height: 8, - ), - FormBuilderTextField( - validator: FormBuilderValidators.maxLength(50, - errorText: "Max characters only 50"), - name: "shorthand", - decoration: - normalTextFieldStyle("Shorthand ", "Shorthand"), - ), - const SizedBox( - height: 12, - ), - SizedBox( - width: double.infinity, - height: 50, - child: ElevatedButton( - style: mainBtnStyle( - primary, Colors.transparent, second), - onPressed: () { - if (formKey.currentState! - .saveAndValidate()) { - String name = formKey - .currentState!.value['object_name']; - String? slug = - formKey.currentState!.value['slug']; - String? short = formKey - .currentState!.value['shorthand']; - parent.read().add(AddRbacRole( - id: id, - name: name, - shorthand: short, - slug: slug)); - Navigator.pop(context); - } - }, - child: const Text("Add"))), - ], + content: SingleChildScrollView( + child: FormBuilder( + key: formKey, + child: StatefulBuilder(builder: (context, setState) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + ////is main parent + FormBuilderSwitch( + initialValue: mainParent, + activeColor: second, + onChanged: (value) { + setState(() { + mainParent = !mainParent; + }); + }, + decoration: normalTextFieldStyle( + "is Main Parent?", 'is Main Parent?'), + name: 'main-parent', + title: Text(mainParent ? "YES" : "NO"), + validator: FormBuilderValidators.required( + errorText: "This field is required"), + ), + SizedBox( + height: mainParent ? 0 : 8, + ), + //// selected main parent + SizedBox( + child: mainParent == true + ? const SizedBox.shrink() + : FormBuilderDropdown( + decoration: normalTextFieldStyle( + "Main Parent Station", + "Main Parent Station"), + name: "parent-stations", + items: mainParentStations.isEmpty + ? [] + : mainParentStations.map((e) { + return DropdownMenuItem( + value: e, + child: Text(e.stationName!), + ); + }).toList(), + onChanged: (RbacStation? e) { + setState(() { + selectedMainParentStation = e; + parentStations = []; + for (RbacStation station + in stations) { + if (station.mainParentStation == + selectedMainParentStation! + .id) { + parentStations.add(station); + } + } + parentStations.add( + selectedMainParentStation!); + }); + }, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + ), + ), + SizedBox( + height: mainParent ? 0 : 8, + ), + ////parent station + SizedBox( + child: mainParent == true + ? const SizedBox.shrink() + : FormBuilderDropdown( + decoration: normalTextFieldStyle( + "Parent Station", "Parent Station"), + name: "parent-stations", + onChanged: (RbacStation? e) { + setState(() { + selectedParentStation = e; + }); + }, + items: parentStations.isEmpty + ? [] + : parentStations.map((e) { + return DropdownMenuItem( + value: e, + child: Text(e.stationName!), + ); + }).toList(), + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + ), + ), + const SizedBox( + height: 12, + ), + ////Station Type + SearchField( + itemHeight: 50, + suggestionsDecoration: box1(), + suggestions: stationTypes + .map((StationType stationType) => + SearchFieldListItem( + stationType.typeName!, + item: stationType, + child: Padding( + padding: + const EdgeInsets.symmetric( + horizontal: 10), + child: ListTile( + title: Text( + stationType.typeName!, + overflow: TextOverflow.visible, + )), + ))) + .toList(), + validator: (station) { + if (station!.isEmpty) { + return "This field is required"; + } + return null; + }, + focusNode: stationTypeFocusNode, + searchInputDecoration: + normalTextFieldStyle("Station Type *", "") + .copyWith( + suffixIcon: GestureDetector( + onTap: () => stationTypeFocusNode.unfocus(), + child: const Icon(Icons.arrow_drop_down), + )), + onSuggestionTap: (position) { + setState(() { + selectedStationType = position.item!; + stationTypeFocusNode.unfocus(); + }); + }, + emptyWidget: EmptyWidget( + title: "Add StationType", + controller: addStationTypeController, + onpressed: () { + setState(() { + StationType stationType = StationType( + id: null, + typeName: + addStationTypeController.text, + color: null, + order: null, + isActive: null, + group: null); + stationTypes.add(stationType); + Navigator.pop(context); + }); + }), + ), + const SizedBox( + height: 12, + ), + ////Position title + FormBuilderDropdown( + decoration: normalTextFieldStyle( + "Head Position", "Head Position"), + name: "head-position", + items: positions.map((e) { + return DropdownMenuItem( + value: e, + child: Text(e.title!), + ); + }).toList(), + onChanged: (title) { + selectedPositiontitle = title; + }, + ), + const SizedBox( + height: 12, + ), + ////is within parent + FormBuilderSwitch( + initialValue: true, + activeColor: second, + onChanged: (value) { + setState(() { + isWithinParent = value!; + }); + }, + decoration: normalTextFieldStyle( + "Location of the station within this parent?", + 'Location of the station within this parent?'), + name: 'isWithinParent', + title: Text(isWithinParent ? "YES" : "NO"), + ), + const SizedBox( + height: 12, + ), + Row( + //// Station Name + children: [ + Flexible( + child: FormBuilderTextField( + validator: + FormBuilderValidators.required( + errorText: + "This Field is required"), + decoration: normalTextFieldStyle( + "Station name", "Station name"), + name: "station-name"), + ), + const SizedBox( + width: 12, + ), + //// Acronym + Flexible( + child: FormBuilderTextField( + validator: + FormBuilderValidators.required( + errorText: + "This Field is required"), + decoration: normalTextFieldStyle( + "Acronym", "Acronym"), + name: "acronym"), + ), + ], + ), + const SizedBox( + height: 12, + ), + FormBuilderTextField( + ////Description + decoration: normalTextFieldStyle( + "Station description", + "Station description"), + name: "station-description"), + const SizedBox( + height: 12, + ), + Row( + children: [ + Flexible( + ////Code + child: FormBuilderTextField( + decoration: normalTextFieldStyle( + "Code", "Code"), + name: "code"), + ), + const SizedBox( + width: 12, + ), + Flexible( + //// Full Code + child: FormBuilderTextField( + decoration: normalTextFieldStyle( + "Full Code", "Full Code"), + name: "fullcode"), + ), + ], + ), + const SizedBox( + height: 12, + ), + ////is Hospital + FormBuilderSwitch( + initialValue: isHospital, + activeColor: second, + onChanged: (value) { + setState(() { + isHospital = !isHospital; + }); + }, + decoration: + normalTextFieldStyle("Is Hospital", ''), + name: 'isHospital', + title: Text(isHospital == true ? "YES" : "NO"), + ), + const SizedBox( + height: 20, + ), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + RbacStation? newStation; + if (formKey.currentState! + .saveAndValidate()) { + String? stationName = formKey + .currentState! + .value['station-name']; + String? acronym = formKey + .currentState!.value['acronym']; + String? code = formKey + .currentState!.value['code']; + String? fullcode = formKey + .currentState!.value['fullcode']; + String? description = formKey + .currentState! + .value['station-description']; + newStation = RbacStation( + id: null, + stationName: stationName, + stationType: selectedStationType, + hierarchyOrderNo: mainParent + ? 1 + : selectedParentStation! + .hierarchyOrderNo! + + 1, + headPosition: + selectedPositiontitle?.title, + governmentAgency: + GovernmentAgency( + agencyid: + selectedAgencyId, + agencyname: null, + agencycatid: null, + privateEntity: null, + contactinfoid: null), + acronym: acronym, + parentStation: mainParent + ? null + : selectedParentStation!.id!, + code: code, + fullcode: fullcode, + childStationInfo: null, + islocationUnderParent: + isWithinParent, + mainParentStation: mainParent + ? null + : selectedMainParentStation! + .id!, + description: description, + ishospital: isHospital, + isactive: true, + sellingStation: null); + Navigator.pop(context); + rbacStationBloc.add(AddRbacStation( + station: newStation)); + } + }, + child: const Text("Add"))), + ], + ); + }), ), ), ); }); - }) + }), + ////Filter + IconButton( + onPressed: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text( + "Select agency to filter stations", + textAlign: TextAlign.center, + ), + content: SizedBox( + child: // //// Filter Agencies + Padding( + padding: const EdgeInsets.all(8), + child: SearchField( + inputFormatters: [UpperCaseTextFormatter()], + itemHeight: 100, + focusNode: agencyFocusNode, + suggestions: agencies + .map((Agency agency) => + SearchFieldListItem(agency.name!, + item: agency, + child: ListTile( + title: Text( + agency.name!, + overflow: TextOverflow.visible, + ), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle("Filter", "").copyWith( + suffixIcon: IconButton( + icon: const Icon(Icons.arrow_drop_down), + onPressed: () { + agencyFocusNode.unfocus(); + }, + )), + onSuggestionTap: (agency) { + agencyFocusNode.unfocus(); + + selectedAgencyId = agency.item!.id!; + print(selectedAgencyId); + Navigator.pop(context); + rbacStationBloc.add(FilterStation( + agencyId: selectedAgencyId)); + }, + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + emptyWidget: const Center( + child: Text("No result found..."), + )), + ), + ), + ); + }); + }, + icon: const Icon(Icons.filter_list)) ], ), body: ProgressHUD( @@ -114,62 +499,85 @@ class RbacStationScreen extends StatelessWidget { backgroundColor: Colors.black87, indicatorWidget: const SpinKitFadingCircle(color: Colors.white), child: BlocConsumer( - listener: (context, state) {}, + listener: (context, state) { + if (state is StationLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is RbacStationAddedState) { + if (state.response['success']) { + successAlert( + context, "Adding Successfull!", state.response['message'], + () { + Navigator.of(context).pop(); + context + .read() + .add(GetStations(agencyId: selectedAgencyId)); + }); + } else { + errorAlert(context, "Adding Failed", state.response['message'], + () { + Navigator.of(context).pop(); + context + .read() + .add(GetStations(agencyId: selectedAgencyId)); + }); + } + } + if (state is StationLoadedState || + state is RbacStationAddedState || + state is StationErrorState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + }, builder: (context, state) { final parent = context; if (state is StationLoadedState) { stations = state.stations; - if (state.stations.isNotEmpty) { + stationTypes = state.stationTypes; + positions = state.positions; + agencies = state.agencies; + int max = 0; + hierarchy = []; + for (RbacStation station in stations) { + if (station.hierarchyOrderNo != null) { + if (max < station.hierarchyOrderNo!) { + max = station.hierarchyOrderNo!; + } + } + } + for (int i = 1; i <= max; i++) { + hierarchy.add({i: []}); + } + for (var station in stations) { + if (station.hierarchyOrderNo != null) { + for (int i = 0; i <= max; i++) { + if (station.hierarchyOrderNo == i + 1) { + hierarchy[i][i + 1].add(station); + } + } + } + } + + if (stations.isNotEmpty && hierarchy[0][1].isNotEmpty) { return Column( children: [ - Padding( - padding: const EdgeInsets.all(8), - child: SearchField( - inputFormatters: [UpperCaseTextFormatter()], - itemHeight: 70, - focusNode: agencyFocusNode, - suggestions: state.agencies - .map((Agency agency) => - SearchFieldListItem(agency.name!, - item: agency, - child: ListTile( - title: Text( - agency.name!, - overflow: TextOverflow.visible, - ), - ))) - .toList(), - searchInputDecoration: - normalTextFieldStyle("Filter", "").copyWith( - prefixIcon: const Icon(Icons.filter_list), - suffixIcon: IconButton( - icon: const Icon(Icons.arrow_drop_down), - onPressed: () { - agencyFocusNode.unfocus(); - }, - )), - onSuggestionTap: (agency) { - agencyFocusNode.unfocus(); - selectedAgency = agency.item!; - parent.read().add( - FilterStation(agencyId: selectedAgency.id!)); - }, - validator: (agency) { - if (agency!.isEmpty) { - return "This field is required"; - } - return null; - }, - emptyWidget: const Center( - child: Text("No result found..."), - )), - ), Expanded( child: ListView.builder( padding: const EdgeInsets.symmetric( vertical: 8, horizontal: 10), - itemCount: state.stations.length, + itemCount: hierarchy[0][1].length, itemBuilder: (BuildContext context, int index) { + List second = []; + if (max >= 2) { + for (var rbacStation in hierarchy[1][2]) { + if (rbacStation.parentStation == + hierarchy[0][1][index].id) { + second.add(rbacStation); + } + } + } return Column( children: [ Container( @@ -182,15 +590,15 @@ class RbacStationScreen extends StatelessWidget { Expanded( child: Row( children: [ - CircleAvatar( - child: Text('${index + 1}'), + const CircleAvatar( + child: Text('1'), ), const SizedBox( width: 12, ), Flexible( child: Text( - state.stations[index] + hierarchy[0][1][index] .stationName!, style: Theme.of(context) .textTheme @@ -205,69 +613,261 @@ class RbacStationScreen extends StatelessWidget { ], ), ), - const SizedBox( - height: 3, - ) + ////SECOND + SizedBox( + child: second.isNotEmpty + ? Column( + mainAxisSize: MainAxisSize.min, + children: second.map((e) { + List childs = []; + if (max >= 3) { + for (RbacStation station + in hierarchy[2][3]) { + if (station.parentStation == + e.id) { + childs.add(station); + } + } + } else { + childs = []; + } + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + Expanded( + child: Container( + width: screenWidth, + decoration: box1() + .copyWith( + boxShadow: []), + padding: + const EdgeInsets + .only( + left: 30), + child: Row( + children: [ + Expanded( + child: Row( + children: [ + Padding( + padding: + const EdgeInsets.all( + 6), + child: + Text( + "2", + style: Theme.of(context) + .textTheme + .bodyLarge, + selectionColor: + Colors.redAccent, + )), + const SizedBox( + width: 12, + ), + Flexible( + child: Text( + e + .stationName!, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: FontWeight.w500, + color: primary)), + ), + ], + )), + ], + ), + ), + ), + ], + ), + ////THIRD + SizedBox( + child: childs.isNotEmpty + ? Column( + mainAxisSize: + MainAxisSize + .min, + children: childs + .map((e) { + List + childs = []; + if (max >= 4) { + for (RbacStation station + in hierarchy[ + 3] + [4]) { + if (station + .parentStation == + e.id) { + childs.add( + station); + } + } + } else { + childs = []; + } + return Column( + children: [ + Container( + width: + screenWidth, + decoration: + box1() + .copyWith(boxShadow: []), + padding: const EdgeInsets + .only( + left: + 50), + child: + Row( + children: [ + Expanded( + child: Row( + children: [ + Padding( + padding: const EdgeInsets.all(6), + child: Text( + "3", + style: Theme.of(context).textTheme.bodyLarge, + selectionColor: Colors.redAccent, + )), + const SizedBox( + width: 12, + ), + Flexible( + child: Text(e.stationName!, style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500, color: primary)), + ), + ], + )), + ], + ), + ), + ////Fourth + SizedBox( + child: childs.isNotEmpty + ? Column( + mainAxisSize: MainAxisSize.min, + children: childs.map((e) { + List childs = []; + if (max > 4) { + for (RbacStation station in hierarchy[4][5]) { + if (station.parentStation == e.id) { + childs.add(station); + } + } + } else { + childs = []; + } + return Column( + children: [ + Container( + width: screenWidth, + decoration: box1().copyWith(boxShadow: []), + padding: const EdgeInsets.only(left: 80), + child: Row( + children: [ + Expanded( + child: Row( + children: [ + Padding( + padding: const EdgeInsets.all(6), + child: Text( + "4", + style: Theme.of(context).textTheme.bodyLarge, + selectionColor: Colors.redAccent, + )), + const SizedBox( + width: 12, + ), + Flexible( + child: Text(e.stationName!, style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500, color: primary)), + ), + ], + )), + ], + ), + ), + ////Fifth + SizedBox( + child: childs.isNotEmpty + ? Column( + mainAxisSize: MainAxisSize.min, + children: childs.map((e) { + List childs = []; + if (max > 5) { + for (RbacStation station in hierarchy[5][6]) { + if (station.parentStation == e.id) { + childs.add(station); + } + } + } else { + childs = []; + } + + return Column( + children: [ + Container( + width: screenWidth, + decoration: box1(), + padding: const EdgeInsets.only(left: 80), + child: Row( + children: [ + Expanded( + child: Row( + children: [ + const CircleAvatar( + child: Text('5'), + ), + const SizedBox( + width: 12, + ), + Flexible( + child: Text(e.stationName!, style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w500, color: primary)), + ), + ], + )), + ], + ), + ), + ], + ); + }).toList(), + ) + : const SizedBox()), + ], + ); + }).toList(), + ) + : const SizedBox()), + ], + ); + }).toList(), + ) + : const SizedBox + .shrink()), + ], + ); + }).toList(), + ) + : const SizedBox()), + const Divider( + height: 5, + ), ], ); }), - ), + ) ], ); } else { - return Column( - children: [ - Padding( - padding: const EdgeInsets.all(8), - child: SearchField( - - inputFormatters: [UpperCaseTextFormatter()], - itemHeight: 70, - focusNode: agencyFocusNode, - suggestions: state.agencies - .map((Agency agency) => - SearchFieldListItem(agency.name!, - item: agency, - child: ListTile( - title: Text( - agency.name!, - overflow: TextOverflow.visible, - ), - ))) - .toList(), - searchInputDecoration: - normalTextFieldStyle("Filter", "").copyWith( - prefixIcon: const Icon(Icons.filter_list), - suffixIcon: IconButton( - icon: const Icon(Icons.arrow_drop_down), - onPressed: () { - agencyFocusNode.unfocus(); - }, - )), - onSuggestionTap: (agency) { - agencyFocusNode.unfocus(); - selectedAgency = agency.item!; - parent.read().add( - FilterStation(agencyId: selectedAgency.id!)); - }, - validator: (agency) { - if (agency!.isEmpty) { - return "This field is required"; - } - return null; - }, - emptyWidget: const Center( - child: Text("No result found..."), - )), - ), - const SizedBox( - height: 20, - ), - const EmptyData( - message: - "No Station available. Please click + to add."), - ], - ); + return const EmptyData( + message: "No Station available. Please click + to add."); } } if (state is StationErrorState) { @@ -277,9 +877,7 @@ class RbacStationScreen extends StatelessWidget { context.read().add(GetRoles()); }); } - if (state is StationLoadingState) { - return CircularProgressIndicator(); - } + return Container(); }, ), diff --git a/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart b/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart index d345097..4d90a08 100644 --- a/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart +++ b/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart @@ -204,6 +204,7 @@ class _DashBoardState extends State { ); })); } + ////Station if (e.object.name == 'Station') { showDialog( context: context, @@ -227,8 +228,13 @@ class _DashBoardState extends State { onChanged: (value) { Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) { return BlocProvider( - create: (context) => EstPointPersonStationBloc()..add( EstPointPersonGetStations(agencyId: value.areaid!,)), - child: EstPointPersonStationScreen(agencyId: value!.areaid!,), + create: (context) => EstPointPersonStationBloc() + ..add(EstPointPersonGetStations( + agencyId: value.areaid!, + )), + child: EstPointPersonStationScreen( + agencyId: value!.areaid!, + ), ); })); }) diff --git a/lib/screens/unit2/homepage.dart/components/dashboard/superadmin_expanded_menu.dart b/lib/screens/unit2/homepage.dart/components/dashboard/superadmin_expanded_menu.dart index ffcd01b..d9d7fb7 100644 --- a/lib/screens/unit2/homepage.dart/components/dashboard/superadmin_expanded_menu.dart +++ b/lib/screens/unit2/homepage.dart/components/dashboard/superadmin_expanded_menu.dart @@ -198,26 +198,15 @@ class SuperAdminMenu extends StatelessWidget { ); })); } + if (object.object.name == 'Station') { Navigator.push(context, MaterialPageRoute( builder: (BuildContext context) { return BlocProvider( create: (context) => StationBloc() ..add(const GetStations(agencyId: 1)), - child: RbacStationScreen( - id: id, - ), - ); - })); - } - if (object.object.name == 'Station') { - Navigator.push(context, MaterialPageRoute( - builder: (BuildContext context) { - return BlocProvider( - create: (context) => StationBloc() - ..add(const GetStations(agencyId: 1)), - child: RbacStationScreen( - id: id, + child: const RbacStationScreen( + agencyId: 1, ), ); })); diff --git a/lib/screens/unit2/roles/establishment_point_person/est_point_person_station.dart b/lib/screens/unit2/roles/establishment_point_person/est_point_person_station.dart index 52cd05e..5193ef5 100644 --- a/lib/screens/unit2/roles/establishment_point_person/est_point_person_station.dart +++ b/lib/screens/unit2/roles/establishment_point_person/est_point_person_station.dart @@ -12,6 +12,7 @@ import 'package:unit2/model/utils/position.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/error_state.dart'; import '../../../../bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_bloc.dart'; +import '../../../../model/rbac/station_type.dart'; import '../../../../theme-data.dart/box_shadow.dart'; import '../../../../theme-data.dart/btn-style.dart'; import '../../../../theme-data.dart/colors.dart'; @@ -57,11 +58,13 @@ class EstPointPersonStationScreen extends StatelessWidget { AddLeading(onPressed: () { BuildContext parent = context; mainParentStations = []; + mainParent = stations.isEmpty?true:false; for (RbacStation station in stations) { if (station.hierarchyOrderNo == 1) { mainParentStations.add(station); } } + //// Add New Station showDialog( context: context, builder: (BuildContext context) { @@ -90,8 +93,8 @@ class EstPointPersonStationScreen extends StatelessWidget { validator: FormBuilderValidators.required( errorText: "This field is required"), ), - const SizedBox( - height: 8, + SizedBox( + height: mainParent?0: 8, ), //// selected main parent SizedBox( @@ -132,8 +135,8 @@ class EstPointPersonStationScreen extends StatelessWidget { "This field is required"), ), ), - const SizedBox( - height: 12, + SizedBox( + height: mainParent?0: 8, ), ////parent station SizedBox( @@ -370,7 +373,7 @@ class EstPointPersonStationScreen extends StatelessWidget { id: null, stationName: stationName, stationType: selectedStationType, - hierarchyOrderNo: + hierarchyOrderNo:mainParent?1: selectedParentStation! .hierarchyOrderNo! + 1, @@ -385,15 +388,15 @@ class EstPointPersonStationScreen extends StatelessWidget { privateEntity: null, contactinfoid: null), acronym: acronym, - parentStation: - selectedParentStation!.id!, + parentStation: mainParent?null: selectedParentStation!.id!, + code: code, fullcode: fullcode, childStationInfo: null, islocationUnderParent: isWithinParent, mainParentStation: - selectedMainParentStation! + mainParent?null:selectedMainParentStation! .id!, description: description, ishospital: isHospital, @@ -489,12 +492,14 @@ class EstPointPersonStationScreen extends StatelessWidget { itemCount: hierarchy[0][1].length, itemBuilder: (BuildContext context, int index) { List second = []; - for (var rbacStation in hierarchy[1][2]) { + if(max >=2){ + for (var rbacStation in hierarchy[1][2]) { if (rbacStation.parentStation == hierarchy[0][1][index].id) { second.add(rbacStation); } } + } return Column( children: [ Container( diff --git a/lib/sevices/roles/rbac_operations/station_services.dart b/lib/sevices/roles/rbac_operations/station_services.dart index 75dc6c3..977a1ee 100644 --- a/lib/sevices/roles/rbac_operations/station_services.dart +++ b/lib/sevices/roles/rbac_operations/station_services.dart @@ -4,6 +4,7 @@ import 'package:unit2/utils/request.dart'; import 'package:unit2/utils/urls.dart'; import 'package:http/http.dart' as http; import '../../../model/rbac/rbac_station.dart'; +import '../../../model/rbac/station_type.dart'; import '../../../model/roles/pass_check/station_assign_area.dart'; class RbacStationServices { @@ -99,12 +100,12 @@ class RbacStationServices { }; String path = Url.instance.postStation(); - // try { + try { Map body = { "station_name": station.stationName, "acronym": station.acronym, "station_type_id": station.stationType?.id, - "_station_type_name": station.stationType?.typeName, + "_station_type_name": station.stationType?.id != null?null:station.stationType?.typeName, "parent_station_id": station.parentStation, "hierarchy_order_no": station.hierarchyOrderNo, "agency_id": station.governmentAgency!.agencyid, @@ -129,9 +130,9 @@ class RbacStationServices { {'success': false}, ); } - // } catch (e) { - // throw e.toString(); - // } + } catch (e) { + throw e.toString(); + } return statusResponse; } } From 7cc0ab1f56d810a9bb4a8487e4f99c51f85721dc Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Sun, 20 Aug 2023 17:36:16 +0800 Subject: [PATCH 81/86] tested profile screen --- .../organization_membership_bloc.dart | 21 -- .../address/address_bloc.dart | 2 - lib/bloc/profile/profile_bloc.dart | 36 +- .../voluntary_works/voluntary_work_bloc.dart | 2 - .../profile/workHistory/workHistory_bloc.dart | 3 - .../role_extend/role_extend_bloc.dart | 2 - .../est_point_person_assinable_role_bloc.dart | 79 +++++ ...est_point_person_assinable_role_event.dart | 28 ++ ...est_point_person_assinable_role_state.dart | 38 +++ .../est_role_assignment_bloc.dart | 79 +++++ .../est_role_assignment_event.dart | 25 ++ .../est_role_assignment_state.dart | 36 ++ .../role_assignment/role_assignment_bloc.dart | 1 - lib/model/rbac/rbac.dart | 2 +- .../roles/pass_check/agency_area_type.dart | 21 +- .../basic_information/address_screen.dart | 1 + .../contact_information/edit_modal.dart | 8 +- .../contact_information_screen.dart | 1 + .../edit_basic_info_modal.dart | 21 +- .../identification_information_screen.dart | 1 + .../primary_information_screen.dart | 6 +- .../components/education/edit_modal.dart | 2 +- .../profile/components/education_screen.dart | 17 +- .../components/eligibility_screen.dart | 5 +- .../components/family_background_screen.dart | 5 +- .../learning_and_development_screen.dart | 4 +- .../learning_development/add_modal.dart | 219 +++++++------ .../non_academic_recognition_screen.dart | 1 + .../org_membership/add_modal.dart | 47 ++- .../org_membership_screen.dart | 30 +- .../components/reference/add_modal.dart | 7 +- .../components/reference/edit_modal.dart | 2 +- .../profile/components/references_screen.dart | 2 +- .../components/voluntary_works_screen.dart | 4 +- .../components/work_history/add_modal.dart | 3 +- .../components/work_history_screen.dart | 4 +- lib/screens/profile/profile.dart | 38 ++- .../components/dashboard/dashboard.dart | 22 ++ .../dashboard/superadmin_expanded_menu.dart | 4 +- .../est_point_person_role_member_screen.dart | 307 ++++++++++++++++++ .../est_point_person_role_under_screen.dart | 264 +++++++++++++++ lib/sevices/profile/education_services.dart | 4 +- lib/sevices/profile/profile_other_info.dart | 2 +- ...point_person_role_assignment_services.dart | 163 ++++++++++ lib/utils/attachment_services.dart | 2 +- lib/utils/request.dart | 8 +- lib/utils/urls.dart | 4 +- 47 files changed, 1307 insertions(+), 276 deletions(-) create mode 100644 lib/bloc/role/pass_check/est_point_person/est_point_person_assignable/est_point_person_assinable_role_bloc.dart create mode 100644 lib/bloc/role/pass_check/est_point_person/est_point_person_assignable/est_point_person_assinable_role_event.dart create mode 100644 lib/bloc/role/pass_check/est_point_person/est_point_person_assignable/est_point_person_assinable_role_state.dart create mode 100644 lib/bloc/role/pass_check/est_point_person/est_point_person_role_assignment/est_role_assignment_bloc.dart create mode 100644 lib/bloc/role/pass_check/est_point_person/est_point_person_role_assignment/est_role_assignment_event.dart create mode 100644 lib/bloc/role/pass_check/est_point_person/est_point_person_role_assignment/est_role_assignment_state.dart create mode 100644 lib/screens/unit2/roles/establishment_point_person/est_point_person_role_member_screen.dart create mode 100644 lib/screens/unit2/roles/establishment_point_person/est_point_person_role_under_screen.dart create mode 100644 lib/sevices/roles/est_point_person/est_point_person_role_assignment_services.dart diff --git a/lib/bloc/profile/other_information/org_membership/organization_membership_bloc.dart b/lib/bloc/profile/other_information/org_membership/organization_membership_bloc.dart index ce26bef..5d27e0b 100644 --- a/lib/bloc/profile/other_information/org_membership/organization_membership_bloc.dart +++ b/lib/bloc/profile/other_information/org_membership/organization_membership_bloc.dart @@ -49,27 +49,6 @@ class OrganizationMembershipBloc agencies: agencies, agencyCategory: agencyCategory)); }); - - ////SHOW ADD ORG MEMBERSHIP FORM - on((event, emit) async { - emit(OrgmembershipLoadingState()); - try { - if (agencies.isEmpty) { - List newAgencies = - await ProfileUtilities.instance.getAgecies(); - agencies = newAgencies; - } - if (agencyCategory.isEmpty) { - List newAgencyCategories = - await ProfileUtilities.instance.agencyCategory(); - agencyCategory = newAgencyCategories; - } - emit(AddOrgMembershipState( - agencies: agencies, agencyCategories: agencyCategory)); - } catch (e) { - emit(OrganizationMembershipErrorState(message: e.toString())); - } - }); //// ADD ORGMEMBERSHIP on((event, emit) async { try { diff --git a/lib/bloc/profile/primary_information/address/address_bloc.dart b/lib/bloc/profile/primary_information/address/address_bloc.dart index 6613fe8..5bcdd5c 100644 --- a/lib/bloc/profile/primary_information/address/address_bloc.dart +++ b/lib/bloc/profile/primary_information/address/address_bloc.dart @@ -1,9 +1,7 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; -import 'package:unit2/bloc/profile/eligibility/eligibility_bloc.dart'; import 'package:unit2/model/location/barangay.dart'; import 'package:unit2/model/location/subdivision.dart'; -import 'package:unit2/model/profile/voluntary_works.dart'; import 'package:unit2/sevices/profile/address_service.dart'; import '../../../../model/location/city.dart'; diff --git a/lib/bloc/profile/profile_bloc.dart b/lib/bloc/profile/profile_bloc.dart index 4278635..2384265 100644 --- a/lib/bloc/profile/profile_bloc.dart +++ b/lib/bloc/profile/profile_bloc.dart @@ -17,16 +17,7 @@ class ProfileBloc extends Bloc { List indigencies = []; List disabilities = []; List genders = []; - List bloodType = [ - "A+", - "B+", - "A-", - "B-", - "AB+", - "AB-", - "O+", - "O-" - ]; + List bloodType = ["A+", "B+", "A-", "B-", "AB+", "AB-", "O+", "O-"]; List nameExtensions = [ "NONE", "N/A", @@ -56,9 +47,12 @@ class ProfileBloc extends Bloc { on((event, emit) async { emit(ProfileLoading()); try { - ProfileInformation? profileInformation = - await ProfileService.instance.getProfile(event.token, event.userID); - globalProfileInformation = profileInformation; + if (globalProfileInformation == null) { + ProfileInformation? profileInformation = await ProfileService.instance + .getProfile(event.token, event.userID); + globalProfileInformation = profileInformation; + } + emit(ProfileLoaded(profileInformation: globalProfileInformation!)); } catch (e) { emit(ProfileErrorState(mesage: e.toString())); @@ -69,8 +63,8 @@ class ProfileBloc extends Bloc { emit(BasicInformationProfileLoaded( primaryBasicInformation: event.primaryBasicInformation)); }); - on((event,emit){ - emit(BasicInformationProfileLoaded( + on((event, emit) { + emit(BasicInformationProfileLoaded( primaryBasicInformation: currentProfileInformation!)); }); on((event, emit) async { @@ -79,22 +73,34 @@ class ProfileBloc extends Bloc { if (religions.isEmpty) { religions = await ProfileOtherInfoServices.instace .getReligions(token: event.token); + religions.insert( + 0, ProfileOtherInfo(id: null, name: "NONE", description: null)); } if (genders.isEmpty) { genders = await ProfileOtherInfoServices.instace .getGenders(token: event.token); + + genders.insert( + 0, ProfileOtherInfo(id: null, name: "NONE", description: null)); } if (ethnicities.isEmpty) { ethnicities = await ProfileOtherInfoServices.instace .getEthnicity(token: event.token); + + ethnicities.insert( + 0, ProfileOtherInfo(id: null, name: "NONE", description: null)); } if (disabilities.isEmpty) { disabilities = await ProfileOtherInfoServices.instace .getDisability(token: event.token); + disabilities.insert( + 0, ProfileOtherInfo(id: null, name: "NONE", description: null)); } if (indigencies.isEmpty) { indigencies = await ProfileOtherInfoServices.instace .getIndigency(token: event.token); + indigencies.insert( + 0, ProfileOtherInfo(id: null, name: "NONE", description: null)); } emit(BasicInformationEditingState( primaryInformation: currentProfileInformation!, diff --git a/lib/bloc/profile/voluntary_works/voluntary_work_bloc.dart b/lib/bloc/profile/voluntary_works/voluntary_work_bloc.dart index 5d32742..d6db36f 100644 --- a/lib/bloc/profile/voluntary_works/voluntary_work_bloc.dart +++ b/lib/bloc/profile/voluntary_works/voluntary_work_bloc.dart @@ -3,7 +3,6 @@ import 'package:equatable/equatable.dart'; import 'package:intl/intl.dart'; import 'package:unit2/sevices/profile/volunatary_services.dart'; import 'package:unit2/utils/profile_utilities.dart'; - import '../../../model/location/city.dart'; import '../../../model/location/country.dart'; import '../../../model/location/provinces.dart'; @@ -13,7 +12,6 @@ import '../../../model/utils/agency.dart'; import '../../../model/utils/category.dart'; import '../../../model/utils/position.dart'; import '../../../utils/location_utilities.dart'; - part 'voluntary_work_event.dart'; part 'voluntary_work_state.dart'; diff --git a/lib/bloc/profile/workHistory/workHistory_bloc.dart b/lib/bloc/profile/workHistory/workHistory_bloc.dart index 176954a..85bd4bd 100644 --- a/lib/bloc/profile/workHistory/workHistory_bloc.dart +++ b/lib/bloc/profile/workHistory/workHistory_bloc.dart @@ -1,17 +1,14 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; -import 'package:unit2/bloc/profile/eligibility/eligibility_bloc.dart'; import 'package:unit2/model/profile/work_history.dart'; import 'package:unit2/model/utils/agency.dart'; import 'package:unit2/model/utils/agency_position.dart'; import 'package:unit2/model/utils/position.dart'; import 'package:unit2/sevices/profile/work_history_services.dart'; import 'package:unit2/utils/profile_utilities.dart'; - import '../../../model/profile/attachment.dart'; import '../../../model/utils/category.dart'; import '../../../utils/attachment_services.dart'; - part 'workHistory_event.dart'; part 'workHistory_state.dart'; diff --git a/lib/bloc/rbac/rbac_operations/role_extend/role_extend_bloc.dart b/lib/bloc/rbac/rbac_operations/role_extend/role_extend_bloc.dart index f4c75d8..85e6837 100644 --- a/lib/bloc/rbac/rbac_operations/role_extend/role_extend_bloc.dart +++ b/lib/bloc/rbac/rbac_operations/role_extend/role_extend_bloc.dart @@ -1,9 +1,7 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; -import 'package:unit2/bloc/rbac/rbac_operations/roles_under/roles_under_bloc.dart'; import 'package:unit2/model/rbac/role_extend.dart'; import 'package:unit2/sevices/roles/rbac_operations/role_extend_services.dart'; - import '../../../../model/rbac/rbac.dart'; import '../../../../sevices/roles/rbac_operations/role_services.dart'; diff --git a/lib/bloc/role/pass_check/est_point_person/est_point_person_assignable/est_point_person_assinable_role_bloc.dart b/lib/bloc/role/pass_check/est_point_person/est_point_person_assignable/est_point_person_assinable_role_bloc.dart new file mode 100644 index 0000000..0ca0aef --- /dev/null +++ b/lib/bloc/role/pass_check/est_point_person/est_point_person_assignable/est_point_person_assinable_role_bloc.dart @@ -0,0 +1,79 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/model/rbac/rbac.dart'; +import 'package:unit2/model/rbac/role_under.dart'; + +import '../../../../../sevices/roles/est_point_person/est_point_person_role_assignment_services.dart'; +import '../../../../../sevices/roles/rbac_operations/role_services.dart'; +import '../../../../../sevices/roles/rbac_operations/roles_under_services.dart'; + +part 'est_point_person_assinable_role_event.dart'; +part 'est_point_person_assinable_role_state.dart'; + +class EstPointPersonAssinableRoleBloc extends Bloc< + EstPointPersonAssinableRoleEvent, EstPointPersonAssinableRoleState> { + EstPointPersonAssinableRoleBloc() + : super(EstPointPersonAssinableRoleInitial()) { + List rolesUnder = []; + List roles = []; + RBAC? mainRole; + on((event, emit) async { + emit(EstPointPersonAssignableRoleLoadingState()); + try { + if (rolesUnder.isEmpty) { + List rolesUnders = await EstPointPersonRoleAssignment + .instance + .getRolesUnder(roleId: event.roleId); + for (var roleUnder in rolesUnders) { + RBAC roleChild = roleUnder.roleUnderChild; + roleChild.id = roleUnder.id; + rolesUnder.add(roleUnder.roleUnderChild); + } + } + if (roles.isEmpty) { + roles = await RbacRoleServices.instance.getRbacRoles(); + } + RBAC role = roles.firstWhere((e) => e.id == event.roleId); + mainRole = role; + emit(EstPointPersonAssignableRoleLoaded( + roles: roles, rolesUnder: rolesUnder, mainRole: role)); + } catch (e) { + emit(EstPointPersonAssignableErrorState(message: e.toString())); + } + }); + on((event, emit) async { + try { + emit(EstPointPersonAssignableRoleLoadingState()); + Map statusResponse = await RbacRoleUnderServices + .instance + .add(roleId: event.mainRoleId, rolesId: event.rolesUnder); + + if (statusResponse['success']) { + emit(EstPointPersonAssignableRoleLoaded( + mainRole: mainRole!, roles: roles, rolesUnder: rolesUnder)); + } else { + emit(EstPointPersonAssignableRoleLoaded( + mainRole: mainRole!, roles: roles, rolesUnder: rolesUnder)); + } + } catch (e) { + emit(EstPointPersonAssignableErrorState(message: e.toString())); + } + }); + + on((event, emit) async { + emit(EstPointPersonAssignableRoleLoadingState()); + try { + bool success = await RbacRoleUnderServices.instance + .deleteRbacRoleUnder(roleUnderId: event.roleId); + if (success) { + rolesUnder.removeWhere((element) => element.id == event.roleId); + emit(EstPointPersonAssignableDeletedState(success: success)); + } else { + emit(EstPointPersonAssignableDeletedState(success: success)); + } + } catch (e) { + emit(EstPointPersonAssignableErrorState(message: e.toString())); + } + }); + } +} diff --git a/lib/bloc/role/pass_check/est_point_person/est_point_person_assignable/est_point_person_assinable_role_event.dart b/lib/bloc/role/pass_check/est_point_person/est_point_person_assignable/est_point_person_assinable_role_event.dart new file mode 100644 index 0000000..8d4fb53 --- /dev/null +++ b/lib/bloc/role/pass_check/est_point_person/est_point_person_assignable/est_point_person_assinable_role_event.dart @@ -0,0 +1,28 @@ +part of 'est_point_person_assinable_role_bloc.dart'; + +abstract class EstPointPersonAssinableRoleEvent extends Equatable { + const EstPointPersonAssinableRoleEvent(); + + @override + List get props => []; +} + +class GetEstPointPersonAssignableRoles + extends EstPointPersonAssinableRoleEvent { + final int roleId; + const GetEstPointPersonAssignableRoles({required this.roleId}); +} + +class AddEstPointPersonAssignableRoles + extends EstPointPersonAssinableRoleEvent { + final int mainRoleId; + final List rolesUnder; + const AddEstPointPersonAssignableRoles( + {required this.mainRoleId, required this.rolesUnder}); +} + +class DeleteEstPointPersonAssignableRoles + extends EstPointPersonAssinableRoleEvent { + final int roleId; + const DeleteEstPointPersonAssignableRoles({required this.roleId}); +} diff --git a/lib/bloc/role/pass_check/est_point_person/est_point_person_assignable/est_point_person_assinable_role_state.dart b/lib/bloc/role/pass_check/est_point_person/est_point_person_assignable/est_point_person_assinable_role_state.dart new file mode 100644 index 0000000..3ce7cbe --- /dev/null +++ b/lib/bloc/role/pass_check/est_point_person/est_point_person_assignable/est_point_person_assinable_role_state.dart @@ -0,0 +1,38 @@ +part of 'est_point_person_assinable_role_bloc.dart'; + +abstract class EstPointPersonAssinableRoleState extends Equatable { + const EstPointPersonAssinableRoleState(); + + @override + List get props => []; +} + +class EstPointPersonAssignableRoleLoadingState + extends EstPointPersonAssinableRoleState {} + +class EstPointPersonAssignableErrorState + extends EstPointPersonAssinableRoleState { + final String message; + const EstPointPersonAssignableErrorState({required this.message}); +} + +class EstPointPersonAssginableRoleAssignState + extends EstPointPersonAssinableRoleState { + final Map responseStatus; + const EstPointPersonAssginableRoleAssignState({required this.responseStatus}); +} + +class EstPointPersonAssinableRoleInitial + extends EstPointPersonAssinableRoleState {} + +class EstPointPersonAssignableRoleLoaded + extends EstPointPersonAssinableRoleState { + final List roles; + final List rolesUnder; + final RBAC mainRole; + const EstPointPersonAssignableRoleLoaded({required this.roles, required this.rolesUnder, required this.mainRole}); +} +class EstPointPersonAssignableDeletedState extends EstPointPersonAssinableRoleState { + final bool success; + const EstPointPersonAssignableDeletedState({required this.success}); +} diff --git a/lib/bloc/role/pass_check/est_point_person/est_point_person_role_assignment/est_role_assignment_bloc.dart b/lib/bloc/role/pass_check/est_point_person/est_point_person_role_assignment/est_role_assignment_bloc.dart new file mode 100644 index 0000000..b85da22 --- /dev/null +++ b/lib/bloc/role/pass_check/est_point_person/est_point_person_role_assignment/est_role_assignment_bloc.dart @@ -0,0 +1,79 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:unit2/model/rbac/assigned_role.dart'; +import 'package:unit2/model/rbac/role_under.dart'; + +import '../../../../../model/rbac/rbac.dart'; +import '../../../../../sevices/roles/est_point_person/est_point_person_role_assignment_services.dart'; +import '../../../../../sevices/roles/rbac_operations/role_assignment_services.dart'; + +part 'est_role_assignment_event.dart'; +part 'est_role_assignment_state.dart'; + +class EstRoleAssignmentBloc + extends Bloc { + EstRoleAssignmentBloc() : super(EstRoleAssignmentInitial()) { + List rolesUnders = []; + List assignedRoles = []; + List assignableRoles = []; + on((event, emit) async { + emit(EstPointPersonRoleLoadingState()); + try { + if (assignedRoles.isEmpty) { + assignedRoles = await EstPointPersonRoleAssignment.instance + .getAssignedRoles(webuserId: event.userId); + } + if (rolesUnders.isEmpty) { + rolesUnders = await EstPointPersonRoleAssignment.instance + .getRolesUnder(roleId: 16); + } + for (var roleUnder in rolesUnders) { + assignableRoles.add(roleUnder.roleUnderChild); + } + emit(EstPointPersonRolesUnderLoadedState( + assignedRoles: assignedRoles, + rolesUnders: rolesUnders, + assignableRole: assignableRoles)); + } catch (e) { + emit(EstPointPersonRolesUnderErrorState(message: e.toString())); + } + }); + on((event, emit) async { + emit(EstPointPersonRoleLoadingState()); + try { + Map statusResponse = + await RbacRoleAssignmentServices.instance.add( + userId: event.userId, + assignerId: event.assingerId, + roles: event.rolesId); + if (statusResponse['success']) { + assignedRoles = []; + statusResponse['data'].forEach((var roles) { + AssignedRole newAssignRole = AssignedRole.fromJson(roles); + + }); + emit(EstPointPersonRoleUnderAddedState(response: statusResponse)); + } else { + emit(EstPointPersonRoleUnderAddedState(response: statusResponse)); + } + } catch (e) { + emit(EstPointPersonRolesUnderErrorState(message: e.toString())); + } + }); + on((event, emit) async { + emit(EstPointPersonRoleLoadingState()); + try { + bool success = await RbacRoleAssignmentServices.instance + .deleteAssignedRole(roleId: event.roleId); + if (success) { + assignedRoles.removeWhere((element) => element.id == event.roleId); + emit(EstPointPersonDeletedState(success: success)); + } else { + emit(EstPointPersonDeletedState(success: success)); + } + } catch (e) { + emit(EstPointPersonRolesUnderErrorState(message: e.toString())); + } + }); + } +} diff --git a/lib/bloc/role/pass_check/est_point_person/est_point_person_role_assignment/est_role_assignment_event.dart b/lib/bloc/role/pass_check/est_point_person/est_point_person_role_assignment/est_role_assignment_event.dart new file mode 100644 index 0000000..a0ea7c3 --- /dev/null +++ b/lib/bloc/role/pass_check/est_point_person/est_point_person_role_assignment/est_role_assignment_event.dart @@ -0,0 +1,25 @@ +part of 'est_role_assignment_bloc.dart'; + +abstract class EstRoleAssignmentEvent extends Equatable { + const EstRoleAssignmentEvent(); + + @override + List get props => []; +} + +class GetEstPointPersonRolesUnder extends EstRoleAssignmentEvent{ + final int userId; + const GetEstPointPersonRolesUnder({required this.userId}); +} + +class EstPointPersonAssignRole extends EstRoleAssignmentEvent{ + final int userId; + final List rolesId; + final int assingerId; + const EstPointPersonAssignRole({required this.assingerId, required this.rolesId, required this.userId}); +} +class EstPointPersonDeleteAssignRole extends EstRoleAssignmentEvent { + final int roleId; + const EstPointPersonDeleteAssignRole({required this.roleId}); +} + diff --git a/lib/bloc/role/pass_check/est_point_person/est_point_person_role_assignment/est_role_assignment_state.dart b/lib/bloc/role/pass_check/est_point_person/est_point_person_role_assignment/est_role_assignment_state.dart new file mode 100644 index 0000000..c047a3a --- /dev/null +++ b/lib/bloc/role/pass_check/est_point_person/est_point_person_role_assignment/est_role_assignment_state.dart @@ -0,0 +1,36 @@ +part of 'est_role_assignment_bloc.dart'; + +abstract class EstRoleAssignmentState extends Equatable { + const EstRoleAssignmentState(); + + @override + List get props => []; +} + +class EstPointPersonRolesUnderLoadedState extends EstRoleAssignmentState{ + final List rolesUnders; + final List assignedRoles; + final List assignableRole; + const EstPointPersonRolesUnderLoadedState({required this.assignedRoles, required this.rolesUnders, required this.assignableRole}); +} + +class EstPointPersonRolesUnderErrorState extends EstRoleAssignmentState{ + final String message; + const EstPointPersonRolesUnderErrorState({required this.message}); +} + +class EstPointPersonRoleLoadingState extends EstRoleAssignmentState{ + +} +class EstPointPersonRoleUnderAddedState extends EstRoleAssignmentState{ + final Map response; + const EstPointPersonRoleUnderAddedState({required this.response}); +} + +class EstPointPersonDeletedState extends EstRoleAssignmentState { + final bool success; + const EstPointPersonDeletedState({required this.success}); +} + + +class EstRoleAssignmentInitial extends EstRoleAssignmentState {} diff --git a/lib/bloc/role_assignment/role_assignment_bloc.dart b/lib/bloc/role_assignment/role_assignment_bloc.dart index 2d75f2d..426a795 100644 --- a/lib/bloc/role_assignment/role_assignment_bloc.dart +++ b/lib/bloc/role_assignment/role_assignment_bloc.dart @@ -26,7 +26,6 @@ class RoleAssignmentBloc fname = event.firstname; lname = event.lastname; fullname = "${event.firstname} ${event.lastname}"; - try { profile = await RbacRoleAssignmentServices.instance.searchUser( page: 1, name: event.firstname, lastname: event.lastname); diff --git a/lib/model/rbac/rbac.dart b/lib/model/rbac/rbac.dart index 9fb897d..5f83962 100644 --- a/lib/model/rbac/rbac.dart +++ b/lib/model/rbac/rbac.dart @@ -10,7 +10,7 @@ RBAC rbacFromJson(String str) => RBAC.fromJson(json.decode(str)); String rbacToJson(RBAC data) => json.encode(data.toJson()); class RBAC { - final int? id; + int? id; final String? name; final String? slug; final String? shorthand; diff --git a/lib/model/roles/pass_check/agency_area_type.dart b/lib/model/roles/pass_check/agency_area_type.dart index 260a7c9..cb595cc 100644 --- a/lib/model/roles/pass_check/agency_area_type.dart +++ b/lib/model/roles/pass_check/agency_area_type.dart @@ -1,4 +1,6 @@ +import '../../utils/category.dart'; + class AgencyAssignedArea { final bool? isactive; final Area? area; @@ -51,25 +53,6 @@ class Area { }; } -class Category { - final int? id; - final IndustryClass? industryClass; - - Category({ - required this.id, - required this.industryClass, - }); - - factory Category.fromJson(Map json) => Category( - id: json["id"], - industryClass: json["industry_class"] == null?null:IndustryClass.fromJson(json["industry_class"]), - ); - - Map toJson() => { - "id": id, - "industry_class": industryClass?.toJson(), - }; -} class IndustryClass { final int? id; diff --git a/lib/screens/profile/components/basic_information/address_screen.dart b/lib/screens/profile/components/basic_information/address_screen.dart index 9a0d844..1df1ddc 100644 --- a/lib/screens/profile/components/basic_information/address_screen.dart +++ b/lib/screens/profile/components/basic_information/address_screen.dart @@ -26,6 +26,7 @@ class AddressScreen extends StatelessWidget { int? profileId; String? token; return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: context.watch().state is AddAddressState?const Text("Add Address"):context.watch().state is EditAddressState?const Text("Edit Address"):const Text("Addresses"), centerTitle: true, diff --git a/lib/screens/profile/components/basic_information/contact_information/edit_modal.dart b/lib/screens/profile/components/basic_information/contact_information/edit_modal.dart index 2d8166b..a372064 100644 --- a/lib/screens/profile/components/basic_information/contact_information/edit_modal.dart +++ b/lib/screens/profile/components/basic_information/contact_information/edit_modal.dart @@ -62,8 +62,8 @@ class _EditContactInformationScreenState selectedServiceType = state.selectedServiceType; selectedCommProvider = state.selectedProvider; commServiceProviders = state.commServiceProviders; - primaryaContact = state.contactInfo.primary!; - active = state.contactInfo.active!; + primaryaContact = state.contactInfo.primary; + active = state.contactInfo.active; numberMailController.text = state.contactInfo.numbermail!; return Padding( padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 24), @@ -223,7 +223,7 @@ class _EditContactInformationScreenState }); }, decoration: normalTextFieldStyle("", ''), - name: 'overseas', + name: 'primary', title: const Text("Primary ?"), ); }), @@ -241,7 +241,7 @@ class _EditContactInformationScreenState }); }, decoration: normalTextFieldStyle("", ''), - name: 'overseas', + name: 'active', title: const Text("Active ?"), ); }), diff --git a/lib/screens/profile/components/basic_information/contact_information_screen.dart b/lib/screens/profile/components/basic_information/contact_information_screen.dart index 3e0f4d2..2d666cd 100644 --- a/lib/screens/profile/components/basic_information/contact_information_screen.dart +++ b/lib/screens/profile/components/basic_information/contact_information_screen.dart @@ -28,6 +28,7 @@ class ContactInformationScreen extends StatelessWidget { String token; return SafeArea( child: Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: context.watch().state is ContactAddingState ? const Text("Add Contact") diff --git a/lib/screens/profile/components/basic_information/edit_basic_info_modal.dart b/lib/screens/profile/components/basic_information/edit_basic_info_modal.dart index 095ca78..4a24280 100644 --- a/lib/screens/profile/components/basic_information/edit_basic_info_modal.dart +++ b/lib/screens/profile/components/basic_information/edit_basic_info_modal.dart @@ -99,16 +99,9 @@ class _EditBasicProfileInfoScreenState state.primaryInformation.disability!.toLowerCase()); } - state.genders.insert( - 0, ProfileOtherInfo(id: null, name: "NONE", description: null)); - state.indigenous.insert( - 0, ProfileOtherInfo(id: null, name: "NONE", description: null)); - state.disability.insert( - 0, ProfileOtherInfo(id: null, name: "NONE", description: null)); - state.ethnicity.insert( - 0, ProfileOtherInfo(id: null, name: "NONE", description: null)); - state.religion.insert( - 0, ProfileOtherInfo(id: null, name: "NONE", description: null)); + + + return Container( padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 32), child: FormBuilder( @@ -375,7 +368,7 @@ class _EditBasicProfileInfoScreenState ////Indigency Flexible( flex: 1, - child: DropdownButtonFormField( + child: DropdownButtonFormField( isExpanded: true, value: selectedIndigency, decoration: normalTextFieldStyle("Indigency", ""), @@ -408,6 +401,9 @@ class _EditBasicProfileInfoScreenState .toList(), onChanged: (e) { selectedEthnicity = e; + print(selectedEthnicity!.name); + print(selectedEthnicity! + .id); }, ), ), @@ -444,6 +440,7 @@ class _EditBasicProfileInfoScreenState Flexible( flex: 1, child: DropdownButtonFormField( + isExpanded: true, value: selectedDisability, decoration: normalTextFieldStyle("Disability", ""), @@ -524,7 +521,7 @@ class _EditBasicProfileInfoScreenState context.read().add( EditBasicProfileInformation( disabilityId: selectedDisability?.id, - ethnicityId: selectedDisability?.id, + ethnicityId: selectedEthnicity?.id, genderId: selectedGender?.id, indigencyId: selectedIndigency?.id, profileId: widget.profileId, diff --git a/lib/screens/profile/components/basic_information/identification_information_screen.dart b/lib/screens/profile/components/basic_information/identification_information_screen.dart index 315189a..4fc6104 100644 --- a/lib/screens/profile/components/basic_information/identification_information_screen.dart +++ b/lib/screens/profile/components/basic_information/identification_information_screen.dart @@ -29,6 +29,7 @@ class IdentificationsScreen extends StatelessWidget { String? token; int? profileId; return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: context.watch().state is IdentificationAddingState ? const Text("Add Identification"):context.watch().state is IdentificationEditingState?const Text("Edit Identification"):const Text("Identifications"), diff --git a/lib/screens/profile/components/basic_information/primary_information_screen.dart b/lib/screens/profile/components/basic_information/primary_information_screen.dart index f574e08..1927bb1 100644 --- a/lib/screens/profile/components/basic_information/primary_information_screen.dart +++ b/lib/screens/profile/components/basic_information/primary_information_screen.dart @@ -28,8 +28,6 @@ class PrimaryInfo extends StatefulWidget { class _PrimaryInfoState extends State { @override Widget build(BuildContext context) { - final _formKey = GlobalKey(); - bool enabled = false; DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); return Scaffold( @@ -237,6 +235,7 @@ class _PrimaryInfoState extends State { Flexible( flex: 1, child: FormBuilderTextField( + maxLines: 2, enabled: enabled, name: height, initialValue: state @@ -284,6 +283,7 @@ class _PrimaryInfoState extends State { Flexible( flex: 1, child: FormBuilderTextField( + maxLines: 2, enabled: enabled, name: height, initialValue: state.primaryBasicInformation.ip ??= @@ -316,6 +316,7 @@ class _PrimaryInfoState extends State { Flexible( flex: 1, child: FormBuilderTextField( + maxLines: 2, enabled: enabled, name: height, initialValue: state @@ -329,6 +330,7 @@ class _PrimaryInfoState extends State { Flexible( flex: 1, child: FormBuilderTextField( + maxLines: 2, enabled: enabled, name: width, initialValue: state diff --git a/lib/screens/profile/components/education/edit_modal.dart b/lib/screens/profile/components/education/edit_modal.dart index 0cf7ced..44812cd 100644 --- a/lib/screens/profile/components/education/edit_modal.dart +++ b/lib/screens/profile/components/education/edit_modal.dart @@ -558,7 +558,7 @@ class _EditEducationScreenState extends State { yearGraduated: graduated ? yearGraduated.text : null, unitsEarned: !graduated - ? int.parse(unitsController.text) + ? int.tryParse(unitsController.text) : null, attachments: null, ); diff --git a/lib/screens/profile/components/education_screen.dart b/lib/screens/profile/components/education_screen.dart index fb32a9a..15c88a0 100644 --- a/lib/screens/profile/components/education_screen.dart +++ b/lib/screens/profile/components/education_screen.dart @@ -7,10 +7,7 @@ import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_svg/svg.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; -import 'package:unit2/bloc/profile/eligibility/eligibility_bloc.dart'; -import 'package:unit2/bloc/profile/learningDevelopment/learning_development_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; -import 'package:unit2/bloc/profile/workHistory/workHistory_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/educational_background.dart'; import 'package:unit2/screens/profile/components/education/add_modal.dart'; @@ -42,6 +39,7 @@ class EducationScreen extends StatelessWidget { int profileId; String? token; return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: context.watch().state is AddEducationState ? const FittedBox(child: Text("Add Educational Background")) @@ -322,10 +320,13 @@ class EducationScreen extends StatelessWidget { ), Column( children: honors - .map((Honor honor) => Text( - "-${honor.name!.trim()}", - style: Theme.of(context).textTheme.labelMedium, - )) + .map((Honor honor) => Padding( + padding: const EdgeInsets.all(3.0), + child: Text( + "-${honor.name!.trim()}", + style: Theme.of(context).textTheme.labelSmall, + ), + )) .toList(), ), ], @@ -398,7 +399,7 @@ class EducationScreen extends StatelessWidget { index])); } if (value == 3) { - results.clear(); + results.clear(); showDialog( context: context, builder: diff --git a/lib/screens/profile/components/eligibility_screen.dart b/lib/screens/profile/components/eligibility_screen.dart index 5b10940..0a51d26 100644 --- a/lib/screens/profile/components/eligibility_screen.dart +++ b/lib/screens/profile/components/eligibility_screen.dart @@ -8,10 +8,7 @@ import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_svg/svg.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; -import 'package:unit2/bloc/profile/education/education_bloc.dart'; -import 'package:unit2/bloc/profile/learningDevelopment/learning_development_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; -import 'package:unit2/bloc/profile/workHistory/workHistory_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/attachment.dart'; import 'package:unit2/model/profile/eligibility.dart'; @@ -49,7 +46,7 @@ class EligibiltyScreen extends StatelessWidget { return true; }, child: Scaffold( - resizeToAvoidBottomInset: true, + resizeToAvoidBottomInset: false, appBar: AppBar( title: context.watch().state is AddEligibilityState ? const Text("Add Eligiblity") diff --git a/lib/screens/profile/components/family_background_screen.dart b/lib/screens/profile/components/family_background_screen.dart index 16c5280..24106e8 100644 --- a/lib/screens/profile/components/family_background_screen.dart +++ b/lib/screens/profile/components/family_background_screen.dart @@ -1316,6 +1316,7 @@ class _FamilyBackgroundScreenState extends State { ], ), Container( + child: children.isNotEmpty ? Column( mainAxisSize: @@ -1332,7 +1333,7 @@ class _FamilyBackgroundScreenState extends State { const EdgeInsets .symmetric( horizontal: 12, - vertical: 8), + vertical: 0), width: screenWidth, child: Column( mainAxisAlignment: @@ -1645,7 +1646,7 @@ class _FamilyBackgroundScreenState extends State { const EdgeInsets .symmetric( horizontal: 12, - vertical: 8), + vertical: 0), width: screenWidth, child: Column( mainAxisAlignment: diff --git a/lib/screens/profile/components/learning_and_development_screen.dart b/lib/screens/profile/components/learning_and_development_screen.dart index 0e70a88..edb1f31 100644 --- a/lib/screens/profile/components/learning_and_development_screen.dart +++ b/lib/screens/profile/components/learning_and_development_screen.dart @@ -8,10 +8,8 @@ import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_svg/svg.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:intl/intl.dart'; -import 'package:unit2/bloc/profile/education/education_bloc.dart'; -import 'package:unit2/bloc/profile/eligibility/eligibility_bloc.dart'; + import 'package:unit2/bloc/profile/profile_bloc.dart'; -import 'package:unit2/bloc/profile/workHistory/workHistory_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/screens/profile/components/learning_development/edit_modal.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; diff --git a/lib/screens/profile/components/learning_development/add_modal.dart b/lib/screens/profile/components/learning_development/add_modal.dart index 9eed392..697d651 100644 --- a/lib/screens/profile/components/learning_development/add_modal.dart +++ b/lib/screens/profile/components/learning_development/add_modal.dart @@ -29,6 +29,7 @@ import '../../../../theme-data.dart/form-style.dart'; import '../../../../utils/formatters.dart'; import '../../../../utils/global.dart'; import '../../../../utils/location_utilities.dart'; +import '../../../../widgets/error_state.dart'; import '../../shared/add_for_empty_search.dart'; class AddLearningAndDevelopmentScreen extends StatefulWidget { @@ -141,6 +142,21 @@ class _AddLearningAndDevelopmentScreenState SizedBox( child: show ? SearchableDropdownFormField.paginated( + errorWidget: (value) { + return SomethingWentWrong( + message: value, + onpressed: () { + context + .read< + LearningDevelopmentBloc>() + .add( + GetLearningDevelopments( + profileId: widget + .profileId, + token: + widget.token)); + }); + }, noRecordTex: SizedBox( width: double.infinity, height: 300, @@ -189,13 +205,21 @@ class _AddLearningAndDevelopmentScreenState paginatedRequest: (int page, String? searchKey) async { List - paginatedList = - await LearningDevelopmentServices - .instance - .getConductedTrainings( - page: page, - key: searchKey ??= ""); - return paginatedList.map((e) { + paginatedList=[]; + try { + paginatedList = + await LearningDevelopmentServices + .instance + .getConductedTrainings( + page: page, + key: searchKey ??= ""); + } catch (e) { + context + .read() + .add(CallErrorState( + message: e.toString())); + } + return paginatedList.map((e) { return SearchableDropdownMenuItem( value: e, onTap: () {}, @@ -442,70 +466,12 @@ class _AddLearningAndDevelopmentScreenState width: screenWidth, child: StatefulBuilder( builder: (context, setState) { - return StatefulBuilder( - builder: (context,setState) { - return Row( - children: [ - //// FROM DATE - Flexible( - flex: 1, - child: DateTimePicker( - validator: - FormBuilderValidators - .required( - errorText: - "This field is required"), - use24HourFormat: - false, - icon: const Icon( - Icons.date_range), - controller: - fromDateController, - firstDate: - DateTime(1990), - lastDate: - DateTime(2100), - selectableDayPredicate: - (date) { - if (to != null && - to!.microsecondsSinceEpoch <= - date.microsecondsSinceEpoch) { - return false; - } - return true; - }, - onChanged: (value) { - setState(() { - from = DateTime - .parse(value); - }); - }, - initialDate: to == - null - ? DateTime.now() - : to!.subtract( - const Duration( - days: 1)), - timeHintText: - "Date of Examination/Conferment", - decoration: - normalTextFieldStyle( - "From *", - "From *") - .copyWith( - prefixIcon: - const Icon( - Icons.date_range, - color: - Colors.black87, - )), - initialValue: null, - )), - const SizedBox( - width: 12, - ), - //// TO DATE - Flexible( + return StatefulBuilder(builder: + (context, setState) { + return Row( + children: [ + //// FROM DATE + Flexible( flex: 1, child: DateTimePicker( validator: @@ -513,14 +479,21 @@ class _AddLearningAndDevelopmentScreenState .required( errorText: "This field is required"), + use24HourFormat: + false, + icon: const Icon( + Icons + .date_range), controller: - toDateController, + fromDateController, firstDate: DateTime(1990), + lastDate: + DateTime(2100), selectableDayPredicate: (date) { - if (from != null && - from!.microsecondsSinceEpoch >= + if (to != null && + to!.microsecondsSinceEpoch <= date.microsecondsSinceEpoch) { return false; } @@ -528,37 +501,89 @@ class _AddLearningAndDevelopmentScreenState }, onChanged: (value) { setState(() { - to = DateTime.parse( - value); + from = DateTime + .parse( + value); }); }, - initialDate: from == + initialDate: to == null ? DateTime.now() - : from!.add( + : to!.subtract( const Duration( - days: 1)), - lastDate: - DateTime(2100), - decoration: - normalTextFieldStyle( - "To *", - "To *") - .copyWith( - prefixIcon: - const Icon( - Icons.date_range, - color: - Colors.black87, - ), - ), + days: + 1)), + timeHintText: + "Date of Examination/Conferment", + decoration: normalTextFieldStyle( + "From *", + "From *") + .copyWith( + prefixIcon: + const Icon( + Icons.date_range, + color: Colors + .black87, + )), initialValue: null, + )), + const SizedBox( + width: 12, + ), + //// TO DATE + Flexible( + flex: 1, + child: DateTimePicker( + validator: + FormBuilderValidators + .required( + errorText: + "This field is required"), + controller: + toDateController, + firstDate: + DateTime(1990), + selectableDayPredicate: + (date) { + if (from != null && + from!.microsecondsSinceEpoch >= + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, + onChanged: (value) { + setState(() { + to = DateTime + .parse(value); + }); + }, + initialDate: from == + null + ? DateTime.now() + : from!.add( + const Duration( + days: 1)), + lastDate: + DateTime(2100), + decoration: + normalTextFieldStyle( + "To *", + "To *") + .copyWith( + prefixIcon: + const Icon( + Icons.date_range, + color: Colors + .black87, + ), ), + initialValue: null, ), - ], - ); - } - ); + ), + ], + ); + }); }), ), const SizedBox( diff --git a/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart b/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart index 11a7ab9..26eb9af 100644 --- a/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart +++ b/lib/screens/profile/components/other_information/non_academic_recognition_screen.dart @@ -29,6 +29,7 @@ class NonAcademicRecognitionScreen extends StatelessWidget { int? profileId; String? token; return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: context.watch().state is AddNonAcademeRecognitionState diff --git a/lib/screens/profile/components/other_information/org_membership/add_modal.dart b/lib/screens/profile/components/other_information/org_membership/add_modal.dart index a8cfd0d..144164f 100644 --- a/lib/screens/profile/components/other_information/org_membership/add_modal.dart +++ b/lib/screens/profile/components/other_information/org_membership/add_modal.dart @@ -1,3 +1,4 @@ +import 'package:auto_size_text/auto_size_text.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; @@ -18,8 +19,11 @@ import '../../../../../utils/formatters.dart'; class AddOrgMemberShipScreen extends StatefulWidget { final int profileId; final String token; + final List agencies; + final List agencyCategories; + final Bloc bloc; const AddOrgMemberShipScreen( - {super.key, required this.profileId, required this.token}); + {super.key, required this.profileId, required this.token, required this.agencies, required this.agencyCategories, required this.bloc}); @override State createState() => _AddOrgMemberShipScreenState(); @@ -45,20 +49,13 @@ class _AddOrgMemberShipScreenState extends State { @override Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, state) { - if (state is AddOrgMembershipState) { - return Padding( - padding: const EdgeInsets.all(24), - child: Card( - - child: SingleChildScrollView( - child: FormBuilder( + return FormBuilder( key: _formKey, child: Container( padding: const EdgeInsets.symmetric(vertical:24, horizontal: 24), child: Column( + mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -71,14 +68,17 @@ class _AddOrgMemberShipScreenState extends State { inputFormatters: [UpperCaseTextFormatter()], itemHeight: 100, - suggestions: state.agencies + suggestions: widget.agencies .map((Agency agency) => SearchFieldListItem(agency.name!, item: agency, child: ListTile( - title: Text( - agency.name!.toUpperCase(), - overflow: TextOverflow.visible, + title: AutoSizeText( + + agency.name!.toUpperCase(), + minFontSize: 12, + + ), subtitle: Text( agency.privateEntity == true @@ -179,7 +179,7 @@ class _AddOrgMemberShipScreenState extends State { name: addAgencyController.text.toUpperCase(), category: null, privateEntity: null); - state.agencies.insert(0, + widget.agencies.insert(0, newAgency!); addAgencyController.clear(); Navigator.pop(context); @@ -205,7 +205,7 @@ class _AddOrgMemberShipScreenState extends State { ? SearchField( focusNode: agencyCategoryFocusNode, itemHeight: 70, - suggestions: state.agencyCategories + suggestions: widget.agencyCategories .map((Category category) => SearchFieldListItem( category.name!, @@ -322,10 +322,8 @@ class _AddOrgMemberShipScreenState extends State { category: selectedCategory, privateEntity: isPrivate); } - final progress = ProgressHUD.of(context); - progress!.showWithText("Loading..."); - context - .read() + Navigator.of(context).pop(); + widget.bloc .add(AddOrgMembership( agency: newAgency!, profileId: widget.profileId, @@ -339,13 +337,6 @@ class _AddOrgMemberShipScreenState extends State { child: const Text(submit)), ) ]), - )), - ), - ), - ); - } - return Container(); - }, - ); + )); } } diff --git a/lib/screens/profile/components/other_information/org_membership_screen.dart b/lib/screens/profile/components/other_information/org_membership_screen.dart index 133f615..c4f1663 100644 --- a/lib/screens/profile/components/other_information/org_membership_screen.dart +++ b/lib/screens/profile/components/other_information/org_membership_screen.dart @@ -3,10 +3,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; -import 'package:fluttericon/font_awesome_icons.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; -import 'package:unit2/model/profile/other_information/organization_memberships.dart'; +import 'package:unit2/model/utils/agency.dart'; import 'package:unit2/screens/profile/components/other_information/org_membership/add_modal.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; @@ -16,6 +15,7 @@ import 'package:unit2/widgets/Leadings/close_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; import '../../../../bloc/profile/other_information/org_membership/organization_membership_bloc.dart'; +import '../../../../model/utils/category.dart'; import '../../../../utils/alerts.dart'; import '../../../../utils/global.dart'; @@ -25,7 +25,10 @@ class OrgMembershipsScreen extends StatelessWidget { @override Widget build(BuildContext context) { String? token; - int profileId; + int? profileId; + List agencies = []; + List agencyCategory = []; + final orgBloc = BlocProvider.of(context); return Scaffold( appBar: AppBar( title: context.watch().state @@ -38,9 +41,12 @@ class OrgMembershipsScreen extends StatelessWidget { is OrganizationMembershipLoaded ? [ AddLeading(onPressed: () { - context - .read() - .add(ShowAddOrgMembershipForm()); + showDialog(context: (context),builder: (BuildContext context){ + return AlertDialog( + title: const Text("Add Organization Membership"), + content: AddOrgMemberShipScreen(profileId: profileId!, token: token!,agencies: agencies,agencyCategories: agencyCategory,bloc: orgBloc,), + ); + }); }) ] : context.watch().state @@ -124,6 +130,8 @@ class OrgMembershipsScreen extends StatelessWidget { }, builder: (context, state) { if (state is OrganizationMembershipLoaded) { + agencies = state.agencies; + agencyCategory = state.agencyCategory; if (state.orgMemberships.isNotEmpty) { return ListView.builder( itemCount: state.orgMemberships.length, @@ -190,7 +198,7 @@ class OrgMembershipsScreen extends StatelessWidget { OrganizationMembershipBloc>() .add(DeleteOrgMemberShip( profileId: - profileId, + profileId!, token: token!, org: state .orgMemberships[ @@ -225,13 +233,7 @@ class OrgMembershipsScreen extends StatelessWidget { "You don't have any Orgazational Membership added. Please click + to add"); } } - if (state is AddOrgMembershipState) { - return - AddOrgMemberShipScreen( - profileId: profileId, - token: token!, - ); - } + if (state is OrganizationMembershipErrorState) { return SomethingWentWrong( message: state.message, diff --git a/lib/screens/profile/components/reference/add_modal.dart b/lib/screens/profile/components/reference/add_modal.dart index cfd8ca3..5140b44 100644 --- a/lib/screens/profile/components/reference/add_modal.dart +++ b/lib/screens/profile/components/reference/add_modal.dart @@ -123,9 +123,12 @@ class _AddReferenceScreenState extends State { Flexible( flex: 1, child: FormBuilderTextField( + keyboardType: TextInputType.number, + inputFormatters: [mobileFormatter], name: "mobile", - decoration: normalTextFieldStyle( - "Tel./Mobile *", "Tel./Mobile"), + decoration: normalTextFieldStyle( + "Mobile *", + "+63 (9xx) xxx - xxxx"), validator: FormBuilderValidators.required( errorText: "This field is required"), ), diff --git a/lib/screens/profile/components/reference/edit_modal.dart b/lib/screens/profile/components/reference/edit_modal.dart index 479bed8..b233d94 100644 --- a/lib/screens/profile/components/reference/edit_modal.dart +++ b/lib/screens/profile/components/reference/edit_modal.dart @@ -88,7 +88,7 @@ class _EditReferenceScreenState extends State { Flexible( flex: 1, child: FormBuilderTextField( - inputFormatters: [UpperCaseTextFormatter()], + inputFormatters: [UpperCaseTextFormatter(),mobileFormatter], initialValue: state.ref.lastName, decoration: normalTextFieldStyle( "Last name *", "Last name *"), diff --git a/lib/screens/profile/components/references_screen.dart b/lib/screens/profile/components/references_screen.dart index a9731fe..ab349c4 100644 --- a/lib/screens/profile/components/references_screen.dart +++ b/lib/screens/profile/components/references_screen.dart @@ -23,7 +23,7 @@ class ReferencesScreen extends StatelessWidget { int? profileId; String? token; return Scaffold( - resizeToAvoidBottomInset: true, + resizeToAvoidBottomInset: false, appBar: AppBar( title: context.watch().state is AddReferenceState ? const Text("Add Personal Reference") diff --git a/lib/screens/profile/components/voluntary_works_screen.dart b/lib/screens/profile/components/voluntary_works_screen.dart index c52dece..5e92815 100644 --- a/lib/screens/profile/components/voluntary_works_screen.dart +++ b/lib/screens/profile/components/voluntary_works_screen.dart @@ -3,7 +3,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; -import 'package:fluttericon/font_awesome_icons.dart'; import 'package:intl/intl.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; @@ -29,6 +28,7 @@ class VolunataryWorkScreen extends StatelessWidget { int? profileId; DateFormat dteFormat2 = DateFormat.yMMMMd('en_US'); return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title:context.watch().state is AddVoluntaryWorkState? const FittedBox(child: Text("Add $voluntaryScreenTitle"),) :context.watch().state is EditVoluntaryWorks? const FittedBox(child: Text("Edit $voluntaryScreenTitle"),): const FittedBox(child: Text(voluntaryScreenTitle)), backgroundColor: primary, @@ -302,7 +302,7 @@ class VolunataryWorkScreen extends StatelessWidget { } if (state is VoluntaryWorkErrorState) { return SomethingWentWrong( - message: state.toString(), onpressed: () { + message: state.message, onpressed: () { context.read().add(GetVoluntarWorks(profileId: profileId!, token: token!)); }); } diff --git a/lib/screens/profile/components/work_history/add_modal.dart b/lib/screens/profile/components/work_history/add_modal.dart index 6fd2f33..38ca36a 100644 --- a/lib/screens/profile/components/work_history/add_modal.dart +++ b/lib/screens/profile/components/work_history/add_modal.dart @@ -90,7 +90,7 @@ class _AddWorkHistoryScreenState extends State { StatefulBuilder(builder: (context, setState) { return SearchField( inputFormatters: [UpperCaseTextFormatter()], - itemHeight: 100, + itemHeight: 70, suggestionsDecoration: box1(), suggestions: state.agencyPositions .map((PositionTitle position) => SearchFieldListItem( @@ -478,6 +478,7 @@ class _AddWorkHistoryScreenState extends State { ), ////MONTHLY SALARY FormBuilderTextField( + keyboardType: TextInputType.number, onChanged: (value) { setState(() { salary = value; diff --git a/lib/screens/profile/components/work_history_screen.dart b/lib/screens/profile/components/work_history_screen.dart index b234467..6035f79 100644 --- a/lib/screens/profile/components/work_history_screen.dart +++ b/lib/screens/profile/components/work_history_screen.dart @@ -9,9 +9,6 @@ import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_svg/svg.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:intl/intl.dart'; -import 'package:unit2/bloc/profile/education/education_bloc.dart'; -import 'package:unit2/bloc/profile/eligibility/eligibility_bloc.dart'; -import 'package:unit2/bloc/profile/learningDevelopment/learning_development_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/screens/profile/components/work_history/add_modal.dart'; @@ -47,6 +44,7 @@ class WorkHistoryScreen extends StatelessWidget { AttachmentCategory? selectedAttachmentCategory; List attachmentCategories = []; return Scaffold( + resizeToAvoidBottomInset: false, appBar: AppBar( title: context.watch().state is AddWorkHistoryState ? const FittedBox(child: Text("Add Work History")) diff --git a/lib/screens/profile/profile.dart b/lib/screens/profile/profile.dart index b601337..ec1a721 100644 --- a/lib/screens/profile/profile.dart +++ b/lib/screens/profile/profile.dart @@ -70,14 +70,19 @@ class ProfileInfo extends StatelessWidget { profileId = state.userData!.user!.login!.user!.profileId; token = state.userData!.user!.login!.token!; profile = state.userData!.employeeInfo!.profile!; - + return BlocConsumer( - listener: (context, state,){ + listener: ( + context, + state, + ) { if (state is ProfileLoading) { final progress = ProgressHUD.of(context); progress!.showWithText("Please wait..."); } - if (state is ProfileLoaded || state is ProfileErrorState || state is BasicInformationEditingState) { + if (state is ProfileLoaded || + state is ProfileErrorState || + state is BasicInformationEditingState) { final progress = ProgressHUD.of(context); progress?.dismiss(); } @@ -110,10 +115,17 @@ class ProfileInfo extends StatelessWidget { ), items: [ subMenu(Icons.person, "Primary", () { - Navigator.push(context, MaterialPageRoute( builder: (BuildContext context) { - return BlocProvider.value(value: ProfileBloc()..add(GetPrimaryBasicInfo(primaryBasicInformation: profile)),child: PrimaryInfo(token: token!,profileId: profileId!,),); + return BlocProvider.value( + value: ProfileBloc() + ..add(GetPrimaryBasicInfo( + primaryBasicInformation: profile)), + child: PrimaryInfo( + token: token!, + profileId: profileId!, + ), + ); })); }), subMenu(Icons.home, "Addresses", () { @@ -159,9 +171,8 @@ class ProfileInfo extends StatelessWidget { })); }), subMenu(Icons.flag, "Citizenships", () { - Navigator.push(context, MaterialPageRoute( + Navigator.push(context, MaterialPageRoute( builder: (BuildContext context) { - return BlocProvider( create: (context) => CitizenshipBloc() ..add(GetCitizenship( @@ -169,7 +180,8 @@ class ProfileInfo extends StatelessWidget { .profileInformation .basicInfo .citizenships)), - child: CitizenShipScreen(profileId: profileId!,token: token!), + child: CitizenShipScreen( + profileId: profileId!, token: token!), ); })); }), @@ -368,13 +380,15 @@ class ProfileInfo extends StatelessWidget { ); } if (state is ProfileLoading) { - return const LoadingScreen(); } if (state is ProfileErrorState) { - return SomethingWentWrong(message: state.mesage, onpressed: (){ BlocProvider.of( - context) - .add(LoadProfile(token:token!, userID: profileId!));}); + return SomethingWentWrong( + message: state.mesage, + onpressed: () { + BlocProvider.of(context).add( + LoadProfile(token: token!, userID: profileId!)); + }); } return Container(); diff --git a/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart b/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart index 4d90a08..c11a424 100644 --- a/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart +++ b/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart @@ -10,9 +10,14 @@ import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/theme-data.dart/form-style.dart'; import '../../../../../bloc/rbac/rbac_operations/agency/agency_bloc.dart'; import '../../../../../bloc/role/pass_check/est_point_person/assign_area/assign_area_agency_bloc.dart'; +import '../../../../../bloc/role/pass_check/est_point_person/est_point_person_assignable/est_point_person_assinable_role_bloc.dart'; +import '../../../../../bloc/role/pass_check/est_point_person/est_point_person_role_assignment/est_role_assignment_bloc.dart'; import '../../../../../bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_bloc.dart'; +import '../../../../../sevices/roles/est_point_person/est_point_person_role_assignment_services.dart'; import '../../../../superadmin/agency.dart/agency_screen.dart'; import '../../../roles/establishment_point_person/est_point_person_agecies.dart'; +import '../../../roles/establishment_point_person/est_point_person_role_member_screen.dart'; +import '../../../roles/establishment_point_person/est_point_person_role_under_screen.dart'; import '../../../roles/establishment_point_person/est_point_person_station.dart'; import './shared_card_label.dart'; @@ -204,6 +209,23 @@ class _DashBoardState extends State { ); })); } + if (e.object.name!.toLowerCase() == "role member") { + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) { + return BlocProvider( + create: (context) => EstRoleAssignmentBloc()..add(GetEstPointPersonRolesUnder(userId: widget.userId)), + child: EstPointPersonRoleAssignmentScreen(id: widget.userId,), + ); + })); + } + if (e.object.name!.toLowerCase() == 'assignable role') { + + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) { + return BlocProvider( + create: (context) => EstPointPersonAssinableRoleBloc()..add(const GetEstPointPersonAssignableRoles(roleId: 16)), + child: EstPointPersonRoleUnderScreen(id: widget.userId,), + ); + })); + } ////Station if (e.object.name == 'Station') { showDialog( diff --git a/lib/screens/unit2/homepage.dart/components/dashboard/superadmin_expanded_menu.dart b/lib/screens/unit2/homepage.dart/components/dashboard/superadmin_expanded_menu.dart index d9d7fb7..4329d67 100644 --- a/lib/screens/unit2/homepage.dart/components/dashboard/superadmin_expanded_menu.dart +++ b/lib/screens/unit2/homepage.dart/components/dashboard/superadmin_expanded_menu.dart @@ -75,9 +75,7 @@ class SuperAdminMenu extends StatelessWidget { ? CardLabel( icon: iconGenerator(name: object.object.name!), title: - object.object.name!.toLowerCase() == 'assignable role' - ? "Role Assignment" - : object.object.name!, + object.object.name!, ontap: () { if (object.object.name == 'Role') { Navigator.push(context, MaterialPageRoute( diff --git a/lib/screens/unit2/roles/establishment_point_person/est_point_person_role_member_screen.dart b/lib/screens/unit2/roles/establishment_point_person/est_point_person_role_member_screen.dart new file mode 100644 index 0000000..185c62d --- /dev/null +++ b/lib/screens/unit2/roles/establishment_point_person/est_point_person_role_member_screen.dart @@ -0,0 +1,307 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/widgets/framework.dart'; +import 'package:flutter/src/widgets/placeholder.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:group_list_view/group_list_view.dart'; +import 'package:multi_dropdown/models/value_item.dart'; +import 'package:multi_dropdown/multiselect_dropdown.dart'; +import 'package:unit2/bloc/role/pass_check/est_point_person/est_point_person_role_assignment/est_role_assignment_bloc.dart'; +import 'package:unit2/bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_bloc.dart'; +import 'package:unit2/bloc/role_assignment/role_assignment_bloc.dart'; +import 'package:unit2/theme-data.dart/form-style.dart'; +import 'package:unit2/widgets/Leadings/add_leading.dart'; +import 'package:unit2/widgets/empty_data.dart'; +import 'package:unit2/widgets/error_state.dart'; + +import '../../../../model/rbac/assigned_role.dart'; +import '../../../../model/rbac/rbac.dart'; +import '../../../../theme-data.dart/btn-style.dart'; +import '../../../../theme-data.dart/colors.dart'; +import '../../../../utils/alerts.dart'; +import '../../../../utils/global_context.dart'; + +class EstPointPersonRoleAssignmentScreen extends StatelessWidget { + final int id; + const EstPointPersonRoleAssignmentScreen({super.key, required this.id}); + + @override + Widget build(BuildContext context) { + Map> assignedRoles = {}; + List roles = []; + List valueItemAssignableRoles = []; + List selectedValueItemAssignableRoles = []; + List assignabledRoles = []; + List users = []; + List userIds = []; + int? selectedUserId; + final formKey = GlobalKey(); + final parent = context; + return Scaffold( + appBar: AppBar( + title: const Text("Role Members"), + backgroundColor: primary, + actions: [ + AddLeading(onPressed: () { + for (var role in roles) { + String fullname = + "${role.user!.firstName} ${role.user!.lastName}"; + if (!userIds.contains(role.user!.id)) { + userIds.add(role.user!.id); + users.add(Content(id: role.user!.id, name: fullname)); + } + } + showDialog( + context: NavigationService.navigatorKey.currentState!.context, + builder: (context) { + valueItemAssignableRoles = assignabledRoles.map((e) { + return ValueItem(label: e.name!, value: e.id.toString()); + }).toList(); + return AlertDialog( + title: const Text("Assign Role to User"), + content: FormBuilder( + key: formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + FormBuilderDropdown( + decoration: normalTextFieldStyle( + "Select Person", "Select Person"), + name: 'users', + onChanged: (value) { + selectedUserId = value!.id; + }, + items: users.map((e) { + return DropdownMenuItem( + value: e, + child: Text(e.name), + ); + }).toList()), + const SizedBox( + height: 12, + ), + MultiSelectDropDown( + onOptionSelected: + (List selectedOptions) { + selectedValueItemAssignableRoles = + selectedOptions; + }, + borderColor: Colors.grey, + borderWidth: 1, + borderRadius: 5, + hint: "Select Roles", + padding: const EdgeInsets.all(8), + options: valueItemAssignableRoles, + selectionType: SelectionType.multi, + chipConfig: + const ChipConfig(wrapType: WrapType.wrap), + dropdownHeight: 300, + optionTextStyle: const TextStyle(fontSize: 16), + selectedOptionIcon: + const Icon(Icons.check_circle), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState! + .saveAndValidate()) { + List rolesId = []; + for (var role in assignabledRoles) { + selectedValueItemAssignableRoles + .forEach((element) { + if (element.value == + role.id.toString()) { + rolesId.add(role.id!); + } + }); + } + parent + .read() + .add(EstPointPersonAssignRole( + rolesId: rolesId, + assingerId: id, + userId: selectedUserId!)); + Navigator.pop(context); + } + }, + child: const Text("Add"))), + ], + )), + ); + }); + }) + ], + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocConsumer( + builder: (context, state) { + if (state is EstPointPersonRolesUnderLoadedState) { + roles = state.assignedRoles; + assignabledRoles = state.assignableRole; + assignedRoles = {}; + if (state.assignedRoles.isNotEmpty) { + for (var assignedRole in state.assignedRoles) { + String? fullName = + "${assignedRole.user!.firstName} ${assignedRole.user!.lastName}"; + if (!assignedRoles.keys.contains(fullName)) { + assignedRoles.addAll({fullName: []}); + assignedRoles[fullName]!.add(Content( + id: assignedRole.id!, name: assignedRole.role!.name)); + } else { + assignedRoles[fullName]!.add(Content( + id: assignedRole.id!, + name: assignedRole.role!.name)); + } + } + } + if (state.assignedRoles.isNotEmpty) { + return GroupListView( + sectionsCount: assignedRoles.keys.toList().length, + countOfItemInSection: (int section) { + return assignedRoles.values.toList()[section].length; + }, + separatorBuilder: (context, index) { + return const Divider(); + }, + itemBuilder: (BuildContext context, IndexPath index) { + return ListTile( + trailing: IconButton( + color: Colors.grey.shade600, + icon: const Icon(Icons.delete), + onPressed: () { + confirmAlert(context, () { + context.read().add( + EstPointPersonDeleteAssignRole( + roleId: assignedRoles.values + .toList()[index.section][index.index] + .id)); + }, "Delete?", "Confirm Delete?"); + }, + ), + title: Row( + children: [ + CircleAvatar( + child: Text("${index.index + 1}", + style: Theme.of(context) + .textTheme + .labelLarge! + .copyWith(color: Colors.white))), + const SizedBox( + width: 20, + ), + Expanded( + child: Text( + assignedRoles.values + .toList()[index.section][index.index] + .name + .toUpperCase(), + style: Theme.of(context) + .textTheme + .labelLarge! + .copyWith( + fontWeight: FontWeight.w600, + color: primary), + ), + ), + ], + ), + ); + }, + groupHeaderBuilder: (BuildContext context, int section) { + return ListTile( + tileColor: second, + title: Text( + assignedRoles.keys.toList()[section].toUpperCase(), + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(color: Colors.white), + ), + ); + }, + ); + } else { + return const EmptyData(message: "Empty"); + } + } + if (state is EstPointPersonRolesUnderErrorState) { + return SomethingWentWrong( + message: "something went wrong. Please try again!", + onpressed: () {}); + } + return Container(); + }, listener: (context, state) { + if (state is EstPointPersonRoleLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + ////Added State + if (state is EstPointPersonRoleUnderAddedState) { + if (state.response['success']) { + successAlert( + context, "Adding Successfull!", state.response['message'], + () { + Navigator.of(context).pop(); + context + .read() + .add(GetEstPointPersonRolesUnder(userId: id)); + }); + } else { + errorAlert(context, "Adding Failed", state.response['message'], + () { + Navigator.of(context).pop(); + context + .read() + .add(GetEstPointPersonRolesUnder(userId: id)); + }); + } + } + ////Deleted State + if (state is EstPointPersonDeletedState) { + if (state.success) { + successAlert( + context, "Delete Successfull!", "Role Deleted Successfully", + () { + Navigator.of(context).pop(); + context + .read() + .add(GetEstPointPersonRolesUnder(userId: id)); + }); + } else { + errorAlert(context, "Delete Failed", "Role Delete Failed", () { + Navigator.of(context).pop(); + context + .read() + .add(GetEstPointPersonRolesUnder(userId: id)); + }); + } + } + if (state is EstPointPersonRolesUnderLoadedState || + state is EstPointPersonRoleUnderAddedState || + state is EstPointPersonRoleLoadingState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + }), + ), + ); + } +} + +class Content { + final int id; + final String name; + const Content({required this.id, required this.name}); +} diff --git a/lib/screens/unit2/roles/establishment_point_person/est_point_person_role_under_screen.dart b/lib/screens/unit2/roles/establishment_point_person/est_point_person_role_under_screen.dart new file mode 100644 index 0000000..9e5ded5 --- /dev/null +++ b/lib/screens/unit2/roles/establishment_point_person/est_point_person_role_under_screen.dart @@ -0,0 +1,264 @@ +import 'package:app_popup_menu/app_popup_menu.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; +import 'package:multi_dropdown/models/value_item.dart'; +import 'package:multi_dropdown/multiselect_dropdown.dart'; +import 'package:unit2/bloc/rbac/rbac_operations/role/role_bloc.dart'; +import 'package:unit2/screens/superadmin/role/shared_pop_up_menu.dart'; +import 'package:unit2/widgets/Leadings/add_leading.dart'; +import 'package:unit2/widgets/error_state.dart'; +import '../../../../bloc/role/pass_check/est_point_person/est_point_person_assignable/est_point_person_assinable_role_bloc.dart'; +import '../../../../model/rbac/rbac.dart'; +import '../../../../theme-data.dart/box_shadow.dart'; +import '../../../../theme-data.dart/btn-style.dart'; +import '../../../../theme-data.dart/colors.dart'; +import '../../../../theme-data.dart/form-style.dart'; +import '../../../../utils/alerts.dart'; +import '../../../../utils/global.dart'; +import '../../../../widgets/empty_data.dart'; + +class EstPointPersonRoleUnderScreen extends StatelessWidget { + final int id; + const EstPointPersonRoleUnderScreen({super.key, required this.id}); + + @override + Widget build(BuildContext context) { + final formKey = GlobalKey(); + List roles = []; + List valueItemRoles = []; + List selectedValueItemRoles = []; + RBAC? mainRole; + return Scaffold( + appBar: AppBar( + centerTitle: true, + backgroundColor: primary, + title: const Text("Establishment Point Person Role Under Screen"), + actions: [ + AddLeading(onPressed: () { + valueItemRoles = roles.map((e) { + return ValueItem(label: e.name!, value: e.name); + }).toList(); + BuildContext parent = context; + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text("Add New Role"), + content: FormBuilder( + key: formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + FormBuilderDropdown( + decoration: normalTextFieldStyle("", ""), + initialValue: mainRole!.name, + name: "main-role",items:[] ,), + const SizedBox(height: 12,), + MultiSelectDropDown( + onOptionSelected: + (List selectedOptions) { + selectedValueItemRoles = selectedOptions; + }, + borderColor: Colors.grey, + borderWidth: 1, + borderRadius: 5, + hint: "Roles Under", + padding: const EdgeInsets.all(8), + options: valueItemRoles, + selectionType: SelectionType.multi, + chipConfig: + const ChipConfig(wrapType: WrapType.wrap), + dropdownHeight: 300, + optionTextStyle: const TextStyle(fontSize: 16), + selectedOptionIcon: + const Icon(Icons.check_circle), + ), + const SizedBox( + height: 8, + ), + + const SizedBox( + height: 12, + ), + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + onPressed: () { + if (formKey.currentState! + .saveAndValidate()) { + List rolesId = []; + for (var role in roles) { + selectedValueItemRoles + .forEach((element) { + if (element.label.toLowerCase() == + role.name?.toLowerCase()) { + rolesId.add(role.id!); + } + }); + } + Navigator.pop(context); + parent.read().add( + AddEstPointPersonAssignableRoles( + mainRoleId: mainRole!.id!, + rolesUnder: rolesId)); + } + }, + child: const Text("Add"))), + ], + ), + ), + ); + }); + }) + ], + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocConsumer( + listener: (context, state) { + if (state is EstPointPersonAssignableRoleLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is EstPointPersonAssignableRoleLoaded || + state is EstPointPersonAssginableRoleAssignState || + state is EstPointPersonAssignableErrorState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + ////Added State + if (state is EstPointPersonAssginableRoleAssignState) { + if (state.responseStatus['success']) { + successAlert( + context, "Adding Successfull!", state.responseStatus['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetEstPointPersonAssignableRoles(roleId:16 )); + }); + } else { + errorAlert(context, "Adding Failed", state.responseStatus['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetEstPointPersonAssignableRoles(roleId:16 )); + }); + } + } + + ////Deleted State + if (state is EstPointPersonAssignableDeletedState) { + if (state.success) { + successAlert( + context, "Delete Successfull!", "Role Deleted Successfully", + () { + Navigator.of(context).pop(); + context.read().add(GetEstPointPersonAssignableRoles(roleId:16 )); + }); + } else { + errorAlert(context, "Delete Failed", "Role Delete Failed", () { + Navigator.of(context).pop(); + context.read().add(GetEstPointPersonAssignableRoles(roleId:16 )); + }); + } + } + + }, + builder: (context, state) { + final parent = context; + + if (state is EstPointPersonAssignableRoleLoaded) { + roles = state.roles; + mainRole = state.mainRole; + if (state.rolesUnder.isNotEmpty) { + return ListView.builder( + padding: + const EdgeInsets.symmetric(vertical: 8, horizontal: 10), + itemCount: state.rolesUnder.length, + itemBuilder: (BuildContext context, int index) { + return Column( + children: [ + Container( + width: screenWidth, + decoration: box1(), + padding: const EdgeInsets.symmetric( + horizontal: 12, vertical: 8), + child: Row( + children: [ + Expanded( + child: Row( + children: [ + CircleAvatar(child: Text('${index+1}'),), + const SizedBox(width: 12,), + Flexible( + child: Text(state.rolesUnder[index].name!, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: FontWeight.w500, + color: primary)), + ), + ], + )), + AppPopupMenu( + offset: const Offset(-10, -10), + elevation: 3, + onSelected: (value) { + + if (value == 1) { + confirmAlert(context, () { + context.read().add( + DeleteEstPointPersonAssignableRoles( + roleId: + state.rolesUnder[index].id!)); + }, "Delete?", "Confirm Delete?"); + } + }, + menuItems: [ + + popMenuItem( + text: "Remove", + value: 1, + icon: Icons.delete), + ], + icon: const Icon( + Icons.more_vert, + color: Colors.grey, + ), + tooltip: "Options", + ), + ], + ), + ), + const SizedBox( + height: 5, + ) + ], + ); + }); + } else { + return const EmptyData( + message: "No Role available. Please click + to add."); + } + } + if (state is EstPointPersonAssignableErrorState) { + return SomethingWentWrong( + message: state.message, onpressed: () { + context.read().add(GetRoles()); + }); + } + return Container(); + }, + ), + ), + ); + } +} diff --git a/lib/sevices/profile/education_services.dart b/lib/sevices/profile/education_services.dart index 05a86f5..9ed67ed 100644 --- a/lib/sevices/profile/education_services.dart +++ b/lib/sevices/profile/education_services.dart @@ -68,7 +68,7 @@ class EducationService { try { http.Response response = await Request.instance .postRequest(path: path, param: {}, body: body, headers: headers); - if (response.statusCode == 201) { + if (response.statusCode == 2011) { Map data = jsonDecode(response.body); statusResponse = data; } else { @@ -150,7 +150,7 @@ class EducationService { http.Response response = await Request.instance.deleteRequest( path: path, headers: headers, body: body, param: params); - if (response.statusCode == 200) { + if (response.statusCode == 2001) { Map data = jsonDecode(response.body); success = data['success']; } else { diff --git a/lib/sevices/profile/profile_other_info.dart b/lib/sevices/profile/profile_other_info.dart index 8945835..5561b1c 100644 --- a/lib/sevices/profile/profile_other_info.dart +++ b/lib/sevices/profile/profile_other_info.dart @@ -21,7 +21,7 @@ class ProfileOtherInfoServices{ try { http.Response response = await Request.instance .getRequest(path: path, headers: headers, param: {}); - if (response.statusCode == 200) { + if (response.statusCode == 20012) { Map data = jsonDecode(response.body); if (data['data'] != null) { data['data'].forEach((var e) { diff --git a/lib/sevices/roles/est_point_person/est_point_person_role_assignment_services.dart b/lib/sevices/roles/est_point_person/est_point_person_role_assignment_services.dart new file mode 100644 index 0000000..eed7064 --- /dev/null +++ b/lib/sevices/roles/est_point_person/est_point_person_role_assignment_services.dart @@ -0,0 +1,163 @@ +import 'dart:convert'; + +import 'package:unit2/model/rbac/assigned_role.dart'; +import 'package:unit2/model/rbac/rbac.dart'; +import 'package:unit2/utils/request.dart'; +import 'package:unit2/utils/urls.dart'; + +import 'package:http/http.dart' as http; + +import '../../../model/profile/basic_information/primary-information.dart'; +import '../../../model/rbac/role_under.dart'; + +class EstPointPersonRoleAssignment { + static final EstPointPersonRoleAssignment _instance = + EstPointPersonRoleAssignment(); + static EstPointPersonRoleAssignment get instance => _instance; + String xClientKey = "unitK3CQaXiWlPReDsBzmmwBZPd9Re1z"; + String xClientKeySecret = "unitcYqAN7GGalyz"; + + Future> getAssignedRoles( + {required int webuserId}) async { + List assignedRoles = []; + String path = Url.instance.getRoleAssignment(); + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + Map param = { +"created_by__id": webuserId.toString() + }; + try { + http.Response response = await Request.instance + .getRequest(param: param, path: path, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + for (var role in data['data']) { + AssignedRole newRole = AssignedRole.fromJson(role); + assignedRoles.add(newRole); + } + } + } + } catch (e) { + throw e.toString(); + } + return assignedRoles; + } + Future> getRolesUnder({required int roleId}) async { + List rolesUnder = []; + String path = Url.instance.getRolesUnder(); + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + Map param={"role_under_main__id":roleId.toString()}; + try { + http.Response response = await Request.instance + .getRequest(param: param, path: path, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + if (data['data'] != null) { + for (var roleUnd in data['data']) { + RolesUnder newRoleUnder = RolesUnder.fromJson(roleUnd); + rolesUnder.add(newRoleUnder); + } + } + } + } catch (e) { + throw e.toString(); + } + return rolesUnder; + } + + Future deleteAssignedRole({required int roleId}) async { + bool success = false; + String path = "${Url.instance.getRoleAssignment()}$roleId/"; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + try { + http.Response response = await Request.instance + .deleteRequest(path: path, headers: headers, body: {}, param: {}); + if (response.statusCode == 200) { + success = true; + } + } catch (e) { + throw e.toString(); + } + return success; + } + ////Add + Future> add({ + required int userId, + required int? assignerId, + required List roles, + }) async { + String path = Url.instance.getRoleAssignment(); + Map statusResponse = {}; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'X-Client-Key': xClientKey, + 'X-Client-Secret': xClientKeySecret + }; + Map body = { + "user_id": userId, + "roles": roles, + "assigner_user_id": assignerId, + }; + try { + http.Response response = await Request.instance + .postRequest(path: path, body: body, headers: headers, param: {}); + if (response.statusCode == 201) { + Map data = jsonDecode(response.body); + statusResponse = data; + } else { + Map data = jsonDecode(response.body); + String message = data['message']; + statusResponse.addAll({'message': message}); + statusResponse.addAll( + {'success': false}, + ); + } + } catch (e) { + throw e.toString(); + } + return statusResponse; + } + + Future searchUser( + {required int page, required String name, required String lastname}) async { + String path = Url.instance.searchUsers(); + Profile? user; + Map params = { + "profile__last_name__icontains": lastname, + "profile__first_name__icontains": name, + "page": page.toString(), + "is_paginated": "true", + }; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + + http.Response response = await Request.instance + .getRequest(param: params, path: path, headers: headers); + if (response.statusCode == 200) { + Map data = jsonDecode(response.body); + for(var profile in data['data']) { + int websuerId = profile['webuserid']; + Profile newUser = Profile.fromJson(profile['profile']); + newUser.webuserId = websuerId; + user= newUser; + break; + } + } + + return user; + } + +} diff --git a/lib/utils/attachment_services.dart b/lib/utils/attachment_services.dart index be51a10..a871c59 100644 --- a/lib/utils/attachment_services.dart +++ b/lib/utils/attachment_services.dart @@ -12,7 +12,7 @@ class AttachmentServices { Future> getCategories() async { List attachmentCategories = []; - String path = Url.instance.attachmentCategories(); + String path = Url.instance.attachmentCategories()+"11232"; Map headers = { 'Content-Type': 'application/json; charset=UTF-8', }; diff --git a/lib/utils/request.dart b/lib/utils/request.dart index 329b3c4..33c7a12 100644 --- a/lib/utils/request.dart +++ b/lib/utils/request.dart @@ -19,7 +19,7 @@ class Request { Map? param}) async { Response response; try { - response = await get(Uri.http(host, path!, param), headers: headers) + response = await get(Uri.https(host, path!, param), headers: headers) .timeout(Duration(seconds: requestTimeout)); } on TimeoutException catch (_) { Fluttertoast.showToast( @@ -61,7 +61,7 @@ class Request { Map? param}) async { Response response; try { - response = await post(Uri.http(host, path!, param), + response = await post(Uri.https(host, path!, param), headers: headers, body: jsonEncode(body)) .timeout(Duration(seconds: requestTimeout)); } on TimeoutException catch (_) { @@ -104,7 +104,7 @@ class Request { required Map? param}) async { Response response; try { - response =await put(Uri.http(host,path,param),headers: headers,body: jsonEncode(body)); + response =await put(Uri.https(host,path,param),headers: headers,body: jsonEncode(body)); } on TimeoutException catch (_) { Fluttertoast.showToast( msg: timeoutError, @@ -186,7 +186,7 @@ class Request { required Map? param}) async { Response response; try { - response = await delete(Uri.http(host, path, param), + response = await delete(Uri.https(host, path, param), headers: headers, body: jsonEncode(body)) .timeout(Duration(seconds: requestTimeout)); } on TimeoutException catch (_) { diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index cc22b4f..cfcd69d 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -5,8 +5,8 @@ class Url { String host() { // return '192.168.10.183:3000'; - // return 'agusandelnorte.gov.ph'; - return "192.168.10.219:3000"; + return 'agusandelnorte.gov.ph'; + // return "192.168.10.219:3000"; // return "192.168.10.241"; // return "192.168.10.221:3004"; // return "playweb.agusandelnorte.gov.ph"; From 690af305d68453766300ec4fadc7ec999f1cc8a4 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Tue, 22 Aug 2023 14:22:08 +0800 Subject: [PATCH 82/86] test superadmin operations --- android/app/src/main/AndroidManifest.xml | 2 +- .../rbac_operations/agency/agency_bloc.dart | 2 +- .../module_objects/module_objects_bloc.dart | 12 +++++-- .../module_objects/module_objects_state.dart | 3 +- .../role_assignment/role_assignment_bloc.dart | 2 -- lib/screens/sos/add_mobile.dart | 2 +- lib/screens/sos/components/add_mobile.dart | 8 +++-- .../agency_screen.dart | 32 ++++++++++++++----- .../superadmin/module/module_screen.dart | 1 + .../module_objects/module_objects_screen.dart | 13 +++++--- .../superadmin/object/object_screen.dart | 1 + .../operation/operation_screen.dart | 3 +- .../permission/permission_screen.dart | 5 ++- .../role_assignment_screen.dart | 13 +++----- .../role_extend/role_extend_screen.dart | 9 ++++-- .../role_module/role_module_scree.dart | 6 ++-- .../roles_under/roles_under_screen.dart | 12 ++++--- .../superadmin/stations/stations_screen.dart | 4 +-- .../components/dashboard/dashboard.dart | 2 +- .../dashboard/superadmin_expanded_menu.dart | 5 +-- .../rbac_operations/agency_services.dart | 2 +- .../rbac_operations/object_services.dart | 4 +-- lib/utils/request.dart | 8 ++--- lib/utils/urls.dart | 4 +-- pubspec.yaml | 1 - 25 files changed, 94 insertions(+), 62 deletions(-) rename lib/screens/superadmin/{agency.dart => agency}/agency_screen.dart (91%) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 922eb2f..56c1932 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -8,7 +8,7 @@ { await AgencyServices.instance.add(agency: event.agency); if (statusResponse['success']) { Agency newAgency = Agency.fromJson(statusResponse['data']); - agencies.add(newAgency); + agencies.insert(0,newAgency); emit(AgencyAddesState(response: statusResponse)); } else { emit(AgencyAddesState(response: statusResponse)); diff --git a/lib/bloc/rbac/rbac_operations/module_objects/module_objects_bloc.dart b/lib/bloc/rbac/rbac_operations/module_objects/module_objects_bloc.dart index 0892399..f401b3b 100644 --- a/lib/bloc/rbac/rbac_operations/module_objects/module_objects_bloc.dart +++ b/lib/bloc/rbac/rbac_operations/module_objects/module_objects_bloc.dart @@ -1,6 +1,5 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; -import 'package:unit2/model/login_data/user_info/module.dart'; import 'package:unit2/model/rbac/rbac_rbac.dart'; import 'package:unit2/sevices/roles/rbac_operations/module_objects_services.dart'; import 'package:unit2/sevices/roles/rbac_operations/object_services.dart'; @@ -15,6 +14,7 @@ class ModuleObjectsBloc extends Bloc { List moduleObjects = []; List objects = []; List modules = []; + List ids = []; on((event, emit) async { emit(ModuleObjectLoadingState()); try { @@ -28,8 +28,12 @@ class ModuleObjectsBloc extends Bloc { if (modules.isEmpty) { modules = await RbacModuleServices.instance.getRbacModule(); } + ids =[]; + for(var mo in moduleObjects){ + ids.add(mo.id); + } emit(ModuleObjectLoadedState( - moduleObjects: moduleObjects, objecs: objects, modules: modules)); + moduleObjects: moduleObjects, objecs: objects, modules: modules,ids: ids)); } catch (e) { emit(ModuleObjectsErrorState(message: e.toString())); } @@ -46,7 +50,9 @@ class ModuleObjectsBloc extends Bloc { if (statusResponse['success']) { statusResponse['data'].forEach((var permission) { ModuleObjects newModuleObject = ModuleObjects.fromJson(permission); - moduleObjects.add(newModuleObject); + if(!ids.contains(newModuleObject.id)){ + moduleObjects.add(newModuleObject); + } emit(ModuleObjectAddedState(response: statusResponse)); }); } else { diff --git a/lib/bloc/rbac/rbac_operations/module_objects/module_objects_state.dart b/lib/bloc/rbac/rbac_operations/module_objects/module_objects_state.dart index 9028df0..3ae8984 100644 --- a/lib/bloc/rbac/rbac_operations/module_objects/module_objects_state.dart +++ b/lib/bloc/rbac/rbac_operations/module_objects/module_objects_state.dart @@ -13,10 +13,11 @@ class ModuleObjectLoadedState extends ModuleObjectsState { final List moduleObjects; final List objecs; final List modules; + final List ids; const ModuleObjectLoadedState( {required this.moduleObjects, required this.modules, - required this.objecs}); + required this.objecs,required this.ids}); } class ModuleObjectsErrorState extends ModuleObjectsState { diff --git a/lib/bloc/role_assignment/role_assignment_bloc.dart b/lib/bloc/role_assignment/role_assignment_bloc.dart index 426a795..8f06a0d 100644 --- a/lib/bloc/role_assignment/role_assignment_bloc.dart +++ b/lib/bloc/role_assignment/role_assignment_bloc.dart @@ -1,7 +1,5 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; -import 'package:unit2/sevices/roles/rbac_services.dart'; - import '../../model/profile/basic_information/primary-information.dart'; import '../../model/rbac/assigned_role.dart'; import '../../model/rbac/rbac.dart'; diff --git a/lib/screens/sos/add_mobile.dart b/lib/screens/sos/add_mobile.dart index 51aa7e9..8fb0d22 100644 --- a/lib/screens/sos/add_mobile.dart +++ b/lib/screens/sos/add_mobile.dart @@ -82,7 +82,7 @@ class AddMobile extends StatelessWidget { validator: mobileNumberValidator, maxLength: 11, decoration: - normalTextFieldStyle(mobile1, "09000000000")), + normalTextFieldStyle(mobile1, "sfdfsdfsd")), const SizedBox( height: 12, ), diff --git a/lib/screens/sos/components/add_mobile.dart b/lib/screens/sos/components/add_mobile.dart index cb2ec9b..cec691c 100644 --- a/lib/screens/sos/components/add_mobile.dart +++ b/lib/screens/sos/components/add_mobile.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:unit2/bloc/sos/sos_bloc.dart'; import 'package:unit2/screens/sos/components/request_sos.dart'; import 'package:unit2/theme-data.dart/text-styles.dart'; @@ -69,20 +70,23 @@ class AddMobile extends StatelessWidget { children: [ //// Mobile number 1 FormBuilderTextField( + keyboardType: const TextInputType.numberWithOptions(), autovalidateMode: AutovalidateMode.onUserInteraction, name: 'mobile1', validator: mobileNumberValidator, maxLength: 11, decoration: - normalTextFieldStyle(mobile1, "09")), + normalTextFieldStyle(mobile1, "+639000000000")), //// Mobile number 2 FormBuilderTextField( + + keyboardType: const TextInputType.numberWithOptions(), autovalidateMode: AutovalidateMode.onUserInteraction, name: 'mobile2', decoration: - normalTextFieldStyle(mobile2, "09")), + normalTextFieldStyle(mobile2, "+639000000000")), const SizedBox(height: 30,), SizedBox( diff --git a/lib/screens/superadmin/agency.dart/agency_screen.dart b/lib/screens/superadmin/agency/agency_screen.dart similarity index 91% rename from lib/screens/superadmin/agency.dart/agency_screen.dart rename to lib/screens/superadmin/agency/agency_screen.dart index 391c006..0640b76 100644 --- a/lib/screens/superadmin/agency.dart/agency_screen.dart +++ b/lib/screens/superadmin/agency/agency_screen.dart @@ -1,4 +1,3 @@ -import 'package:app_popup_menu/app_popup_menu.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; @@ -11,7 +10,7 @@ import 'package:unit2/bloc/rbac/rbac_operations/agency/agency_bloc.dart'; import 'package:unit2/bloc/rbac/rbac_operations/object/object_bloc.dart'; import 'package:unit2/model/utils/agency.dart'; import 'package:unit2/model/utils/category.dart'; -import 'package:unit2/screens/superadmin/role/shared_pop_up_menu.dart'; +import 'package:unit2/utils/formatters.dart'; import 'package:unit2/utils/global_context.dart'; import 'package:unit2/widgets/error_state.dart'; import '../../../theme-data.dart/box_shadow.dart'; @@ -19,14 +18,12 @@ import '../../../theme-data.dart/btn-style.dart'; import '../../../theme-data.dart/colors.dart'; import '../../../theme-data.dart/form-style.dart'; import '../../../utils/alerts.dart'; -import '../../../utils/formatters.dart'; import '../../../utils/global.dart'; import '../../../widgets/Leadings/add_leading.dart'; import '../../../widgets/empty_data.dart'; class RbacAgencyScreen extends StatelessWidget { - - const RbacAgencyScreen(); + const RbacAgencyScreen({super.key}); @override Widget build(BuildContext context) { @@ -38,6 +35,7 @@ class RbacAgencyScreen extends StatelessWidget { BuildContext parent; return Scaffold( appBar: AppBar( + centerTitle: true, backgroundColor: primary, title: const Text("Agencies"), actions: [ @@ -54,6 +52,7 @@ class RbacAgencyScreen extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], validator: FormBuilderValidators.required( errorText: "This field is required"), name: "name", @@ -149,15 +148,14 @@ class RbacAgencyScreen extends StatelessWidget { .saveAndValidate()) { String name = formKey.currentState!.value['name']; + Navigator.pop(context); parent.read().add(AddAgency( agency: Agency( category: selectedAgencyCategory, id: null, name: name, privateEntity: isPrivate))); - Navigator.pop(context); } - }, child: const Text("Add")), ) @@ -174,6 +172,7 @@ class RbacAgencyScreen extends StatelessWidget { indicatorWidget: const SpinKitFadingCircle(color: Colors.white), child: BlocConsumer( listener: (context, state) { + print(state); if (state is AgencyLoadingState) { final progress = ProgressHUD.of(context); progress!.showWithText("Please wait..."); @@ -201,6 +200,23 @@ class RbacAgencyScreen extends StatelessWidget { }); } } + ////ADDED STATE + if (state is AgencyAddesState) { + if (state.response['success']) { + successAlert( + context, "Adding Successfull!", state.response['message'], + () { + Navigator.of(context).pop(); + context.read().add(GetAgencies()); + }); + } else { + errorAlert(context, "Adding Failed", + "Something went wrong. Please try again.", () { + Navigator.of(context).pop(); + context.read().add(GetAgencies()); + }); + } + } }, builder: (context, state) { final parent = context; @@ -255,7 +271,7 @@ class RbacAgencyScreen extends StatelessWidget { return SomethingWentWrong( message: state.message, onpressed: () { - context.read().add(GetObjects()); + parent.read().add(GetAgencies()); }); } return Container(); diff --git a/lib/screens/superadmin/module/module_screen.dart b/lib/screens/superadmin/module/module_screen.dart index 7520f77..98d287f 100644 --- a/lib/screens/superadmin/module/module_screen.dart +++ b/lib/screens/superadmin/module/module_screen.dart @@ -25,6 +25,7 @@ class RbacModuleScreen extends StatelessWidget { final formKey = GlobalKey(); return Scaffold( appBar: AppBar( + centerTitle: true, backgroundColor: primary, title: const Text("Module Screen"), actions: [ diff --git a/lib/screens/superadmin/module_objects/module_objects_screen.dart b/lib/screens/superadmin/module_objects/module_objects_screen.dart index 30ff430..e63f33c 100644 --- a/lib/screens/superadmin/module_objects/module_objects_screen.dart +++ b/lib/screens/superadmin/module_objects/module_objects_screen.dart @@ -1,4 +1,4 @@ -import 'package:app_popup_menu/app_popup_menu.dart'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; @@ -6,9 +6,7 @@ import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:group_list_view/group_list_view.dart'; -import 'package:multi_dropdown/models/value_item.dart'; import 'package:multi_dropdown/multiselect_dropdown.dart'; -import 'package:unit2/bloc/rbac/rbac_operations/module/module_bloc.dart'; import 'package:unit2/bloc/rbac/rbac_operations/module_objects/module_objects_bloc.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/error_state.dart'; @@ -35,7 +33,10 @@ class RbacModuleObjectsScreen extends StatelessWidget { List selectedValueItemObjects = []; final formKey = GlobalKey(); return Scaffold( + appBar: AppBar( + centerTitle: true, + elevation: 0, backgroundColor: primary, title: const Text("Module Object Screen"), actions: [ @@ -139,7 +140,7 @@ class RbacModuleObjectsScreen extends StatelessWidget { indicatorWidget: const SpinKitFadingCircle(color: Colors.white), child: BlocConsumer( listener: (context, state) { - print(state); + if (state is ModuleObjectLoadingState) { final progress = ProgressHUD.of(context); progress!.showWithText("Please wait..."); @@ -281,7 +282,9 @@ class RbacModuleObjectsScreen extends StatelessWidget { } if (state is ModuleObjectsErrorState) { return SomethingWentWrong( - message: state.message, onpressed: () {}); + message: state.message, onpressed: () { + parent.read().add(GetModuleObjects()); + }); } return Container(); }, diff --git a/lib/screens/superadmin/object/object_screen.dart b/lib/screens/superadmin/object/object_screen.dart index 98edea4..7a4640c 100644 --- a/lib/screens/superadmin/object/object_screen.dart +++ b/lib/screens/superadmin/object/object_screen.dart @@ -26,6 +26,7 @@ class RbacObjectScreen extends StatelessWidget { final formKey = GlobalKey(); return Scaffold( appBar: AppBar( + centerTitle: true, backgroundColor: primary, title: const Text("Objects Screen"), actions: [ diff --git a/lib/screens/superadmin/operation/operation_screen.dart b/lib/screens/superadmin/operation/operation_screen.dart index f9a9f8d..6b4a6bb 100644 --- a/lib/screens/superadmin/operation/operation_screen.dart +++ b/lib/screens/superadmin/operation/operation_screen.dart @@ -6,7 +6,6 @@ import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:unit2/bloc/rbac/rbac_operations/operation/operation_bloc.dart'; -import 'package:unit2/bloc/rbac/rbac_operations/role/role_bloc.dart'; import 'package:unit2/screens/superadmin/role/shared_pop_up_menu.dart'; import 'package:unit2/widgets/error_state.dart'; import '../../../theme-data.dart/box_shadow.dart'; @@ -361,7 +360,7 @@ class RbacOperationScreen extends StatelessWidget { } if (state is OperationErrorState) { return SomethingWentWrong( - message: state.toString(), + message: state.message.toString(), onpressed: () { context.read().add(GetOperations()); }); diff --git a/lib/screens/superadmin/permission/permission_screen.dart b/lib/screens/superadmin/permission/permission_screen.dart index 28e18c2..0400599 100644 --- a/lib/screens/superadmin/permission/permission_screen.dart +++ b/lib/screens/superadmin/permission/permission_screen.dart @@ -36,6 +36,7 @@ class RbacPermissionScreen extends StatelessWidget { BuildContext? parent; return Scaffold( appBar: AppBar( + centerTitle: true, backgroundColor: primary, title: const Text("Permissions Screen"), actions: [ @@ -272,7 +273,9 @@ class RbacPermissionScreen extends StatelessWidget { } if (state is PermissionErrorState) { return SomethingWentWrong( - message: state.message, onpressed: () {}); + message: state.message, onpressed: () { + parent!.read().add(GetPermissions()); + }); } return Container(); }, diff --git a/lib/screens/superadmin/role_assignment.dart/role_assignment_screen.dart b/lib/screens/superadmin/role_assignment.dart/role_assignment_screen.dart index a354473..e895170 100644 --- a/lib/screens/superadmin/role_assignment.dart/role_assignment_screen.dart +++ b/lib/screens/superadmin/role_assignment.dart/role_assignment_screen.dart @@ -5,27 +5,24 @@ import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; -import 'package:form_builder_validators/form_builder_validators.dart'; -import 'package:multi_dropdown/models/value_item.dart'; import 'package:multi_dropdown/multiselect_dropdown.dart'; import 'package:unit2/bloc/rbac/rbac_operations/role/role_bloc.dart'; -import 'package:unit2/bloc/rbac/rbac_operations/role_extend/role_extend_bloc.dart'; import 'package:unit2/bloc/role_assignment/role_assignment_bloc.dart'; import 'package:unit2/screens/superadmin/role/shared_pop_up_menu.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/error_state.dart'; import '../../../model/rbac/rbac.dart'; -import '../../../theme-data.dart/box_shadow.dart'; import '../../../theme-data.dart/btn-style.dart'; import '../../../theme-data.dart/colors.dart'; -import '../../../theme-data.dart/form-style.dart'; import '../../../utils/alerts.dart'; import '../../../utils/global.dart'; import '../../../widgets/empty_data.dart'; class RbacRoleAssignment extends StatelessWidget { final int id; - const RbacRoleAssignment({super.key, required this.id}); + final String name; + final String lname; + const RbacRoleAssignment({super.key, required this.id, required this.lname, required this.name}); @override Widget build(BuildContext context) { @@ -37,7 +34,7 @@ class RbacRoleAssignment extends StatelessWidget { appBar: AppBar( centerTitle: true, backgroundColor: primary, - title: const Text("User Roles Screen"), + title: const Text("User Roles Screenss"), actions: [ AddLeading(onPressed: () { BuildContext parent = context; @@ -272,7 +269,7 @@ class RbacRoleAssignment extends StatelessWidget { return SomethingWentWrong( message: state.message, onpressed: () { - context.read().add(GetRoles()); + context.read().add(GetAssignedRoles(firstname: name,lastname: lname)); }); } if (state is UserNotExistError) { diff --git a/lib/screens/superadmin/role_extend/role_extend_screen.dart b/lib/screens/superadmin/role_extend/role_extend_screen.dart index 670c1d4..ffe490c 100644 --- a/lib/screens/superadmin/role_extend/role_extend_screen.dart +++ b/lib/screens/superadmin/role_extend/role_extend_screen.dart @@ -32,6 +32,7 @@ class RbacRoleExtendScreen extends StatelessWidget { final formKey = GlobalKey(); return Scaffold( appBar: AppBar( + centerTitle: true, elevation: 0, backgroundColor: primary, title: const Text("Role Extend"), @@ -151,13 +152,13 @@ class RbacRoleExtendScreen extends StatelessWidget { if (state is RoleExtendDeletedState) { if (state.success) { successAlert(context, "Delete Successfull!", - "Role Module Deleted Successfully", () { + "Role Deleted Successfully", () { Navigator.of(context).pop(); context.read().add(GetRoleExtend()); }); } else { errorAlert( - context, "Delete Failed", "Role Module Delete Failed", () { + context, "Delete Failed", "Role Delete Failed", () { Navigator.of(context).pop(); context.read().add(GetRoleExtend()); }); @@ -279,7 +280,9 @@ class RbacRoleExtendScreen extends StatelessWidget { } if (state is RoleExtendErrorState) { return SomethingWentWrong( - message: state.message, onpressed: () {}); + message: state.message, onpressed: () { + context.read().add(GetRoleExtend()); + }); } return Container(); }, diff --git a/lib/screens/superadmin/role_module/role_module_scree.dart b/lib/screens/superadmin/role_module/role_module_scree.dart index 58521bf..1f3b199 100644 --- a/lib/screens/superadmin/role_module/role_module_scree.dart +++ b/lib/screens/superadmin/role_module/role_module_scree.dart @@ -1,4 +1,3 @@ -import 'package:app_popup_menu/app_popup_menu.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; @@ -7,7 +6,6 @@ import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:group_list_view/group_list_view.dart'; import 'package:multi_dropdown/multiselect_dropdown.dart'; -import 'package:unit2/bloc/rbac/rbac_operations/module_objects/module_objects_bloc.dart'; import 'package:unit2/bloc/rbac/rbac_operations/role_module/role_module_bloc.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/error_state.dart'; @@ -272,7 +270,9 @@ class RbacRoleModuleScreen extends StatelessWidget { } if (state is RoleModuleErrorState) { return SomethingWentWrong( - message: state.message, onpressed: () {}); + message: state.message, onpressed: () { + context.read().add(GetRoleModules()); + }); } return Container(); }, diff --git a/lib/screens/superadmin/roles_under/roles_under_screen.dart b/lib/screens/superadmin/roles_under/roles_under_screen.dart index a085fc5..6f82a78 100644 --- a/lib/screens/superadmin/roles_under/roles_under_screen.dart +++ b/lib/screens/superadmin/roles_under/roles_under_screen.dart @@ -1,4 +1,4 @@ -import 'package:app_popup_menu/app_popup_menu.dart'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; @@ -7,7 +7,6 @@ import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:group_list_view/group_list_view.dart'; import 'package:multi_dropdown/multiselect_dropdown.dart'; -import 'package:unit2/bloc/rbac/rbac_operations/role_module/role_module_bloc.dart'; import 'package:unit2/bloc/rbac/rbac_operations/roles_under/roles_under_bloc.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/error_state.dart'; @@ -34,8 +33,9 @@ class RbacRoleUnderScreen extends StatelessWidget { final formKey = GlobalKey(); return Scaffold( appBar: AppBar( + centerTitle: true, backgroundColor: primary, - title: const Text("Role Module"), + title: const Text("Assignable Roles"), actions: [ AddLeading(onPressed: () { showDialog( @@ -152,7 +152,7 @@ class RbacRoleUnderScreen extends StatelessWidget { if (state is RoleUnderDeletedState) { if (state.success) { successAlert(context, "Delete Successfull!", - "Role Module Deleted Successfully", () { + "Role Deleted Successfully", () { Navigator.of(context).pop(); context.read().add(GetRolesUnder()); }); @@ -277,7 +277,9 @@ tileColor: Colors.white, } if (state is RoleUnderErrorState) { return SomethingWentWrong( - message: state.message, onpressed: () {}); + message: state.message, onpressed: () { + context.read().add(GetRolesUnder()); + }); } return Container(); }, diff --git a/lib/screens/superadmin/stations/stations_screen.dart b/lib/screens/superadmin/stations/stations_screen.dart index 822feec..6bfa28d 100644 --- a/lib/screens/superadmin/stations/stations_screen.dart +++ b/lib/screens/superadmin/stations/stations_screen.dart @@ -8,12 +8,10 @@ import 'package:searchfield/searchfield.dart'; import 'package:unit2/bloc/rbac/rbac_operations/role/role_bloc.dart'; import 'package:unit2/bloc/rbac/rbac_operations/station/station_bloc.dart'; import 'package:unit2/model/rbac/rbac_station.dart'; -import 'package:unit2/model/roles/pass_check/station_assign_area.dart'; import 'package:unit2/model/utils/agency.dart'; import 'package:unit2/model/utils/position.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/error_state.dart'; -import '../../../../bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_bloc.dart'; import '../../../../model/rbac/station_type.dart'; import '../../../../theme-data.dart/box_shadow.dart'; import '../../../../theme-data.dart/btn-style.dart'; @@ -874,7 +872,7 @@ class _RbacStationScreenState extends State { return SomethingWentWrong( message: state.message, onpressed: () { - context.read().add(GetRoles()); + context.read().add(GetStations(agencyId: selectedAgencyId)); }); } diff --git a/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart b/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart index c11a424..3cd3420 100644 --- a/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart +++ b/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart @@ -14,7 +14,7 @@ import '../../../../../bloc/role/pass_check/est_point_person/est_point_person_as import '../../../../../bloc/role/pass_check/est_point_person/est_point_person_role_assignment/est_role_assignment_bloc.dart'; import '../../../../../bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_bloc.dart'; import '../../../../../sevices/roles/est_point_person/est_point_person_role_assignment_services.dart'; -import '../../../../superadmin/agency.dart/agency_screen.dart'; +import '../../../../superadmin/agency/agency_screen.dart'; import '../../../roles/establishment_point_person/est_point_person_agecies.dart'; import '../../../roles/establishment_point_person/est_point_person_role_member_screen.dart'; import '../../../roles/establishment_point_person/est_point_person_role_under_screen.dart'; diff --git a/lib/screens/unit2/homepage.dart/components/dashboard/superadmin_expanded_menu.dart b/lib/screens/unit2/homepage.dart/components/dashboard/superadmin_expanded_menu.dart index 4329d67..52f2d97 100644 --- a/lib/screens/unit2/homepage.dart/components/dashboard/superadmin_expanded_menu.dart +++ b/lib/screens/unit2/homepage.dart/components/dashboard/superadmin_expanded_menu.dart @@ -28,7 +28,7 @@ import 'package:unit2/screens/unit2/homepage.dart/module-screen.dart'; import 'package:unit2/theme-data.dart/btn-style.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/theme-data.dart/form-style.dart'; -import '../../../../superadmin/agency.dart/agency_screen.dart'; +import '../../../../superadmin/agency/agency_screen.dart'; import '../../../../superadmin/module_objects/module_objects_screen.dart'; import '../../../../superadmin/role_module/role_module_scree.dart'; import './shared_card_label.dart'; @@ -210,6 +210,7 @@ class SuperAdminMenu extends StatelessWidget { })); } if (object.object.name == 'Role Member') { + Navigator.of(context).pop(); showDialog( context: context, builder: (BuildContext context) { @@ -279,7 +280,7 @@ class SuperAdminMenu extends StatelessWidget { firstname: fname, lastname: - lname),),child:RbacRoleAssignment(id:id) ,); + lname),),child:RbacRoleAssignment(id:id,name: fname,lname: lname,) ,); })); } }, diff --git a/lib/sevices/roles/rbac_operations/agency_services.dart b/lib/sevices/roles/rbac_operations/agency_services.dart index 3f8d713..5445652 100644 --- a/lib/sevices/roles/rbac_operations/agency_services.dart +++ b/lib/sevices/roles/rbac_operations/agency_services.dart @@ -58,7 +58,7 @@ class AgencyServices { Map data = jsonDecode(response.body); statusResponse = data; }else{ - Map data = jsonDecode(response.body); + Map data = jsonDecode(response.body); String message = data['message']; statusResponse.addAll({'message': message}); statusResponse.addAll( diff --git a/lib/sevices/roles/rbac_operations/object_services.dart b/lib/sevices/roles/rbac_operations/object_services.dart index 402b2ac..7e61543 100644 --- a/lib/sevices/roles/rbac_operations/object_services.dart +++ b/lib/sevices/roles/rbac_operations/object_services.dart @@ -67,7 +67,7 @@ class RbacObjectServices { } else { Map data = jsonDecode(response.body); String message = data['message']; - statusResponse.addAll({'message': message}); + statusResponse.addAll({'message': "Error Adding Object"}); statusResponse.addAll( {'success': false}, ); @@ -111,7 +111,7 @@ class RbacObjectServices { } else { Map data = jsonDecode(response.body); String message = data['message']; - statusResponse.addAll({'message': message}); + statusResponse.addAll({'message': "Error Updating Object"}); statusResponse.addAll( {'success': false}, ); diff --git a/lib/utils/request.dart b/lib/utils/request.dart index 33c7a12..329b3c4 100644 --- a/lib/utils/request.dart +++ b/lib/utils/request.dart @@ -19,7 +19,7 @@ class Request { Map? param}) async { Response response; try { - response = await get(Uri.https(host, path!, param), headers: headers) + response = await get(Uri.http(host, path!, param), headers: headers) .timeout(Duration(seconds: requestTimeout)); } on TimeoutException catch (_) { Fluttertoast.showToast( @@ -61,7 +61,7 @@ class Request { Map? param}) async { Response response; try { - response = await post(Uri.https(host, path!, param), + response = await post(Uri.http(host, path!, param), headers: headers, body: jsonEncode(body)) .timeout(Duration(seconds: requestTimeout)); } on TimeoutException catch (_) { @@ -104,7 +104,7 @@ class Request { required Map? param}) async { Response response; try { - response =await put(Uri.https(host,path,param),headers: headers,body: jsonEncode(body)); + response =await put(Uri.http(host,path,param),headers: headers,body: jsonEncode(body)); } on TimeoutException catch (_) { Fluttertoast.showToast( msg: timeoutError, @@ -186,7 +186,7 @@ class Request { required Map? param}) async { Response response; try { - response = await delete(Uri.https(host, path, param), + response = await delete(Uri.http(host, path, param), headers: headers, body: jsonEncode(body)) .timeout(Duration(seconds: requestTimeout)); } on TimeoutException catch (_) { diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index cfcd69d..cc22b4f 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -5,8 +5,8 @@ class Url { String host() { // return '192.168.10.183:3000'; - return 'agusandelnorte.gov.ph'; - // return "192.168.10.219:3000"; + // return 'agusandelnorte.gov.ph'; + return "192.168.10.219:3000"; // return "192.168.10.241"; // return "192.168.10.221:3004"; // return "playweb.agusandelnorte.gov.ph"; diff --git a/pubspec.yaml b/pubspec.yaml index ce54884..9ac7efd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -104,7 +104,6 @@ dev_dependencies: flutter_lints: ^2.0.0 build_runner: ^2.1.7 hive_generator: ^1.1.2 - # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec From e7d6cb813390e7d0d4b156e6004ab07995938273 Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Sun, 27 Aug 2023 16:38:05 +0800 Subject: [PATCH 83/86] share attachment implemented --- android/app/src/main/AndroidManifest.xml | 2 +- .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 544 -> 0 bytes android/app/src/main/res/mipmap-hdpi/unit.png | Bin 0 -> 5881 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 442 -> 0 bytes android/app/src/main/res/mipmap-mdpi/unit.png | Bin 0 -> 3656 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 721 -> 0 bytes .../app/src/main/res/mipmap-xhdpi/unit.png | Bin 0 -> 8137 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 1031 -> 0 bytes .../app/src/main/res/mipmap-xxhdpi/unit.png | Bin 0 -> 12522 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 1443 -> 0 bytes .../app/src/main/res/mipmap-xxxhdpi/unit.png | Bin 0 -> 17244 bytes android/build.gradle | 2 +- .../profile/education/education_bloc.dart | 51 ++++ .../profile/education/education_event.dart | 12 + .../profile/education/education_state.dart | 14 +- .../profile/eligibility/eligibility_bloc.dart | 14 +- .../eligibility/eligibility_event.dart | 78 ++++-- .../eligibility/eligibility_state.dart | 5 + .../learning_development_bloc.dart | 6 + .../learning_development_event.dart | 6 + .../learning_development_state.dart | 6 + .../profile/workHistory/workHistory_bloc.dart | 121 ++++---- .../workHistory/workHistory_event.dart | 14 +- lib/bloc/role/pass_check/pass_check_bloc.dart | 8 +- lib/model/profile/work_history.dart | 208 ++++++++++---- .../education/education_view_attachment.dart | 121 ++++++++ .../profile/components/education_screen.dart | 28 +- .../eligibility_view_attachment.dart | 73 +++++ .../components/eligibility_screen.dart | 22 ++ .../learning_and_development_screen.dart | 22 ++ .../learning_development_view_attachment.dart | 71 +++++ .../components/work_history/add_modal.dart | 229 +++++++++++---- .../components/work_history/edit_modal.dart | 94 +++---- .../components/work_history_screen.dart | 164 +++++------ .../profile/shared/multiple_attachment.dart | 14 +- .../profile/shared/single_attachment.dart | 15 +- .../profile/shared/view_attachment.dart | 62 ++++ .../role_assignment_screen.dart | 1 + .../components/dashboard/dashboard.dart | 45 +-- .../dashboard/superadmin_expanded_menu.dart | 2 + .../unit2/homepage.dart/module-screen.dart | 4 +- .../profile/work_history_services.dart | 264 +++++++++++++----- lib/sevices/roles/pass_check_services.dart | 8 +- lib/utils/attachment_services.dart | 34 ++- lib/utils/request.dart | 8 +- lib/utils/request_permission.dart | 14 + lib/utils/urls.dart | 13 +- linux/flutter/generated_plugin_registrant.cc | 4 + linux/flutter/generated_plugins.cmake | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 8 + pubspec.lock | 160 +++++++++++ pubspec.yaml | 4 + .../flutter/generated_plugin_registrant.cc | 9 + windows/flutter/generated_plugins.cmake | 3 + 54 files changed, 1573 insertions(+), 471 deletions(-) delete mode 100644 android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-hdpi/unit.png delete mode 100644 android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-mdpi/unit.png delete mode 100644 android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xhdpi/unit.png delete mode 100644 android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xxhdpi/unit.png delete mode 100644 android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xxxhdpi/unit.png create mode 100644 lib/screens/profile/components/education/education_view_attachment.dart create mode 100644 lib/screens/profile/components/eligibility/eligibility_view_attachment.dart create mode 100644 lib/screens/profile/components/learning_development/learning_development_view_attachment.dart create mode 100644 lib/screens/profile/shared/view_attachment.dart create mode 100644 lib/utils/request_permission.dart diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 56c1932..c3002df 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -10,7 +10,7 @@ + android:icon="@mipmap/unit"> P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ diff --git a/android/app/src/main/res/mipmap-hdpi/unit.png b/android/app/src/main/res/mipmap-hdpi/unit.png new file mode 100644 index 0000000000000000000000000000000000000000..f8ecca055419f54b90cdd90fca17ab9c69612e22 GIT binary patch literal 5881 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 diff --git a/android/app/src/main/res/mipmap-xhdpi/unit.png b/android/app/src/main/res/mipmap-xhdpi/unit.png new file mode 100644 index 0000000000000000000000000000000000000000..15a1e0217fb370acc4753ae99775d4f46baef412 GIT binary patch literal 8137 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index d5f1c8d34e7a88e3f88bea192c3a370d44689c3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof diff --git a/android/app/src/main/res/mipmap-xxhdpi/unit.png b/android/app/src/main/res/mipmap-xxhdpi/unit.png new file mode 100644 index 0000000000000000000000000000000000000000..c1a8e83c1cf92c72d61c0b278dfff1963cc38ad9 GIT binary patch literal 12522 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 diff --git a/android/app/src/main/res/mipmap-xxxhdpi/unit.png b/android/app/src/main/res/mipmap-xxxhdpi/unit.png new file mode 100644 index 0000000000000000000000000000000000000000..d7c5bfebf800b008b6d74273279cf0ea5bd18c78 GIT binary patch literal 17244 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 literal 0 HcmV?d00001 diff --git a/android/build.gradle b/android/build.gradle index 83ae220..dfce013 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.6.10' + ext.kotlin_version = '1.8.0' repositories { google() mavenCentral() diff --git a/lib/bloc/profile/education/education_bloc.dart b/lib/bloc/profile/education/education_bloc.dart index 0117b6d..c10fa17 100644 --- a/lib/bloc/profile/education/education_bloc.dart +++ b/lib/bloc/profile/education/education_bloc.dart @@ -1,10 +1,18 @@ +import 'dart:io'; + import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:share_plus/share_plus.dart'; import 'package:unit2/model/profile/educational_background.dart'; import 'package:unit2/sevices/profile/education_services.dart'; +import 'package:unit2/utils/request_permission.dart'; import '../../../model/profile/attachment.dart'; import '../../../utils/attachment_services.dart'; +import '../../../utils/urls.dart'; part 'education_event.dart'; part 'education_state.dart'; @@ -14,9 +22,13 @@ class EducationBloc extends Bloc { List schools = []; List programs = []; List honors = []; + int? profileId; + String? token; List attachmentCategories = []; EducationBloc() : super(EducationInitial()) { on((event, emit) async { + profileId = event.profileId; + token = event.token; emit(EducationalBackgroundLoadingState()); try { if (attachmentCategories.isEmpty) { @@ -206,5 +218,44 @@ class EducationBloc extends Bloc { emit(EducationalBackgroundErrorState(message: e.toString())); } }); + on((event, emit) { + String fileUrl = + '${Url.instance.prefixHost()}://${Url.instance.host()}${event.source}'; + emit(EducationAttachmentViewState( + fileUrl: fileUrl, fileName: event.fileName)); + }); + on((event, emit) async { + emit(EducationalBackgroundLoadingState()); + Directory directory; + String? appDocumentPath; + if (await requestPermission(Permission.storage)) { + directory = await getApplicationDocumentsDirectory(); + appDocumentPath = directory.path; + } + try{ + final bool success = await AttachmentServices.instance.downloadAttachment( + filename: event.fileName, + source: event.source, + downLoadDir: appDocumentPath!); + if (success) { + final result = await Share.shareXFiles( + [XFile("$appDocumentPath/${event.fileName}")]); + if (result.status == ShareResultStatus.success) { + Fluttertoast.showToast(msg: "Attachment shared successfully"); + emit(EducationAttachmentViewState( + fileUrl: event.source, fileName: event.fileName)); + } else { + Fluttertoast.showToast(msg: "Attachment shared unsuccessfully"); + emit(EducationAttachmentViewState( + fileUrl: event.source, fileName: event.fileName)); + } + } else { + emit(EducationAttachmentViewState( + fileUrl: event.source, fileName: event.fileName)); + } + }catch(e){ +emit(EducationalBackgroundErrorState(message: e.toString())); + } + }); } } diff --git a/lib/bloc/profile/education/education_event.dart b/lib/bloc/profile/education/education_event.dart index 9b68a0f..d2f57e6 100644 --- a/lib/bloc/profile/education/education_event.dart +++ b/lib/bloc/profile/education/education_event.dart @@ -83,3 +83,15 @@ class DeleteEducationAttachment extends EducationEvent{ final int profileId; const DeleteEducationAttachment({required this.attachment, required this.moduleId, required this.profileId, required this.token}); } + +class EducationViewAttachment extends EducationEvent{ + final String fileName; + final String source; + const EducationViewAttachment({required this.source,required this.fileName}); +} + +class ShareAttachment extends EducationEvent{ + final String fileName; + final String source; + const ShareAttachment({required this.fileName, required this.source}); +} diff --git a/lib/bloc/profile/education/education_state.dart b/lib/bloc/profile/education/education_state.dart index 3cfe505..d7fd394 100644 --- a/lib/bloc/profile/education/education_state.dart +++ b/lib/bloc/profile/education/education_state.dart @@ -60,7 +60,6 @@ class EducationAddedState extends EducationState { List get props => [response]; } - //// Edited State class EditedEducationState extends EducationState { final Map response; @@ -89,4 +88,15 @@ class EducationAttachmentDeletedState extends EducationState { const EducationAttachmentDeletedState({required this.success}); @override List get props => [success]; -} \ No newline at end of file +} + +class EducationAttachmentViewState extends EducationState { + final String fileUrl; + final String fileName; + const EducationAttachmentViewState({required this.fileUrl, required this.fileName}); +} + +class EducationAttachmentShareState extends EducationState{ + final bool success; + const EducationAttachmentShareState({required this.success,}); +} diff --git a/lib/bloc/profile/eligibility/eligibility_bloc.dart b/lib/bloc/profile/eligibility/eligibility_bloc.dart index 1be36b4..6710f47 100644 --- a/lib/bloc/profile/eligibility/eligibility_bloc.dart +++ b/lib/bloc/profile/eligibility/eligibility_bloc.dart @@ -11,6 +11,7 @@ import '../../../model/utils/eligibility.dart'; import '../../../sevices/profile/eligibility_services.dart'; import '../../../utils/location_utilities.dart'; import '../../../utils/profile_utilities.dart'; +import '../../../utils/urls.dart'; part 'eligibility_event.dart'; part 'eligibility_state.dart'; @@ -52,6 +53,7 @@ class EligibilityBloc extends Bloc { //// GET ELIGIBILITY on((event, emit) async { + emit(EligibilityLoadingState()); try { if (attachmentCategories.isEmpty) { attachmentCategories = @@ -257,7 +259,7 @@ class EligibilityBloc extends Bloc { on((event, emit) async { emit(EligibilityLoadingState()); - // try { + try { final bool success = await AttachmentServices.instance.deleteAttachment( attachment: event.attachment, moduleId: int.parse(event.moduleId), @@ -275,9 +277,13 @@ class EligibilityBloc extends Bloc { } else { emit(EligibilitytAttachmentDeletedState(success: success)); } - // } catch (e) { - // emit(EligibilityErrorState(message: e.toString())); - // } + } catch (e) { + emit(EligibilityErrorState(message: e.toString())); + } + }); + on((event,emit){ + String fileUrl = '${Url.instance.prefixHost()}://${Url.instance.host()}${event.source}'; + emit(EligibilityAttachmentViewState(fileUrl: fileUrl)); }); } } diff --git a/lib/bloc/profile/eligibility/eligibility_event.dart b/lib/bloc/profile/eligibility/eligibility_event.dart index a9b6ca8..925b105 100644 --- a/lib/bloc/profile/eligibility/eligibility_event.dart +++ b/lib/bloc/profile/eligibility/eligibility_event.dart @@ -7,38 +7,44 @@ abstract class EligibilityEvent extends Equatable { List get props => []; } -class ShowAddEligibilityForm extends EligibilityEvent { +class ShowAddEligibilityForm extends EligibilityEvent {} -} - -class GetEligibilities extends EligibilityEvent{ +class GetEligibilities extends EligibilityEvent { final int profileId; final String token; const GetEligibilities({required this.profileId, required this.token}); @override - List get props => [profileId,token]; + List get props => [profileId, token]; } -class AddEligibility extends EligibilityEvent{ - final EligibityCert eligibityCert; +class AddEligibility extends EligibilityEvent { + final EligibityCert eligibityCert; final String profileId; final String token; - const AddEligibility({required this.eligibityCert, required this.profileId, required this.token}); + const AddEligibility( + {required this.eligibityCert, + required this.profileId, + required this.token}); @override - List get props => [eligibityCert, profileId, token]; + List get props => [eligibityCert, profileId, token]; } -class UpdateEligibility extends EligibilityEvent{ + +class UpdateEligibility extends EligibilityEvent { final EligibityCert eligibityCert; final String profileId; final String token; final int oldEligibility; - const UpdateEligibility({required this.eligibityCert, required this.oldEligibility,required this.profileId, required this.token}); + const UpdateEligibility( + {required this.eligibityCert, + required this.oldEligibility, + required this.profileId, + required this.token}); @override - List get props =>[eligibityCert,profileId,token,oldEligibility]; + List get props => [eligibityCert, profileId, token, oldEligibility]; } -class LoadEligibility extends EligibilityEvent { +class LoadEligibility extends EligibilityEvent { const LoadEligibility(); @override List get props => []; @@ -55,45 +61,55 @@ class DeleteEligibility extends EligibilityEvent { final String profileId; final int eligibilityId; final String token; - + const DeleteEligibility( - { - required this.eligibilityId, + {required this.eligibilityId, required this.profileId, required this.token}); @override - List get props => [ profileId, eligibilityId, token]; + List get props => [profileId, eligibilityId, token]; } -class CallErrorState extends EligibilityEvent{ - -} +class CallErrorState extends EligibilityEvent {} + ////Add Attachment -class AddEligibiltyAttachment extends EligibilityEvent{ +class AddEligibiltyAttachment extends EligibilityEvent { final String categoryId; final String attachmentModule; final List filePaths; final String token; final String profileId; - const AddEligibiltyAttachment({required this.attachmentModule, required this.filePaths, required this.categoryId, required this.profileId, required this.token}); + const AddEligibiltyAttachment( + {required this.attachmentModule, + required this.filePaths, + required this.categoryId, + required this.profileId, + required this.token}); @override - List get props => [categoryId,attachmentModule,filePaths, token,profileId]; + List get props => + [categoryId, attachmentModule, filePaths, token, profileId]; } ////Delete Attachment -class DeleteEligibyAttachment extends EligibilityEvent{ +class DeleteEligibyAttachment extends EligibilityEvent { final String profileId; final String token; final Attachment attachment; final String moduleId; - const DeleteEligibyAttachment({required this.attachment,required this.moduleId, required this.profileId, required this.token}); + const DeleteEligibyAttachment( + {required this.attachment, + required this.moduleId, + required this.profileId, + required this.token}); } +class EligibilityViewAttachments extends EligibilityEvent { + final String source; + const EligibilityViewAttachments({required this.source}); +} - - - - - - +class EligibiltyViewAttachmentEvent extends EligibilityEvent{ + final String source; + const EligibiltyViewAttachmentEvent({required this.source}); +} diff --git a/lib/bloc/profile/eligibility/eligibility_state.dart b/lib/bloc/profile/eligibility/eligibility_state.dart index 7d746d9..6f8976c 100644 --- a/lib/bloc/profile/eligibility/eligibility_state.dart +++ b/lib/bloc/profile/eligibility/eligibility_state.dart @@ -107,3 +107,8 @@ class EligibilitytAttachmentDeletedState extends EligibilityState { @override List get props => [success]; } + +class EligibilityAttachmentViewState extends EligibilityState { + final String fileUrl; + const EligibilityAttachmentViewState({required this.fileUrl}); +} diff --git a/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart b/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart index 465cefb..9dd75b7 100644 --- a/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart +++ b/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart @@ -13,6 +13,7 @@ import '../../../model/utils/category.dart'; import '../../../utils/attachment_services.dart'; import '../../../utils/location_utilities.dart'; import '../../../utils/profile_utilities.dart'; +import '../../../utils/urls.dart'; part 'learning_development_event.dart'; part 'learning_development_state.dart'; @@ -325,5 +326,10 @@ class LearningDevelopmentBloc emit(LearningDevelopmentErrorState(message: e.toString())); } }); + on((event,emit){ + String fileUrl = '${Url.instance.prefixHost()}://${Url.instance.host()}${event.source}'; + emit(LearningAndDevelopmentAttachmentViewState(fileUrl: fileUrl)); + }); } + } diff --git a/lib/bloc/profile/learningDevelopment/learning_development_event.dart b/lib/bloc/profile/learningDevelopment/learning_development_event.dart index c8528ac..d450d7d 100644 --- a/lib/bloc/profile/learningDevelopment/learning_development_event.dart +++ b/lib/bloc/profile/learningDevelopment/learning_development_event.dart @@ -88,3 +88,9 @@ class DeleteLearningDevAttachment extends LearningDevelopmentEvent{ const DeleteLearningDevAttachment({required this.attachment, required this.moduleId, required this.profileId, required this.token}); } +class LearningDevelopmentViewAttachmentEvent extends LearningDevelopmentEvent{ + final String source; + const LearningDevelopmentViewAttachmentEvent({required this.source}); +} + + diff --git a/lib/bloc/profile/learningDevelopment/learning_development_state.dart b/lib/bloc/profile/learningDevelopment/learning_development_state.dart index 2699470..7507e9c 100644 --- a/lib/bloc/profile/learningDevelopment/learning_development_state.dart +++ b/lib/bloc/profile/learningDevelopment/learning_development_state.dart @@ -133,3 +133,9 @@ class LearningDevAttachmentDeletedState extends LearningDevelopmentState { @override List get props => [success]; } + + +class LearningAndDevelopmentAttachmentViewState extends LearningDevelopmentState { + final String fileUrl; + const LearningAndDevelopmentAttachmentViewState({required this.fileUrl}); +} \ No newline at end of file diff --git a/lib/bloc/profile/workHistory/workHistory_bloc.dart b/lib/bloc/profile/workHistory/workHistory_bloc.dart index 85bd4bd..b751baa 100644 --- a/lib/bloc/profile/workHistory/workHistory_bloc.dart +++ b/lib/bloc/profile/workHistory/workHistory_bloc.dart @@ -7,6 +7,7 @@ import 'package:unit2/model/utils/position.dart'; import 'package:unit2/sevices/profile/work_history_services.dart'; import 'package:unit2/utils/profile_utilities.dart'; import '../../../model/profile/attachment.dart'; + import '../../../model/utils/category.dart'; import '../../../utils/attachment_services.dart'; part 'workHistory_event.dart'; @@ -67,8 +68,10 @@ class WorkHistoryBloc extends Bloc { }); //// ADD WORK HISTORIES on((event, emit) async { - try { + // try { Map status = await WorkHistoryService.instance.add( + accomplishment: event.accomplishment, + actualDuties: event.actualDuties, isPrivate: event.isPrivate, workHistory: event.workHistory, token: event.token, @@ -80,35 +83,35 @@ class WorkHistoryBloc extends Bloc { } else { emit(WorkHistoryAddedState(response: status)); } - } catch (e) { - emit(WorkHistoryErrorState(message: e.toString())); - } + // } catch (e) { + // emit(WorkHistoryErrorState(message: e.toString())); + // } }); ////UPDATE WORK HISTORY - on((event, emit) async { - try { - Map status = await WorkHistoryService.instance.update( - oldWorkHistory: event.oldWorkHistory, - newWorkHistory: event.workHistory, - token: event.token, - profileId: event.profileId); - if (status['success']) { - WorkHistory workHistory = WorkHistory.fromJson(status['data']); - workExperiences.removeWhere((WorkHistory work) { - return work.id == event.oldWorkHistory.id; - }); - workExperiences.add(workHistory); - emit(WorkHistoryEditedState(response: status)); - } else { - emit(WorkHistoryEditedState( - response: status, - )); - } - } catch (e) { - emit(WorkHistoryErrorState(message: e.toString())); - } - }); + // on((event, emit) async { + // try { + // Map status = await WorkHistoryService.instance.update( + // oldWorkHistory: event.oldWorkHistory, + // newWorkHistory: event.workHistory, + // token: event.token, + // profileId: event.profileId); + // if (status['success']) { + // WorkHistory workHistory = WorkHistory.fromJson(status['data']); + // workExperiences.removeWhere((WorkHistory work) { + // return work.id == event.oldWorkHistory.id; + // }); + // workExperiences.add(workHistory); + // emit(WorkHistoryEditedState(response: status)); + // } else { + // emit(WorkHistoryEditedState( + // response: status, + // )); + // } + // } catch (e) { + // emit(WorkHistoryErrorState(message: e.toString())); + // } + // }); ////SHOW EDIT WORK HISTORIES on((event, emit) async { @@ -209,12 +212,12 @@ class WorkHistoryBloc extends Bloc { Attachment newAttachment = Attachment.fromJson(element); attachments.add(newAttachment); }); - workHistory.attachments == null - ? workHistory.attachments = attachments - : workHistory.attachments = [ - ...workHistory.attachments!, - ...attachments - ]; + // workHistory.attachments == null + // ? workHistory.attachments = attachments + // : workHistory.attachments = [ + // ...workHistory.attachments!, + // ...attachments + // ]; emit(WorkHistoryDevAttachmentAddedState(response: status)); } else { emit(WorkHistoryDevAttachmentAddedState(response: status)); @@ -223,31 +226,31 @@ class WorkHistoryBloc extends Bloc { emit(WorkHistoryErrorState(message: e.toString())); } }); - ////Delete Attachment - on((event, emit) async { - emit(WorkHistoryLoadingState()); - try { - final bool success = await AttachmentServices.instance.deleteAttachment( - attachment: event.attachment, - moduleId: event.moduleId, - profileId: event.profileId.toString(), - token: event.token); - if (success) { - final WorkHistory workHistory = - workExperiences - .firstWhere((element) => element.id == event.moduleId); - workHistory.attachments - ?.removeWhere((element) => element.id == event.attachment.id); - workExperiences - .removeWhere((element) => element.id == event.moduleId); - workExperiences.add(workHistory); - emit(WorkHistoryDevAttachmentDeletedState(success: success)); - } else { - emit(WorkHistoryDevAttachmentDeletedState(success: success)); - } - } catch (e) { - emit(WorkHistoryErrorState(message: e.toString())); - } - }); + // ////Delete Attachment + // on((event, emit) async { + // emit(WorkHistoryLoadingState()); + // try { + // final bool success = await AttachmentServices.instance.deleteAttachment( + // attachment: event.attachment, + // moduleId: event.moduleId, + // profileId: event.profileId.toString(), + // token: event.token); + // if (success) { + // final WorkHistory workHistory = + // workExperiences + // .firstWhere((element) => element.id == event.moduleId); + // workHistory.attachments + // ?.removeWhere((element) => element.id == event.attachment.id); + // workExperiences + // .removeWhere((element) => element.id == event.moduleId); + // workExperiences.add(workHistory); + // emit(WorkHistoryDevAttachmentDeletedState(success: success)); + // } else { + // emit(WorkHistoryDevAttachmentDeletedState(success: success)); + // } + // } catch (e) { + // emit(WorkHistoryErrorState(message: e.toString())); + // } + // }); } } diff --git a/lib/bloc/profile/workHistory/workHistory_event.dart b/lib/bloc/profile/workHistory/workHistory_event.dart index 1895bfe..6e73101 100644 --- a/lib/bloc/profile/workHistory/workHistory_event.dart +++ b/lib/bloc/profile/workHistory/workHistory_event.dart @@ -43,12 +43,14 @@ class DeleteWorkHistory extends WorkHistorytEvent{ class UpdateWorkHistory extends WorkHistorytEvent{ final WorkHistory workHistory; - final WorkHistory oldWorkHistory; - final String profileId; + final bool isPrivate; + final int profileId; final String token; - const UpdateWorkHistory({required this.oldWorkHistory, required this.profileId, required this.token, required this.workHistory}); + final String? actualDuties; + final String? accomplishment; + const UpdateWorkHistory({required this.profileId, required this.token, required this.workHistory, required this.accomplishment, required this.actualDuties, required this.isPrivate}); @override - List get props => [profileId,token,workHistory,oldWorkHistory]; + List get props => [profileId,token,workHistory,]; } class AddWorkHostory extends WorkHistorytEvent{ @@ -56,7 +58,9 @@ class AddWorkHostory extends WorkHistorytEvent{ final bool isPrivate; final int profileId; final String token; - const AddWorkHostory({required this.workHistory, required this.isPrivate, required this.profileId, required this.token}); + final String? actualDuties; + final String? accomplishment; + const AddWorkHostory({required this.workHistory, required this.isPrivate, required this.profileId, required this.token, required this.accomplishment, required this.actualDuties}); @override List get props => [workHistory,profileId,token,isPrivate]; } diff --git a/lib/bloc/role/pass_check/pass_check_bloc.dart b/lib/bloc/role/pass_check/pass_check_bloc.dart index 9458357..c1d157a 100644 --- a/lib/bloc/role/pass_check/pass_check_bloc.dart +++ b/lib/bloc/role/pass_check/pass_check_bloc.dart @@ -22,15 +22,15 @@ class PassCheckBloc extends Bloc { int? stationId; String? cpId; on((event, emit) async { - // try { + try { emit(PassCheckLoadingState()); List response = await PassCheckServices.instance .getPassCheckArea(roleId: event.roleId, userId: event.userId); roleId = event.roleId; emit(AssignAreaLoaded(assignedArea: response, roleId: roleId!)); - // } catch (e) { - // emit(PassCheckErrorState(message: e.toString())); - // } + } catch (e) { + emit(PassCheckErrorState(message: e.toString())); + } }); on((event, emit) { otherInputs = event.includeOtherInputs; diff --git a/lib/model/profile/work_history.dart b/lib/model/profile/work_history.dart index 5e01642..538e6f4 100644 --- a/lib/model/profile/work_history.dart +++ b/lib/model/profile/work_history.dart @@ -1,75 +1,163 @@ -// To parse this JSON data, do -// -// final workHistory = workHistoryFromJson(jsonString); - -import 'dart:convert'; +import 'package:unit2/model/utils/industry_class.dart'; import '../utils/agency.dart'; -import '../utils/category.dart'; -import '../utils/industry_class.dart'; import '../utils/position.dart'; -import 'attachment.dart'; - -WorkHistory workHistoryFromJson(String str) => WorkHistory.fromJson(json.decode(str)); - -String workHistoryToJson(WorkHistory data) => json.encode(data.toJson()); class WorkHistory { - WorkHistory({ - this.id, - this.agency, - this.sgStep, - this.toDate, - this.position, - this.fromDate, - this.attachments, - this.salaryGrade, - this.monthlySalary, - this.appointmentStatus, - }); + final PositionTitle? position; + final Agency? agency; + final Supervisor? supervisor; + final int? id; + final DateTime? fromDate; + final DateTime? toDate; + final int? agencydepid; + final double? monthlysalary; + final String? statusAppointment; + final int? salarygrade; + final int? sgstep; + final List? accomplishment; + final List? actualDuties; - final int? id; - final Agency? agency; - final int? sgStep; - final DateTime? toDate; - final PositionTitle? position; - final DateTime? fromDate; - List? attachments; - final int? salaryGrade; - final double? monthlySalary; - final String? appointmentStatus; + WorkHistory({ + required this.position, + required this.agency, + required this.supervisor, + required this.id, + required this.fromDate, + required this.toDate, + required this.agencydepid, + required this.monthlysalary, + required this.statusAppointment, + required this.salarygrade, + required this.sgstep, + required this.accomplishment, + required this.actualDuties, + }); - factory WorkHistory.fromJson(Map json) => WorkHistory( + factory WorkHistory.fromJson(Map json) => WorkHistory( + position: PositionTitle.fromJson(json["position"]), + agency: json['agency'] == null?null: Agency.fromJson(json["agency"]), + supervisor: json['supervisor'] == null?null: Supervisor.fromJson(json["supervisor"]), id: json["id"], - agency: json["agency"] == null ? null : Agency.fromJson(json["agency"]), - sgStep: json["sg_step"], - toDate: json["to_date"] == null ? null : DateTime.parse(json["to_date"]), - position: json["position"] == null ? null : PositionTitle.fromJson(json["position"]), - fromDate: json["from_date"] == null ? null : DateTime.parse(json["from_date"]), - attachments: json['attachments'] ==null?null: List.from(json["attachments"].map((x) => Attachment.fromJson(x))), - salaryGrade: json["salary_grade"], - monthlySalary: json["monthly_salary"], - appointmentStatus: json["appointment_status"], - ); + fromDate: json['from_date'] == null?null: DateTime.tryParse(json["from_date"]), + toDate: json['to_date'] == null?null: DateTime.tryParse(json["to_date"]), + agencydepid: json["agencydepid"], + monthlysalary: json["monthlysalary"], + statusAppointment: json["status_appointment"], + salarygrade: json["salarygrade"], + sgstep: json["sgstep"], + accomplishment:json['accomplishment'] == null?null: json['accomplishment'] == null?null: List.from( + json["accomplishment"].map((x) => Accomplishment.fromJson(x))), + actualDuties: json['actual_duties'] == null?null: List.from( + json["actual_duties"].map((x) => ActualDuty.fromJson(x))), + ); - Map toJson() => { - "id": id, - "agency": agency?.toJson(), - "sg_step": sgStep, - "to_date": "${toDate!.year.toString().padLeft(4, '0')}-${toDate!.month.toString().padLeft(2, '0')}-${toDate!.day.toString().padLeft(2, '0')}", + Map toJson() => { "position": position?.toJson(), - "from_date": "${fromDate!.year.toString().padLeft(4, '0')}-${fromDate!.month.toString().padLeft(2, '0')}-${fromDate!.day.toString().padLeft(2, '0')}", - // "attachments": attachments, - "salary_grade": salaryGrade, - "monthly_salary": monthlySalary, - "appointment_status": appointmentStatus, - }; + "agency": agency?.toJson(), + "supervisor": supervisor?.toJson(), + "id": id, + "from_date": + "${fromDate?.year.toString().padLeft(4, '0')}-${fromDate?.month.toString().padLeft(2, '0')}-${fromDate?.day.toString().padLeft(2, '0')}", + "to_date": toDate, + "agencydepid": agencydepid, + "monthlysalary": monthlysalary, + "status_appointment": statusAppointment, + "salarygrade": salarygrade, + "sgstep": sgstep, + "accomplishment": + List.from(accomplishment!.map((x) => x.toJson())), + "actual_duties": + List.from(actualDuties!.map((x) => x.toJson())), + }; +} + +class Accomplishment { + final int? id; + final int? workExperienceId; + final String? accomplishment; + + Accomplishment({ + required this.id, + required this.workExperienceId, + required this.accomplishment, + }); + + factory Accomplishment.fromJson(Map json) => Accomplishment( + id: json["id"], + workExperienceId: json["work_experience_id"], + accomplishment: json["accomplishment"], + ); + + Map toJson() => { + "id": id, + "work_experience_id": workExperienceId, + "accomplishment": accomplishment, + }; +} + +class ActualDuty { + final int? id; + final int? workExperienceId; + final String description; + + ActualDuty({ + required this.id, + required this.workExperienceId, + required this.description, + }); + + factory ActualDuty.fromJson(Map json) => ActualDuty( + id: json["id"], + workExperienceId: json["work_experience_id"], + description: json["description"], + ); + + Map toJson() => { + "id": id, + "work_experience_id": workExperienceId, + "description": description, + }; +} + + + +class Supervisor { + final int? id; + final int? agencyId; + final String? lastname; + final String? firstname; + final String? middlename; + final String? stationName; + + Supervisor({ + required this.id, + required this.agencyId, + required this.lastname, + required this.firstname, + required this.middlename, + required this.stationName, + }); + + factory Supervisor.fromJson(Map json) => Supervisor( + id: json["id"], + agencyId: json["agency_id"], + lastname: json["lastname"], + firstname: json["firstname"], + middlename: json["middlename"], + stationName: json["station_name"], + ); + + Map toJson() => { + "id": id, + "agency_id": agencyId, + "lastname": lastname, + "firstname": firstname, + "middlename": middlename, + "station_name": stationName, + }; } - - - - diff --git a/lib/screens/profile/components/education/education_view_attachment.dart b/lib/screens/profile/components/education/education_view_attachment.dart new file mode 100644 index 0000000..88657f9 --- /dev/null +++ b/lib/screens/profile/components/education/education_view_attachment.dart @@ -0,0 +1,121 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:share_plus/share_plus.dart'; +import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart'; +import 'package:unit2/bloc/profile/education/education_bloc.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +class EudcationViewAttachment extends StatefulWidget { + const EudcationViewAttachment({super.key}); + + @override + State createState() => + _EudcationViewAttachmentState(); +} + +class _EudcationViewAttachmentState extends State { + @override + Widget build(BuildContext context) { + String? fileUrl; + String? filename; + return Scaffold( + floatingActionButton: FloatingActionButton( + onPressed: () async { + await _launchInBrowser(fileUrl!); + }, + child: const Icon(Icons.file_download), + ), + appBar: AppBar( + title: const Text("Attachment"), + centerTitle: true, + actions: [ + IconButton( + onPressed: () { + context.read().add( + ShareAttachment(fileName: filename!, source: fileUrl!)); + }, + icon: const Icon(Icons.share)), + ], + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocConsumer( + builder: (context, state) { + if (state is EducationAttachmentViewState) { + fileUrl = state.fileUrl; + filename = state.fileName; + + bool isPDF = state.fileUrl[state.fileUrl.length - 1] == 'f' + ? true + : false; + return SizedBox( + child: isPDF + ? SfPdfViewer.network( + state.fileUrl, + onDocumentLoadFailed: (details) { + Center( + child: Text(details.description), + ); + }, + ) + : Center( + child: CachedNetworkImage( + progressIndicatorBuilder: (context, url, progress) { + return const SizedBox( + height: 100, + width: 100, + child: CircularProgressIndicator( + color: primary, + )); + }, + imageBuilder: (context, imageProvider) => Container( + decoration: BoxDecoration( + image: DecorationImage( + image: imageProvider, fit: BoxFit.fill)), + ), + imageUrl: state.fileUrl, + width: double.infinity, + height: 220, + fit: BoxFit.cover, + ), + ), + ); + } + return Container(); + }, + listener: (context, state) { + if (state is EducationalBackgroundLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is EducationAttachmentViewState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + }, + ), + )); + } + + Future _launchInBrowser(String url) async { + final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + if (!await launcher.launch( + url, + useSafariVC: false, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: {}, + )) { + throw Exception('Could not launch $url'); + } + } +} diff --git a/lib/screens/profile/components/education_screen.dart b/lib/screens/profile/components/education_screen.dart index 15c88a0..761f285 100644 --- a/lib/screens/profile/components/education_screen.dart +++ b/lib/screens/profile/components/education_screen.dart @@ -11,6 +11,7 @@ import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/educational_background.dart'; import 'package:unit2/screens/profile/components/education/add_modal.dart'; +import 'package:unit2/screens/profile/shared/view_attachment.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; @@ -26,6 +27,7 @@ import '../../../widgets/Leadings/close_leading.dart'; import '../shared/multiple_attachment.dart'; import '../shared/single_attachment.dart'; import 'education/edit_modal.dart'; +import 'education/education_view_attachment.dart'; class EducationScreen extends StatelessWidget { const EducationScreen({super.key}); @@ -321,12 +323,12 @@ class EducationScreen extends StatelessWidget { Column( children: honors .map((Honor honor) => Padding( - padding: const EdgeInsets.all(3.0), - child: Text( + padding: const EdgeInsets.all(3.0), + child: Text( "-${honor.name!.trim()}", style: Theme.of(context).textTheme.labelSmall, ), - )) + )) .toList(), ), ], @@ -696,6 +698,15 @@ class EducationScreen extends StatelessWidget { children: [ const Divider(), SingleAttachment( + view: () { + Navigator.push( + context, + MaterialPageRoute( + builder: ((context) => BlocProvider.value( + value: EducationBloc()..add(EducationViewAttachment(source: state.educationalBackground[index].attachments!.first.source!,fileName: state.educationalBackground[index].attachments!.first.filename!)), + child: const EudcationViewAttachment(), + )))); + }, onpressed: () { confirmAlert( @@ -728,6 +739,17 @@ class EducationScreen extends StatelessWidget { ) ////Multiple Attachments View : MultipleAttachments( + viewAttachment: + (source,filname) { + Navigator.push( + context, + MaterialPageRoute( + builder: ((context) => + BlocProvider.value( + value: EducationBloc()..add(EducationViewAttachment(source: source,fileName: filname)), + child: const EudcationViewAttachment(), + )))); + }, profileId: profileId, token: token!, diff --git a/lib/screens/profile/components/eligibility/eligibility_view_attachment.dart b/lib/screens/profile/components/eligibility/eligibility_view_attachment.dart new file mode 100644 index 0000000..9d21ba1 --- /dev/null +++ b/lib/screens/profile/components/eligibility/eligibility_view_attachment.dart @@ -0,0 +1,73 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart'; +import 'package:unit2/bloc/profile/education/education_bloc.dart'; +import 'package:unit2/bloc/profile/eligibility/eligibility_bloc.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:url_launcher/url_launcher.dart'; +import '../../../../utils/urls.dart'; +class EligibilityViewAttachment extends StatefulWidget { + + const EligibilityViewAttachment({super.key}); + + @override + State createState() => _EligibilityViewAttachmentState(); +} + +class _EligibilityViewAttachmentState extends State { + @override + Widget build(BuildContext context) { + String? fileUrl; + return Scaffold( + floatingActionButton: FloatingActionButton( + onPressed: ()async { + await launchUrl(Uri.parse(fileUrl!)); + }, + child: const Icon(Icons.file_download), + ), + appBar: AppBar( + title: const Text("Attachment"), + centerTitle: true, + actions: [ + IconButton(onPressed: () {}, icon: const Icon(Icons.share)), + ], + ), + body: BlocConsumer(builder: (context,state){ + if(state is EligibilityAttachmentViewState){ + fileUrl = state.fileUrl; + bool isPDF = state.fileUrl[state.fileUrl.length - 1] == 'f' ? true : false; + return SizedBox( + child: isPDF?SfPdfViewer.network( + state.fileUrl,onDocumentLoadFailed: (details) { + Center(child: Text(details.description),); + },): Center( + child: CachedNetworkImage( + progressIndicatorBuilder: (context, url, progress) { + return const SizedBox( + height: 100, + width: 100, + child: CircularProgressIndicator(color: primary,)); + }, + + imageBuilder: (context, imageProvider) => Container( + decoration: BoxDecoration( + image: DecorationImage( + image: imageProvider, fit: BoxFit.fill)), + ), + imageUrl: + state.fileUrl, + width: double.infinity, + height: 220, + fit: BoxFit.cover, + ), + ), + ); + } + return Container(); + },listener: (context, state) { + + },) + ); + } +} diff --git a/lib/screens/profile/components/eligibility_screen.dart b/lib/screens/profile/components/eligibility_screen.dart index 0a51d26..4376fd1 100644 --- a/lib/screens/profile/components/eligibility_screen.dart +++ b/lib/screens/profile/components/eligibility_screen.dart @@ -28,6 +28,8 @@ import '../../../bloc/profile/eligibility/eligibility_bloc.dart'; import '../../../utils/alerts.dart'; import '../shared/multiple_attachment.dart'; import '../shared/single_attachment.dart'; +import '../shared/view_attachment.dart'; +import 'eligibility/eligibility_view_attachment.dart'; class EligibiltyScreen extends StatelessWidget { const EligibiltyScreen({super.key}); @@ -629,6 +631,17 @@ class EligibiltyScreen extends StatelessWidget { ? ////Single Attachment view SingleAttachment( + view: (){ + + Navigator.push( + context, + MaterialPageRoute( + builder: ((context) => BlocProvider.value( + value: EligibilityBloc()..add(EligibiltyViewAttachmentEvent(source: state.eligibilities[index].attachments!.first.source!)), + child: const EligibilityViewAttachment(), + )))); + + }, onpressed: () { confirmAlert( @@ -660,6 +673,15 @@ class EligibiltyScreen extends StatelessWidget { ) ////Multiple Attachments View : MultipleAttachments( + viewAttachment: (source,filename) { + Navigator.push( + context, + MaterialPageRoute( + builder: ((context) => BlocProvider.value( + value: EligibilityBloc()..add(EligibiltyViewAttachmentEvent(source: source)), + child: const EligibilityViewAttachment(), + )))); + }, profileId: profileId!, token: token!, diff --git a/lib/screens/profile/components/learning_and_development_screen.dart b/lib/screens/profile/components/learning_and_development_screen.dart index edb1f31..dbb74c8 100644 --- a/lib/screens/profile/components/learning_and_development_screen.dart +++ b/lib/screens/profile/components/learning_and_development_screen.dart @@ -12,6 +12,7 @@ import 'package:intl/intl.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/screens/profile/components/learning_development/edit_modal.dart'; +import 'package:unit2/screens/profile/components/learning_development/learning_development_view_attachment.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/utils/global.dart'; @@ -27,6 +28,7 @@ import '../../../utils/alerts.dart'; import '../../../widgets/Leadings/close_leading.dart'; import '../shared/multiple_attachment.dart'; import '../shared/single_attachment.dart'; +import '../shared/view_attachment.dart'; import 'learning_development/add_modal.dart'; class LearningAndDevelopmentScreen extends StatelessWidget { @@ -673,6 +675,15 @@ class LearningAndDevelopmentScreen extends StatelessWidget { ? ////Single Attachment view SingleAttachment( + view: (){ + Navigator.push( + context, + MaterialPageRoute( + builder: ((context) => BlocProvider.value( + value: LearningDevelopmentBloc()..add(LearningDevelopmentViewAttachmentEvent(source: state.learningsAndDevelopment[index].attachments!.first.source!)), + child: const LearningDevelopmentViewAttachment(), + )))); + }, onpressed: () { confirmAlert( context, @@ -703,6 +714,17 @@ class LearningAndDevelopmentScreen extends StatelessWidget { ) ////Multiple Attachments View : MultipleAttachments( + viewAttachment: + (source,filename) { + Navigator.push( + context, + MaterialPageRoute( + builder: ((context) => + BlocProvider.value( + value: LearningDevelopmentBloc()..add(LearningDevelopmentViewAttachmentEvent(source: source)), + child: const LearningDevelopmentViewAttachment(), + )))); + }, profileId: profileId, token: token, diff --git a/lib/screens/profile/components/learning_development/learning_development_view_attachment.dart b/lib/screens/profile/components/learning_development/learning_development_view_attachment.dart new file mode 100644 index 0000000..7d07997 --- /dev/null +++ b/lib/screens/profile/components/learning_development/learning_development_view_attachment.dart @@ -0,0 +1,71 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart'; +import 'package:unit2/bloc/profile/learningDevelopment/learning_development_bloc.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:url_launcher/url_launcher.dart'; +class LearningDevelopmentViewAttachment extends StatefulWidget { + + const LearningDevelopmentViewAttachment({super.key}); + + @override + State createState() => _LearningDevelopmentViewAttachmentState(); +} + +class _LearningDevelopmentViewAttachmentState extends State { + @override + Widget build(BuildContext context) { + String? fileUrl; + return Scaffold( + floatingActionButton: FloatingActionButton( + onPressed: ()async { + await launchUrl(Uri.parse(fileUrl!)); + }, + child: const Icon(Icons.file_download), + ), + appBar: AppBar( + title: const Text("Attachment"), + centerTitle: true, + actions: [ + IconButton(onPressed: () {}, icon: const Icon(Icons.share)), + ], + ), + body: BlocConsumer(builder: (context,state){ + if(state is LearningAndDevelopmentAttachmentViewState){ + fileUrl = state.fileUrl; + bool isPDF = state.fileUrl[state.fileUrl.length - 1] == 'f' ? true : false; + return SizedBox( + child: isPDF?SfPdfViewer.network( + state.fileUrl,onDocumentLoadFailed: (details) { + Center(child: Text(details.description),); + },): Center( + child: CachedNetworkImage( + progressIndicatorBuilder: (context, url, progress) { + return const SizedBox( + height: 100, + width: 100, + child: CircularProgressIndicator(color: primary,)); + }, + + imageBuilder: (context, imageProvider) => Container( + decoration: BoxDecoration( + image: DecorationImage( + image: imageProvider, fit: BoxFit.fill)), + ), + imageUrl: + state.fileUrl, + width: double.infinity, + height: 220, + fit: BoxFit.cover, + ), + ), + ); + } + return Container(); + },listener: (context, state) { + + },) + ); + } +} diff --git a/lib/screens/profile/components/work_history/add_modal.dart b/lib/screens/profile/components/work_history/add_modal.dart index 38ca36a..75b0aab 100644 --- a/lib/screens/profile/components/work_history/add_modal.dart +++ b/lib/screens/profile/components/work_history/add_modal.dart @@ -14,7 +14,7 @@ import 'package:unit2/model/utils/category.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/btn-style.dart'; import 'package:unit2/theme-data.dart/colors.dart'; -import 'package:unit2/theme-data.dart/form-style.dart'; +import 'package:unit2/theme-data.dart/form-style.dart'; import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/utils/validators.dart'; @@ -45,6 +45,12 @@ class _AddWorkHistoryScreenState extends State { String? salary; String? salaryGrade; String? salaryGradeStep; + String? accomplishment; + String? duties; + String? sFname; + String? sLname; + String? sMname; + String? sOffice; bool showAgency = false; bool showSalaryGradeAndSalaryStep = false; bool? isPrivate = false; @@ -67,7 +73,9 @@ class _AddWorkHistoryScreenState extends State { @override Widget build(BuildContext context) { + return BlocConsumer( + listener: (context, state) { if (state is AddWorkHistoryState) { final progress = ProgressHUD.of(context); @@ -77,15 +85,14 @@ class _AddWorkHistoryScreenState extends State { if (state is AddWorkHistoryState) { return FormBuilder( key: _formKey, - child: SizedBox( - height: blockSizeVertical * 90, + child: SingleChildScrollView( child: Padding( padding: const EdgeInsets.symmetric(vertical: 32, horizontal: 28), child: Column( + mainAxisAlignment: MainAxisAlignment.start, children: [ - Flexible( - child: ListView( - children: [ + + ////POSITIONS StatefulBuilder(builder: (context, setState) { return SearchField( @@ -93,18 +100,18 @@ class _AddWorkHistoryScreenState extends State { itemHeight: 70, suggestionsDecoration: box1(), suggestions: state.agencyPositions - .map((PositionTitle position) => SearchFieldListItem( - position.title!, - item: position, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10), - child: ListTile( - title: Text( - position.title!, - overflow: TextOverflow.visible, - ), - )))) + .map((PositionTitle position) => + SearchFieldListItem(position.title!, + item: position, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10), + child: ListTile( + title: Text( + position.title!, + overflow: TextOverflow.visible, + ), + )))) .toList(), focusNode: positionFocusNode, searchInputDecoration: @@ -127,11 +134,12 @@ class _AddWorkHistoryScreenState extends State { controller: addPositionController, onpressed: () { setState(() { - PositionTitle newAgencyPosition = PositionTitle( - id: null, - title: addPositionController.text - .toUpperCase()); - + PositionTitle newAgencyPosition = + PositionTitle( + id: null, + title: addPositionController.text + .toUpperCase()); + state.agencyPositions .insert(0, newAgencyPosition); selectedPosition = newAgencyPosition; @@ -170,7 +178,7 @@ class _AddWorkHistoryScreenState extends State { }, onSuggestionTap: (status) { selectedStatus = status.item; - + appointmentStatusNode.unfocus(); }, searchInputDecoration: @@ -182,7 +190,7 @@ class _AddWorkHistoryScreenState extends State { appointmentStatusNode.unfocus(); }, ))), - + const SizedBox( height: 12, ), @@ -226,7 +234,7 @@ class _AddWorkHistoryScreenState extends State { onSuggestionTap: (agency) { setState(() { selectedAgency = agency.item; - + if (selectedAgency!.privateEntity == null) { showIsPrivateRadio = true; @@ -260,19 +268,19 @@ class _AddWorkHistoryScreenState extends State { .toUpperCase(), category: null, privateEntity: null); - + state.agencies.insert(0, newAgency); selectedAgency = newAgency; addAgencyController.text = ""; showAgency = true; - + showIsPrivateRadio = true; - + Navigator.pop(context); }); }, title: "Add Agency")), - + SizedBox( height: showAgency ? 12 : 0, ), @@ -335,7 +343,7 @@ class _AddWorkHistoryScreenState extends State { ) : const SizedBox(), ), - + ////PRVIATE SECTOR SizedBox( child: showIsPrivateRadio @@ -356,7 +364,7 @@ class _AddWorkHistoryScreenState extends State { ], ), ), - + ////onvhange private sector onChanged: (value) { setState(() { @@ -381,7 +389,7 @@ class _AddWorkHistoryScreenState extends State { agencyCategoryFocusNode.unfocus(); }); }, - + name: 'isPrivate', validator: FormBuilderValidators.required(), @@ -476,21 +484,80 @@ class _AddWorkHistoryScreenState extends State { const SizedBox( height: 12, ), - ////MONTHLY SALARY + //// NAME OF OFFICE UNIT FormBuilderTextField( - keyboardType: TextInputType.number, - onChanged: (value) { - setState(() { - salary = value; - }); + onChanged: (value){ + sOffice = value; }, - validator: numericRequired, - name: "salary", - decoration: - normalTextFieldStyle("Monthly Salary *", "") - .copyWith(prefix: const Text("₱ ")), + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: 'office', + decoration: normalTextFieldStyle( + "Name of Office/Unit", "Name of Office/Unit"), ), - + const SizedBox( + height: 12, + ), + + ////MONTHLY SALARY + StatefulBuilder(builder: (context, setState) { + return FormBuilderTextField( + keyboardType: TextInputType.number, + onChanged: (value) { + setState(() { + salary = value; + }); + }, + validator: numericRequired, + name: "salary", + decoration: + normalTextFieldStyle("Monthly Salary *", "") + .copyWith(prefix: const Text("₱ ")), + ); + }), + const SizedBox( + height: 12, + ), + const Text("Immediate SuperVisor"), + const SizedBox( + height: 12, + ), + ////IMMEDIATE SUPERVISOR + FormBuilderTextField( + onChanged: (value){ + sFname = value; + }, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: 'supervisor_firstname', + decoration: + normalTextFieldStyle("First name", "First Name"), + ), + const SizedBox( + height: 12, + ), + FormBuilderTextField( + onChanged: (value){ + sMname = value; + }, + name: 'supervisor_middlename', + decoration: normalTextFieldStyle( + "Middle name", "Middle Name"), + ), + const SizedBox( + height: 12, + ), + FormBuilderTextField( + onChanged: (value){ + sLname = value; + }, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: 'supervisor_lastname', + decoration: + normalTextFieldStyle("Last name", "Last Name"), + ), + const SizedBox( height: 12, ), @@ -595,12 +662,11 @@ class _AddWorkHistoryScreenState extends State { } return true; }, - onChanged: (value) { - setState(() { - to = - DateTime.parse(value); - }); - }, + onChanged: (value) { + setState(() { + to = DateTime.parse(value); + }); + }, initialDate: from == null ? DateTime.now() : from!.add( @@ -624,8 +690,40 @@ class _AddWorkHistoryScreenState extends State { ], ); }), - ], - ), + + const Text("Work Experience"), + const SizedBox( + height: 8, + ), + FormBuilderTextField( + onChanged: (value){ + accomplishment = value; + }, + name: "accomplishment", + decoration: normalTextFieldStyle( + "List of Accomplishment and Contribution", + "", + ), + ), + const SizedBox( + height: 12, + ), + FormBuilderTextField( + onChanged: (value){ + duties = value; + }, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "summary", + decoration: normalTextFieldStyle( + "Summary of Actual Duties", + "", + ), + ), + + + const SizedBox( + height: 20, ), ////SUBMIT BUTTON SizedBox( @@ -635,11 +733,24 @@ class _AddWorkHistoryScreenState extends State { style: mainBtnStyle(primary, Colors.transparent, second), onPressed: () { - print(selectedStatus?.value); + print(salaryGrade); + print(salaryGradeStep); + if (_formKey.currentState!.validate()) { final progress = ProgressHUD.of(context); progress!.showWithText("Loading..."); + WorkHistory workHistory = WorkHistory( + accomplishment: null, + actualDuties: null, + agencydepid: null, + supervisor: Supervisor( + agencyId: selectedAgency?.id, + id: null, + lastname: sLname, + firstname: sFname, + middlename: sMname, + stationName: sOffice), position: selectedPosition, id: null, agency: selectedAgency, @@ -651,15 +762,17 @@ class _AddWorkHistoryScreenState extends State { "PRESENT" ? null : DateTime.parse(toDateController.text), - salaryGrade: salaryGrade == null + salarygrade: salaryGrade == null ? null : int.parse(salaryGrade!), - sgStep: salaryGradeStep == null + sgstep: salaryGradeStep == null ? null : int.parse(salaryGradeStep!), - monthlySalary: double.parse(salary!), - appointmentStatus: selectedStatus!.value); + monthlysalary: double.parse(salary!), + statusAppointment: selectedStatus!.value); context.read().add(AddWorkHostory( + accomplishment: accomplishment, + actualDuties: duties, workHistory: workHistory, profileId: widget.profileId, token: widget.token, diff --git a/lib/screens/profile/components/work_history/edit_modal.dart b/lib/screens/profile/components/work_history/edit_modal.dart index 1045946..5c0f633 100644 --- a/lib/screens/profile/components/work_history/edit_modal.dart +++ b/lib/screens/profile/components/work_history/edit_modal.dart @@ -81,7 +81,7 @@ class _EditWorkHistoryScreenState extends State { if (state is EditWorkHistoryState) { oldPositionController.text = state.workHistory.position!.title!; oldAppointmentStatusController.text = - state.workHistory.appointmentStatus!; + state.workHistory.statusAppointment!; oldAgencyController.text = state.workHistory.agency!.name!; currentlyEmployed = state.workHistory.toDate == null ? true : false; showSalaryGradeAndSalaryStep = @@ -428,7 +428,7 @@ class _EditWorkHistoryScreenState extends State { child: FormBuilderTextField( initialValue: state .workHistory - .salaryGrade + .salarygrade ?.toString(), name: 'salary_grade', keyboardType: @@ -452,7 +452,7 @@ class _EditWorkHistoryScreenState extends State { flex: 1, child: FormBuilderTextField( initialValue: state - .workHistory.sgStep + .workHistory.sgstep ?.toString(), name: 'salary_step', keyboardType: @@ -482,7 +482,7 @@ class _EditWorkHistoryScreenState extends State { ////MONTHLY SALARY FormBuilderTextField( initialValue: - state.workHistory.monthlySalary.toString(), + state.workHistory.monthlysalary.toString(), onChanged: (value) { setState(() { salary = value; @@ -654,50 +654,50 @@ class _EditWorkHistoryScreenState extends State { style: mainBtnStyle(primary, Colors.transparent, second), onPressed: () { - if (_formKey.currentState!.saveAndValidate()) { - final progress = ProgressHUD.of(context); - progress!.showWithText("Loading..."); - salary = _formKey.currentState!.value['salary']; - selectedPosition ??= state.workHistory.position; - salaryGrade = - _formKey.currentState!.value['salary_grade']; - salaryGradeStep = - _formKey.currentState!.value['salary_step']; - selectedAgency ??= state.workHistory.agency; + // if (_formKey.currentState!.saveAndValidate()) { + // final progress = ProgressHUD.of(context); + // progress!.showWithText("Loading..."); + // salary = _formKey.currentState!.value['salary']; + // selectedPosition ??= state.workHistory.position; + // salaryGrade = + // _formKey.currentState!.value['salary_grade']; + // salaryGradeStep = + // _formKey.currentState!.value['salary_step']; + // selectedAgency ??= state.workHistory.agency; - selectedStatus ??= AppoinemtStatus( - value: state.workHistory.appointmentStatus!, - label: state.workHistory.appointmentStatus!); - WorkHistory newWorkHistory = WorkHistory( - id: state.workHistory.id, - position: selectedPosition, - agency: selectedAgency, - fromDate: fromDateController.text.isEmpty - ? null - : DateTime.parse(fromDateController.text), - toDate: toDateController.text.isEmpty || - toDateController.text.toUpperCase() == - "PRESENT" || - toDateController.text.toLowerCase() == - 'null' - ? null - : DateTime.parse(toDateController.text), - monthlySalary: double.parse(salary!), - appointmentStatus: selectedStatus!.value, - salaryGrade: salaryGrade == null - ? null - : int.parse(salaryGrade!), - sgStep: salaryGradeStep == null - ? null - : int.parse(salaryGradeStep!), - ); - context.read().add( - UpdateWorkHistory( - oldWorkHistory: state.workHistory, - profileId: widget.profileId.toString(), - token: widget.token, - workHistory: newWorkHistory)); - } + // selectedStatus ??= AppoinemtStatus( + // value: state.workHistory.statusAppointment!, + // label: state.workHistory.statusAppointment!); + // WorkHistory newWorkHistory = WorkHistory( + // id: state.workHistory.id, + // position: selectedPosition, + // agency: selectedAgency, + // fromDate: fromDateController.text.isEmpty + // ? null + // : DateTime.parse(fromDateController.text), + // toDate: toDateController.text.isEmpty || + // toDateController.text.toUpperCase() == + // "PRESENT" || + // toDateController.text.toLowerCase() == + // 'null' + // ? null + // : DateTime.parse(toDateController.text), + // monthlySalary: double.parse(salary!), + // appointmentStatus: selectedStatus!.value, + // salaryGrade: salaryGrade == null + // ? null + // : int.parse(salaryGrade!), + // sgStep: salaryGradeStep == null + // ? null + // : int.parse(salaryGradeStep!), + // ); + // context.read().add( + // UpdateWorkHistory( + // oldWorkHistory: state.workHistory, + // profileId: widget.profileId.toString(), + // token: widget.token, + // workHistory: newWorkHistory)); + // } }, child: const Text(submit)), ), diff --git a/lib/screens/profile/components/work_history_screen.dart b/lib/screens/profile/components/work_history_screen.dart index 6035f79..9255c4b 100644 --- a/lib/screens/profile/components/work_history_screen.dart +++ b/lib/screens/profile/components/work_history_screen.dart @@ -44,7 +44,7 @@ class WorkHistoryScreen extends StatelessWidget { AttachmentCategory? selectedAttachmentCategory; List attachmentCategories = []; return Scaffold( - resizeToAvoidBottomInset: false, + resizeToAvoidBottomInset: true, appBar: AppBar( title: context.watch().state is AddWorkHistoryState ? const FittedBox(child: Text("Add Work History")) @@ -623,87 +623,87 @@ class WorkHistoryScreen extends StatelessWidget { ]), const Divider(), ////Show Attachments - SizedBox( - child: state - .workExperiences[ - index] - .attachments == - null || - state - .workExperiences[ - index] - .attachments! - .isEmpty - ? const SizedBox() - : state - .workExperiences[ - index] - .attachments != - null && - state - .workExperiences[ - index] - .attachments! - .length == - 1 - ? - ////Single Attachment view - SingleAttachment( - onpressed: () { - confirmAlert( - context, - () { - parent.read().add(DeleteWorkHistoryAttachment( - attachment: state - .workExperiences[ - index] - .attachments! - .first, - moduleId: state - .workExperiences[ - index] - .id!, - profileId: - profileId!, - token: - token!)); - }, "Delete?", - "Confirm Delete?"); - }, - attachment: state - .workExperiences[ - index] - .attachments! - .first, - ) - ////Multiple Attachments View - : MultipleAttachments( - profileId: - profileId!, - token: token!, - moduleId: state - .workExperiences[ - index] - .id!, - educationBloc: - null, - workHistoryBloc: - BlocProvider.of(context), - eligibilityBloc: - null, - learningDevelopmentBloc: - null, - blocId: 3, - eligibilityName: state - .workExperiences[ - index] - .position! - .title!, - attachments: state - .workExperiences[ - index] - .attachments!, - )) + // SizedBox( + // child: state + // .workExperiences[ + // index] + // .attachments == + // null || + // state + // .workExperiences[ + // index] + // .attachments! + // .isEmpty + // ? const SizedBox() + // : state + // .workExperiences[ + // index] + // .attachments != + // null && + // state + // .workExperiences[ + // index] + // .attachments! + // .length == + // 1 + // ? + // ////Single Attachment view + // SingleAttachment( + // onpressed: () { + // confirmAlert( + // context, + // () { + // parent.read().add(DeleteWorkHistoryAttachment( + // attachment: state + // .workExperiences[ + // index] + // .attachments! + // .first, + // moduleId: state + // .workExperiences[ + // index] + // .id!, + // profileId: + // profileId!, + // token: + // token!)); + // }, "Delete?", + // "Confirm Delete?"); + // }, + // attachment: state + // .workExperiences[ + // index] + // .attachments! + // .first, + // ) + // ////Multiple Attachments View + // : MultipleAttachments( + // profileId: + // profileId!, + // token: token!, + // moduleId: state + // .workExperiences[ + // index] + // .id!, + // educationBloc: + // null, + // workHistoryBloc: + // BlocProvider.of(context), + // eligibilityBloc: + // null, + // learningDevelopmentBloc: + // null, + // blocId: 3, + // eligibilityName: state + // .workExperiences[ + // index] + // .position! + // .title!, + // attachments: state + // .workExperiences[ + // index] + // .attachments!, + // )) ], ), ), diff --git a/lib/screens/profile/shared/multiple_attachment.dart b/lib/screens/profile/shared/multiple_attachment.dart index 22b04c3..149cdfb 100644 --- a/lib/screens/profile/shared/multiple_attachment.dart +++ b/lib/screens/profile/shared/multiple_attachment.dart @@ -7,6 +7,7 @@ import 'package:unit2/bloc/profile/education/education_bloc.dart'; import 'package:unit2/bloc/profile/eligibility/eligibility_bloc.dart'; import 'package:unit2/bloc/profile/learningDevelopment/learning_development_bloc.dart'; import 'package:unit2/bloc/profile/workHistory/workHistory_bloc.dart'; +import 'package:unit2/screens/profile/shared/view_attachment.dart'; import 'package:unit2/utils/global_context.dart'; import '../../../model/profile/attachment.dart'; @@ -16,6 +17,7 @@ import '../../../utils/alerts.dart'; import '../../../utils/global.dart'; class MultipleAttachments extends StatelessWidget { + final Function(String source, String fileName) viewAttachment; final List attachments; final String eligibilityName; final EducationBloc? educationBloc; @@ -28,6 +30,7 @@ class MultipleAttachments extends StatelessWidget { final String token; const MultipleAttachments( {super.key, + required this.viewAttachment, required this.blocId, required this.educationBloc, required this.eligibilityBloc, @@ -128,9 +131,14 @@ class MultipleAttachments extends StatelessWidget { flex: 4, child: Tooltip( message: e.filename, - child: Text( - e.filename!, - overflow: TextOverflow.ellipsis, + child: GestureDetector( + onTap: (){ + viewAttachment(e.source!,e.filename!); + }, + child: Text( + e.filename!, + overflow: TextOverflow.ellipsis, + ), )), ), const SizedBox( diff --git a/lib/screens/profile/shared/single_attachment.dart b/lib/screens/profile/shared/single_attachment.dart index 64f9c91..3f790da 100644 --- a/lib/screens/profile/shared/single_attachment.dart +++ b/lib/screens/profile/shared/single_attachment.dart @@ -10,9 +10,11 @@ import '../../../utils/alerts.dart'; class SingleAttachment extends StatelessWidget { final Function()? onpressed; final Attachment attachment; + final Function()? view; const SingleAttachment({ required this.attachment, required this.onpressed, + required this.view, super.key, }); @@ -32,11 +34,14 @@ class SingleAttachment extends StatelessWidget { children: [ Expanded( child: - AutoSizeText( - attachment.filename!, - wrapWords: false, - maxLines: 1, - ), + GestureDetector( + onTap: view, + child: AutoSizeText( + attachment.filename!, + wrapWords: false, + maxLines: 1, + ), + ), ), const SizedBox( width: diff --git a/lib/screens/profile/shared/view_attachment.dart b/lib/screens/profile/shared/view_attachment.dart new file mode 100644 index 0000000..54bd51d --- /dev/null +++ b/lib/screens/profile/shared/view_attachment.dart @@ -0,0 +1,62 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart'; +import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/widgets/error_state.dart'; + +import '../../../utils/urls.dart'; + +class ImageAttachment extends StatefulWidget { + final String imgUrl; + const ImageAttachment({super.key, required this.imgUrl}); + + @override + State createState() => _ImageAttachmentState(); +} + +class _ImageAttachmentState extends State { + @override + Widget build(BuildContext context) { + bool isPDF = widget.imgUrl[widget.imgUrl.length - 1] == 'f' ? true : false; + + return Scaffold( + floatingActionButton: FloatingActionButton( + onPressed: () {}, + child: const Icon(Icons.file_download), + ), + appBar: AppBar( + title: const Text("Attachment"), + centerTitle: true, + actions: [ + IconButton(onPressed: () {}, icon: const Icon(Icons.share)), + ], + ), + body: isPDF + ? SfPdfViewer.network( + '${Url.instance.prefixHost()}://${Url.instance.host()}${widget.imgUrl}',onDocumentLoadFailed: (details) { + Center(child: Text(details.description),); + },) + : Center( + child: CachedNetworkImage( + progressIndicatorBuilder: (context, url, progress) { + return const SizedBox( + height: 100, + width: 100, + child: CircularProgressIndicator(color: primary,)); + }, + + imageBuilder: (context, imageProvider) => Container( + decoration: BoxDecoration( + image: DecorationImage( + image: imageProvider, fit: BoxFit.fill)), + ), + imageUrl: + '${Url.instance.prefixHost()}://${Url.instance.host()}${widget.imgUrl}', + width: double.infinity, + height: 220, + fit: BoxFit.cover, + ), + ), + ); + } +} diff --git a/lib/screens/superadmin/role_assignment.dart/role_assignment_screen.dart b/lib/screens/superadmin/role_assignment.dart/role_assignment_screen.dart index e895170..8feade2 100644 --- a/lib/screens/superadmin/role_assignment.dart/role_assignment_screen.dart +++ b/lib/screens/superadmin/role_assignment.dart/role_assignment_screen.dart @@ -32,6 +32,7 @@ class RbacRoleAssignment extends StatelessWidget { List selectedValueItemRoles = []; return Scaffold( appBar: AppBar( + elevation: 0, centerTitle: true, backgroundColor: primary, title: const Text("User Roles Screenss"), diff --git a/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart b/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart index 3cd3420..c03573d 100644 --- a/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart +++ b/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart @@ -8,13 +8,10 @@ import 'package:unit2/screens/unit2/homepage.dart/components/dashboard/superadmi import 'package:unit2/screens/unit2/homepage.dart/module-screen.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/theme-data.dart/form-style.dart'; -import '../../../../../bloc/rbac/rbac_operations/agency/agency_bloc.dart'; import '../../../../../bloc/role/pass_check/est_point_person/assign_area/assign_area_agency_bloc.dart'; import '../../../../../bloc/role/pass_check/est_point_person/est_point_person_assignable/est_point_person_assinable_role_bloc.dart'; import '../../../../../bloc/role/pass_check/est_point_person/est_point_person_role_assignment/est_role_assignment_bloc.dart'; import '../../../../../bloc/role/pass_check/est_point_person/est_point_person_station/est_point_person_station_bloc.dart'; -import '../../../../../sevices/roles/est_point_person/est_point_person_role_assignment_services.dart'; -import '../../../../superadmin/agency/agency_screen.dart'; import '../../../roles/establishment_point_person/est_point_person_agecies.dart'; import '../../../roles/establishment_point_person/est_point_person_role_member_screen.dart'; import '../../../roles/establishment_point_person/est_point_person_role_under_screen.dart'; @@ -71,7 +68,9 @@ class _DashBoardState extends State { docSmsCards.add(e); } }); - + unit2Cards.forEach((element) { + print("${element.moduleName} - ${element.object.name!} - ${element.roleName} " ); + }); if (superadminCards.length > 3) { tempSuperAdminCards = superadminCards.sublist(0, 4); } @@ -85,6 +84,7 @@ class _DashBoardState extends State { child: ListView( children: [ Column( + mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ ////unit2 module operations @@ -107,7 +107,7 @@ class _DashBoardState extends State { ), Container( child: unit2Cards.isEmpty - ? const SizedBox() + ? const SizedBox.shrink() : GridView.count( shrinkWrap: true, crossAxisCount: 4, @@ -185,7 +185,7 @@ class _DashBoardState extends State { child: FadeInAnimation( child: Container( - child: (e.roleName == 'superadmin' || e.roleName == 'qr code scanner' || e.roleName == 'security guard' || e.roleName == 'establishment point-person' || e.roleName == 'registration in-charge') && e.moduleName == 'unit2' + child: (e.roleName == 'qr code scanner' || e.roleName == 'security guard' || e.roleName == 'establishment point-person' || e.roleName == 'registration in-charge') && e.moduleName == 'unit2' ? CardLabel( icon: iconGenerator(name: e.object.name!), title: e.object.name!.toLowerCase() == 'role based access control' @@ -195,7 +195,7 @@ class _DashBoardState extends State { : e.object.name!, ontap: () { if (e.object.name!.toLowerCase() == 'pass check') { - PassCheckArguments passCheckArguments = PassCheckArguments(roleId: 10, userId: widget.userId); + PassCheckArguments passCheckArguments = PassCheckArguments(roleId: e.roleId, userId: widget.userId); Navigator.pushNamed(context, '/pass-check', arguments: passCheckArguments); } if (e.object.name!.toLowerCase() == 'role based access control') { @@ -210,19 +210,22 @@ class _DashBoardState extends State { })); } if (e.object.name!.toLowerCase() == "role member") { - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) { + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) { return BlocProvider( create: (context) => EstRoleAssignmentBloc()..add(GetEstPointPersonRolesUnder(userId: widget.userId)), - child: EstPointPersonRoleAssignmentScreen(id: widget.userId,), + child: EstPointPersonRoleAssignmentScreen( + id: widget.userId, + ), ); })); } if (e.object.name!.toLowerCase() == 'assignable role') { - Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) { return BlocProvider( create: (context) => EstPointPersonAssinableRoleBloc()..add(const GetEstPointPersonAssignableRoles(roleId: 16)), - child: EstPointPersonRoleUnderScreen(id: widget.userId,), + child: EstPointPersonRoleUnderScreen( + id: widget.userId, + ), ); })); } @@ -421,11 +424,11 @@ class _DashBoardState extends State { ), ), SizedBox( - height: unit2Cards.isEmpty ? 0 : 24, + height: superadminCards.isEmpty ? 0 : 24, ), Container( child: superadminCards.isEmpty - ? const SizedBox() + ? const SizedBox.shrink() : Text( "Superadmin module operations", style: Theme.of(context) @@ -442,7 +445,7 @@ class _DashBoardState extends State { ), Container( child: superadminCards.isEmpty - ? const SizedBox() + ? const SizedBox.shrink() : GridView.count( shrinkWrap: true, crossAxisCount: 4, @@ -602,12 +605,12 @@ class _DashBoardState extends State { columnCount: 4, )); }).toList())), - const SizedBox( - height: 24, + SizedBox( + height: rpassCards.isEmpty?0: 24, ), Container( child: rpassCards.isEmpty - ? const SizedBox() + ? const SizedBox.shrink() : Text( "RPAss module operations", style: Theme.of(context) @@ -624,7 +627,7 @@ class _DashBoardState extends State { ), Container( child: rpassCards.isEmpty - ? const SizedBox() + ? const SizedBox.shrink() : GridView.count( shrinkWrap: true, crossAxisCount: 4, @@ -649,12 +652,12 @@ class _DashBoardState extends State { }).toList(), ), ), - const SizedBox( - height: 24, + SizedBox( + height: docSmsCards.isEmpty?0: 24, ), Container( child: docSmsCards.isEmpty - ? const SizedBox() + ? const SizedBox.shrink() : Text( "DocSMS module operations", style: Theme.of(context) diff --git a/lib/screens/unit2/homepage.dart/components/dashboard/superadmin_expanded_menu.dart b/lib/screens/unit2/homepage.dart/components/dashboard/superadmin_expanded_menu.dart index 52f2d97..dd4c00f 100644 --- a/lib/screens/unit2/homepage.dart/components/dashboard/superadmin_expanded_menu.dart +++ b/lib/screens/unit2/homepage.dart/components/dashboard/superadmin_expanded_menu.dart @@ -77,7 +77,9 @@ class SuperAdminMenu extends StatelessWidget { title: object.object.name!, ontap: () { + if (object.object.name == 'Role') { + Navigator.push(context, MaterialPageRoute( builder: (BuildContext context) { return BlocProvider( diff --git a/lib/screens/unit2/homepage.dart/module-screen.dart b/lib/screens/unit2/homepage.dart/module-screen.dart index 26762d5..5f2046f 100644 --- a/lib/screens/unit2/homepage.dart/module-screen.dart +++ b/lib/screens/unit2/homepage.dart/module-screen.dart @@ -43,6 +43,7 @@ class _MainScreenState extends State { for (var module in role.modules!) { for (var object in module!.objects!) { DisplayCard newCard = DisplayCard( + roleId: role.id!, moduleName: module.name!.toLowerCase(), object: object!, roleName: role.name!.toLowerCase()); @@ -91,8 +92,9 @@ class DisplayCard { final String roleName; final String moduleName; final ModuleObject object; + final int roleId; const DisplayCard( - {required this.moduleName, required this.object, required this.roleName}); + {required this.moduleName, required this.object, required this.roleName,required this.roleId}); } class Module { diff --git a/lib/sevices/profile/work_history_services.dart b/lib/sevices/profile/work_history_services.dart index 068f20c..e46b08d 100644 --- a/lib/sevices/profile/work_history_services.dart +++ b/lib/sevices/profile/work_history_services.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import 'package:intl/intl.dart'; import 'package:unit2/model/profile/work_history.dart'; import 'package:http/http.dart' as http; import 'package:unit2/model/utils/agency_position.dart'; @@ -48,17 +49,17 @@ class WorkHistoryService { bool? success; Map params = {"force_mode": "true"}; String authToken = "Token $token"; - String path = "${Url.instance.workhistory()}$profileId/"; + String path = "${Url.instance.deleteWorkHistory()}$profileId/"; Map body = { "id": work.id, "position_id": work.position!.id, "agency_id": work.agency!.id, "from_date": work.fromDate?.toString(), "to_date": work.toDate?.toString(), - "monthly_salary": work.monthlySalary, - "appointment_status": work.appointmentStatus, - "salary_step": work.sgStep, - "salary_grade": work.salaryGrade, + // "monthly_salary": work.monthlysalary, + // "appointment_status": work.statusAppointment, + // "salary_step": work.sgstep, + // "salary_grade": work.salarygrade, }; Map headers = { 'Content-Type': 'application/json; charset=UTF-8', @@ -82,85 +83,212 @@ class WorkHistoryService { ////edit work history - Future> update({required WorkHistory oldWorkHistory, required WorkHistory newWorkHistory, required String token, required String profileId})async{ - Map? statusResponse={}; - String authtoken = "Token $token"; - String path = '${Url.instance.workhistory()}$profileId/'; - Map headers = { - 'Content-Type': 'application/json; charset=UTF-8', - 'Authorization': authtoken - }; - Map body = { - "id":newWorkHistory.id, - "position_id":newWorkHistory.position!.id, - "agency_id":newWorkHistory.agency!.id, - "from_date":newWorkHistory.fromDate?.toString(), - "to_date":newWorkHistory.toDate?.toString(), - "monthly_salary":newWorkHistory.monthlySalary, - "appointment_status":newWorkHistory.appointmentStatus, - "salary_grade":newWorkHistory.salaryGrade, - "sg_step":newWorkHistory.sgStep, - "_positionName":newWorkHistory.position!.title!, - "_agencyName":newWorkHistory.agency!.name!, - "_agencyCatId":newWorkHistory.agency!.category!.id!, - "_privateEntity":newWorkHistory.agency!.privateEntity, - "oldPosId":oldWorkHistory.position!.id, - "_oldAgencyId":oldWorkHistory.agency!.id, - "oldFromDate":oldWorkHistory.fromDate?.toString(), - }; - try{ - http.Response response = await Request.instance.putRequest(path: path, headers: headers, body: body, param: {}); - if(response.statusCode == 200 ){ - Map data = jsonDecode(response.body); - statusResponse = data; - }else{ - statusResponse.addAll({'success':false}); - } - return statusResponse; - }catch(e){ - throw e.toString(); - } - } + // Future> update({required WorkHistory oldWorkHistory, required WorkHistory newWorkHistory, required String token, required String profileId})async{ + // Map? statusResponse={}; + // String authtoken = "Token $token"; + // String path = '${Url.instance.workhistory()}$profileId/'; + // Map headers = { + // 'Content-Type': 'application/json; charset=UTF-8', + // 'Authorization': authtoken + // }; + // Map body = { + // "id":newWorkHistory.id, + // "position_id":newWorkHistory.position!.id, + // "agency_id":newWorkHistory.agency!.id, + // "from_date":newWorkHistory.fromDate?.toString(), + // "to_date":newWorkHistory.toDate?.toString(), + // "monthly_salary":newWorkHistory.monthlysalary, + // "appointment_status":newWorkHistory.statusAppointment, + // "salary_grade":newWorkHistory.salarygrade, + // "sg_step":newWorkHistory.sgstep, + // "_positionName":newWorkHistory.position!.title!, + // "_agencyName":newWorkHistory.agency!.name!, + // "_agencyCatId":newWorkHistory.agency!.category!.id!, + // "_privateEntity":newWorkHistory.agency!.privateEntity, + // "oldPosId":oldWorkHistory.position!.id, + // "_oldAgencyId":oldWorkHistory.agency!.id, + // "oldFromDate":oldWorkHistory.fromDate?.toString(), + // }; + + + // try{ + // http.Response response = await Request.instance.putRequest(path: path, headers: headers, body: body, param: {}); + // if(response.statusCode == 200 ){ + // Map data = jsonDecode(response.body); + // statusResponse = data; + // }else{ + // statusResponse.addAll({'success':false}); + // } + // return statusResponse; + // }catch(e){ + // throw e.toString(); + // } + // } + + ////Add work history - Future>add({required WorkHistory workHistory, required String token, required int profileId , required bool isPrivate})async{ + Future>add({required WorkHistory workHistory, required String token, required int profileId , required bool isPrivate,required String? accomplishment, required String? actualDuties})async{ String authtoken = "Token $token"; String path = '${Url.instance.workhistory()}$profileId/'; Map headers = { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': authtoken }; + Map body = {}; Map statusResponse = {}; - Map body = { - 'position_id':workHistory.position?.id, - 'agency_id': workHistory.agency?.id, - 'from_date': workHistory.fromDate?.toString(), - 'to_date': workHistory.toDate?.toString(), - 'monthly_salary': workHistory.monthlySalary, - 'appointment_status': workHistory.appointmentStatus, - 'salary_grade': workHistory.salaryGrade, - 'sg_step':workHistory.sgStep, - '_positionName':workHistory.position?.title, - '_agencyName':workHistory.agency?.name, - '_agencyCatId':workHistory.agency?.category?.id, - '_privateEntity':workHistory.agency?.privateEntity, + String fromDate = DateFormat('yyyy-MM-dd').format(workHistory.fromDate!); + String? toDate; + if(workHistory.toDate != null){ + toDate = DateFormat('yyyy-MM-dd').format(workHistory.toDate!); + } + if(workHistory.toDate == null){ + body = { +"a_category_id ": workHistory.agency?.category?.id == null? "":workHistory.agency!.category!.id.toString(), + "a_name" : workHistory.agency?.name == null? "":workHistory.agency!.name!, + " a_private_entity ": workHistory.agency?.privateEntity == null?"":workHistory.agency!.privateEntity.toString(), + "accomplishment" : accomplishment??"", + "actual_duties ": actualDuties!, + "agency_id" : workHistory.agency?.id == null?"": workHistory.agency!.id.toString() , + "from_date" : fromDate, + "monthly_salary" : workHistory.monthlysalary == null? "": workHistory.monthlysalary.toString(), + "position_id" : workHistory.position?.id == null? "": workHistory.position!.id.toString(), + "position_name" : workHistory.position?.title == null? "":workHistory.position!.title!, + "s_fname" : workHistory.supervisor?.firstname == null?"":workHistory.supervisor!.firstname!, + "s_lname" : workHistory.supervisor?.lastname == null? "":workHistory.supervisor!.lastname!, + "s_mname" : workHistory.supervisor?.middlename == null?"":workHistory.supervisor!.middlename!, + "s_office" : workHistory.supervisor?.stationName == null?"":workHistory.supervisor!.stationName!, + "salary_grade" : workHistory.salarygrade == null? "":workHistory.salarygrade.toString(), + "sg_step" : workHistory.sgstep == null?"":workHistory.sgstep.toString() , + 'status_appointment' : workHistory.statusAppointment??"", }; - try{ - http.Response response = await Request.instance.postRequest(path: path,param: {},body: body,headers: headers); - if(response.statusCode == 201){ - Map data = jsonDecode(response.body); + }else{ + body = { +"a_category_id ": workHistory.agency?.category?.id == null? "":workHistory.agency!.category!.id.toString(), + "a_name" : workHistory.agency?.name == null? "":workHistory.agency!.name!, + " a_private_entity ": workHistory.agency?.privateEntity == null?"":workHistory.agency!.privateEntity.toString(), + "accomplishment" : accomplishment??"", + "actual_duties ": actualDuties!, + "agency_id" : workHistory.agency?.id == null?"": workHistory.agency!.id.toString() , + "from_date" : workHistory.fromDate == null? "2018-10-04":"2018-06-04", + "monthly_salary" : workHistory.monthlysalary == null? "": workHistory.monthlysalary.toString(), + "position_id" : workHistory.position?.id == null? "": workHistory.position!.id.toString(), + "position_name" : workHistory.position?.title == null? "":workHistory.position!.title!, + "s_fname" : workHistory.supervisor?.firstname == null?"":workHistory.supervisor!.firstname!, + "s_lname" : workHistory.supervisor?.lastname == null? "":workHistory.supervisor!.lastname!, + "s_mname" : workHistory.supervisor?.middlename == null?"":workHistory.supervisor!.middlename!, + "s_office" : workHistory.supervisor?.stationName == null?"":workHistory.supervisor!.stationName!, + "salary_grade" : workHistory.salarygrade == null? "":workHistory.salarygrade.toString(), + "sg_step" : workHistory.sgstep == null?"":workHistory.sgstep.toString() , + 'status_appointment' : workHistory.statusAppointment??"", + "to_date" : toDate!, + }; + } + + var request = http.MultipartRequest('POST',Uri.parse('${Url.instance.prefixHost()}://${Url.instance.host()}$path')); + request.fields.addAll(body); + request.headers.addAll(headers); + try { + http.StreamedResponse response = await request.send(); + final steamResponse = await response.stream.bytesToString(); + Map data = jsonDecode(steamResponse); + if (response.statusCode == 201) { statusResponse = data; - - }else{ - statusResponse.addAll({'success':false}); + } else { + String message = data['response']['details']; + statusResponse.addAll({'message': message}); + statusResponse.addAll( + {'success': false}, + ); } - return statusResponse; - }catch(e){ + } catch (e) { throw e.toString(); } - +return statusResponse; } +Future>update({required WorkHistory workHistory, required String token, required int profileId , required bool isPrivate,required String? accomplishment, required String? actualDuties})async{ + String authtoken = "Token $token"; + String path = '${Url.instance.workhistory()}$profileId/'; + Map headers = { + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': authtoken + }; + Map body = {}; + Map statusResponse = {}; + String fromDate = DateFormat('yyyy-MM-dd').format(workHistory.fromDate!); + String? toDate; + if(workHistory.toDate != null){ + toDate = DateFormat('yyyy-MM-dd').format(workHistory.toDate!); + } + if(workHistory.toDate == null){ + body = { +"a_category_id ": workHistory.agency?.category?.id == null? "":workHistory.agency!.category!.id.toString(), + "a_name" : workHistory.agency?.name == null? "":workHistory.agency!.name!, + " a_private_entity ": workHistory.agency?.privateEntity == null?"":workHistory.agency!.privateEntity.toString(), + "accomplishment" : accomplishment??"", + "actual_duties ": actualDuties!, + "agency_id" : workHistory.agency?.id == null?"": workHistory.agency!.id.toString() , + "from_date" : fromDate, + "monthly_salary" : workHistory.monthlysalary == null? "": workHistory.monthlysalary.toString(), + "position_id" : workHistory.position?.id == null? "": workHistory.position!.id.toString(), + "position_name" : workHistory.position?.title == null? "":workHistory.position!.title!, + "s_fname" : workHistory.supervisor?.firstname == null?"":workHistory.supervisor!.firstname!, + "s_lname" : workHistory.supervisor?.lastname == null? "":workHistory.supervisor!.lastname!, + "s_mname" : workHistory.supervisor?.middlename == null?"":workHistory.supervisor!.middlename!, + "s_office" : workHistory.supervisor?.stationName == null?"":workHistory.supervisor!.stationName!, + "salary_grade" : workHistory.salarygrade == null? "":workHistory.salarygrade.toString(), + "sg_step" : workHistory.sgstep == null?"":workHistory.sgstep.toString() , + 'status_appointment' : workHistory.statusAppointment??"", + }; + }else{ + body = { +"a_category_id ": workHistory.agency?.category?.id == null? "":workHistory.agency!.category!.id.toString(), + "a_name" : workHistory.agency?.name == null? "":workHistory.agency!.name!, + " a_private_entity ": workHistory.agency?.privateEntity == null?"":workHistory.agency!.privateEntity.toString(), + "accomplishment" : accomplishment??"", + "actual_duties ": actualDuties!, + "agency_id" : workHistory.agency?.id == null?"": workHistory.agency!.id.toString() , + "from_date" : workHistory.fromDate == null? "2018-10-04":"2018-06-04", + "monthly_salary" : workHistory.monthlysalary == null? "": workHistory.monthlysalary.toString(), + "position_id" : workHistory.position?.id == null? "": workHistory.position!.id.toString(), + "position_name" : workHistory.position?.title == null? "":workHistory.position!.title!, + "s_fname" : workHistory.supervisor?.firstname == null?"":workHistory.supervisor!.firstname!, + "s_lname" : workHistory.supervisor?.lastname == null? "":workHistory.supervisor!.lastname!, + "s_mname" : workHistory.supervisor?.middlename == null?"":workHistory.supervisor!.middlename!, + "s_office" : workHistory.supervisor?.stationName == null?"":workHistory.supervisor!.stationName!, + "salary_grade" : workHistory.salarygrade == null? "":workHistory.salarygrade.toString(), + "sg_step" : workHistory.sgstep == null?"":workHistory.sgstep.toString() , + 'status_appointment' : workHistory.statusAppointment??"", + "to_date" : toDate!, + }; + } + + var request = http.MultipartRequest('PUT',Uri.parse('${Url.instance.prefixHost()}://${Url.instance.host()}$path')); + request.fields.addAll(body); + request.headers.addAll(headers); + try { + http.StreamedResponse response = await request.send(); + final steamResponse = await response.stream.bytesToString(); + Map data = jsonDecode(steamResponse); + if (response.statusCode == 201) { + statusResponse = data; + } else { + String message = data['response']['details']; + statusResponse.addAll({'message': message}); + statusResponse.addAll( + {'success': false}, + ); + } + } catch (e) { + throw e.toString(); + } +return statusResponse; + } + + + + ////get agency position Future> getAgencyPosition() async { List agencyPositions = []; diff --git a/lib/sevices/roles/pass_check_services.dart b/lib/sevices/roles/pass_check_services.dart index 8ff5c22..3b076a6 100644 --- a/lib/sevices/roles/pass_check_services.dart +++ b/lib/sevices/roles/pass_check_services.dart @@ -32,7 +32,7 @@ class PassCheckServices { 'X-Client-Key': xClientKey, 'X-Client-Secret': xClientSecret }; - // try { + try { http.Response response = await Request.instance .getRequest(param: params, headers: headers, path: path); if (response.statusCode == 200) { @@ -133,9 +133,9 @@ class PassCheckServices { statusResponse = assignedArea; } } - // } catch (e) { - // throw e.toString(); - // } + } catch (e) { + throw e.toString(); + } return statusResponse!; } diff --git a/lib/utils/attachment_services.dart b/lib/utils/attachment_services.dart index a871c59..d09e6ba 100644 --- a/lib/utils/attachment_services.dart +++ b/lib/utils/attachment_services.dart @@ -1,6 +1,10 @@ import 'dart:convert'; +import 'package:dio/dio.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:share_plus/share_plus.dart'; import 'package:unit2/utils/request.dart'; +import 'package:unit2/utils/request_permission.dart'; import 'package:unit2/utils/urls.dart'; import '../model/profile/attachment.dart'; @@ -12,7 +16,7 @@ class AttachmentServices { Future> getCategories() async { List attachmentCategories = []; - String path = Url.instance.attachmentCategories()+"11232"; + String path = Url.instance.attachmentCategories(); Map headers = { 'Content-Type': 'application/json; charset=UTF-8', }; @@ -113,7 +117,7 @@ class AttachmentServices { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': authtoken }; - // try { + try { http.Response response = await Request.instance.deleteRequest( path: path, headers: headers, body: body, param: params); if (response.statusCode == 200) { @@ -122,9 +126,29 @@ class AttachmentServices { } else { success = false; } - // } catch (e) { - // throw (e.toString()); - // } + } catch (e) { + throw (e.toString()); + } return success!; } + + Future downloadAttachment( + {required String filename, + required String source, + required String downLoadDir}) async { + bool success = false; + var dio = Dio(); + Response response; + try { + if (await requestPermission(Permission.storage)) { + response = await dio.download(source, "$downLoadDir/$filename"); + if (response.statusCode == 200) { + success = true; + } + } + return success; + } catch (e) { + throw e.toString(); + } + } } diff --git a/lib/utils/request.dart b/lib/utils/request.dart index 329b3c4..33c7a12 100644 --- a/lib/utils/request.dart +++ b/lib/utils/request.dart @@ -19,7 +19,7 @@ class Request { Map? param}) async { Response response; try { - response = await get(Uri.http(host, path!, param), headers: headers) + response = await get(Uri.https(host, path!, param), headers: headers) .timeout(Duration(seconds: requestTimeout)); } on TimeoutException catch (_) { Fluttertoast.showToast( @@ -61,7 +61,7 @@ class Request { Map? param}) async { Response response; try { - response = await post(Uri.http(host, path!, param), + response = await post(Uri.https(host, path!, param), headers: headers, body: jsonEncode(body)) .timeout(Duration(seconds: requestTimeout)); } on TimeoutException catch (_) { @@ -104,7 +104,7 @@ class Request { required Map? param}) async { Response response; try { - response =await put(Uri.http(host,path,param),headers: headers,body: jsonEncode(body)); + response =await put(Uri.https(host,path,param),headers: headers,body: jsonEncode(body)); } on TimeoutException catch (_) { Fluttertoast.showToast( msg: timeoutError, @@ -186,7 +186,7 @@ class Request { required Map? param}) async { Response response; try { - response = await delete(Uri.http(host, path, param), + response = await delete(Uri.https(host, path, param), headers: headers, body: jsonEncode(body)) .timeout(Duration(seconds: requestTimeout)); } on TimeoutException catch (_) { diff --git a/lib/utils/request_permission.dart b/lib/utils/request_permission.dart new file mode 100644 index 0000000..1d3355c --- /dev/null +++ b/lib/utils/request_permission.dart @@ -0,0 +1,14 @@ + +import 'package:permission_handler/permission_handler.dart'; + +Future requestPermission(Permission permission) async { + if (await permission.isGranted) { + return true; + } else { + var result = await permission.request(); + if (result == PermissionStatus.granted) { + return true; + } + } + return false; +} \ No newline at end of file diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index cc22b4f..a2f27e4 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -5,13 +5,17 @@ class Url { String host() { // return '192.168.10.183:3000'; - // return 'agusandelnorte.gov.ph'; - return "192.168.10.219:3000"; + return 'agusandelnorte.gov.ph'; + // return "192.168.10.219:3000"; // return "192.168.10.241"; // return "192.168.10.221:3004"; // return "playweb.agusandelnorte.gov.ph"; // return 'devapi.agusandelnorte.gov.ph:3004'; } + String prefixHost(){ + return "https"; + // return "https"; + } String authentication() { return '/api/account/auth/login/'; @@ -58,7 +62,10 @@ class Url { //// work history paths String workhistory() { - return "/api/jobnet_app/profile/pds/work/"; + return "/api/jobnet_app/profile/pds/work_experience/"; + } + String deleteWorkHistory(){ + return "/api/jobnet_app/profile/pds/work/"; } String getPositions() { diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 41d8796..fcad7d4 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -9,6 +9,7 @@ #include #include #include +#include void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar = @@ -20,4 +21,7 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) platform_device_id_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "PlatformDeviceIdLinuxPlugin"); platform_device_id_linux_plugin_register_with_registrar(platform_device_id_linux_registrar); + 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_plugins.cmake b/linux/flutter/generated_plugins.cmake index 64b5f1f..54e72c5 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST audioplayers_linux modal_progress_hud_nsn platform_device_id_linux + url_launcher_linux ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index fa11536..b1f7d47 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -8,6 +8,7 @@ import Foundation import assets_audio_player import assets_audio_player_web import audioplayers_darwin +import device_info_plus import location import modal_progress_hud_nsn import package_info_plus @@ -15,13 +16,17 @@ import path_provider_foundation import platform_device_id import platform_device_id_macos import rive_common +import share_plus import shared_preferences_foundation import sqflite +import syncfusion_pdfviewer_macos +import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { AssetsAudioPlayerPlugin.register(with: registry.registrar(forPlugin: "AssetsAudioPlayerPlugin")) AssetsAudioPlayerWebPlugin.register(with: registry.registrar(forPlugin: "AssetsAudioPlayerWebPlugin")) AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin")) + DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) LocationPlugin.register(with: registry.registrar(forPlugin: "LocationPlugin")) ModalProgressHudNsnPlugin.register(with: registry.registrar(forPlugin: "ModalProgressHudNsnPlugin")) FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) @@ -29,6 +34,9 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { PlatformDeviceIdMacosPlugin.register(with: registry.registrar(forPlugin: "PlatformDeviceIdMacosPlugin")) PlatformDeviceIdMacosPlugin.register(with: registry.registrar(forPlugin: "PlatformDeviceIdMacosPlugin")) RivePlugin.register(with: registry.registrar(forPlugin: "RivePlugin")) + SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) + SyncfusionFlutterPdfViewerPlugin.register(with: registry.registrar(forPlugin: "SyncfusionFlutterPdfViewerPlugin")) + UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index 3544957..9ce9322 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -337,6 +337,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "0b0036e8cccbfbe0555fd83c1d31a6f30b77a96b598b35a5d36dd41f718695e9" + url: "https://pub.dev" + source: hosted + version: "0.3.3+4" crypto: dependency: transitive description: @@ -385,6 +393,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" + device_info_plus: + dependency: transitive + description: + name: device_info_plus + sha256: f52ab3b76b36ede4d135aab80194df8925b553686f0fa12226b4e2d658e45903 + url: "https://pub.dev" + source: hosted + version: "8.2.2" + device_info_plus_platform_interface: + dependency: transitive + description: + name: device_info_plus_platform_interface + sha256: d3b01d5868b50ae571cd1dc6e502fc94d956b665756180f7b16ead09e836fd64 + url: "https://pub.dev" + source: hosted + version: "7.0.0" device_preview: dependency: "direct main" description: @@ -1253,6 +1277,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.8" + share_plus: + dependency: "direct main" + description: + name: share_plus + sha256: "6cec740fa0943a826951223e76218df002804adb588235a8910dc3d6b0654e11" + url: "https://pub.dev" + source: hosted + version: "7.1.0" + share_plus_platform_interface: + dependency: transitive + description: + name: share_plus_platform_interface + sha256: "357412af4178d8e11d14f41723f80f12caea54cf0d5cd29af9dcdab85d58aea7" + url: "https://pub.dev" + source: hosted + version: "3.3.0" shared_preferences: dependency: transitive description: @@ -1418,6 +1458,62 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + syncfusion_flutter_core: + dependency: transitive + description: + name: syncfusion_flutter_core + sha256: "8db8f55c77f56968681447d3837c10f27a9e861e238a898fda116c7531def979" + url: "https://pub.dev" + source: hosted + version: "21.2.10" + syncfusion_flutter_pdf: + dependency: transitive + description: + name: syncfusion_flutter_pdf + sha256: a42186922a416c2c9634a8f221aee261101babc2d30b1a1e908a7f034e743046 + url: "https://pub.dev" + source: hosted + version: "21.2.4" + syncfusion_flutter_pdfviewer: + dependency: "direct main" + description: + name: syncfusion_flutter_pdfviewer + sha256: "2dc016f251c675f8e4923135c485356473b4d89c677670164292159cd1dd4f45" + url: "https://pub.dev" + source: hosted + version: "21.2.3" + syncfusion_pdfviewer_macos: + dependency: transitive + description: + name: syncfusion_pdfviewer_macos + sha256: "8cc925cae532c0fa17e849165796d962107f45b86e66ee334dcaabf6b7305c82" + url: "https://pub.dev" + source: hosted + version: "21.2.10" + syncfusion_pdfviewer_platform_interface: + dependency: transitive + description: + name: syncfusion_pdfviewer_platform_interface + sha256: "08039ecdb8f79454fb367c6bf5a833846a666039415d2b5d76a7e59a5b3ff710" + url: "https://pub.dev" + source: hosted + version: "21.2.10" + syncfusion_pdfviewer_web: + dependency: transitive + description: + name: syncfusion_pdfviewer_web + sha256: "8e5ed0d313a1aa3869e4f2e8d079bc9bfa37ce79d91be7bb328e456f37b7995f" + url: "https://pub.dev" + source: hosted + version: "21.2.10" + syncfusion_pdfviewer_windows: + dependency: transitive + description: + name: syncfusion_pdfviewer_windows + sha256: "3e93f281135fb0562f7e6c343d2db741cf3cbd78c5b04884eef9af414408bc77" + url: "https://pub.dev" + source: hosted + version: "21.2.10" synchronized: dependency: transitive description: @@ -1474,6 +1570,70 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + sha256: eb1e00ab44303d50dd487aab67ebc575456c146c6af44422f9c13889984c00f3 + url: "https://pub.dev" + source: hosted + version: "6.1.11" + url_launcher_android: + dependency: "direct main" + description: + name: url_launcher_android + sha256: "3dd2388cc0c42912eee04434531a26a82512b9cb1827e0214430c9bcbddfe025" + url: "https://pub.dev" + source: hosted + version: "6.0.38" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: "9af7ea73259886b92199f9e42c116072f05ff9bea2dcb339ab935dfc957392c2" + url: "https://pub.dev" + source: hosted + version: "6.1.4" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: "207f4ddda99b95b4d4868320a352d374b0b7e05eefad95a4a26f57da413443f5" + url: "https://pub.dev" + source: hosted + version: "3.0.5" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: "1c4fdc0bfea61a70792ce97157e5cc17260f61abbe4f39354513f39ec6fd73b1" + url: "https://pub.dev" + source: hosted + version: "3.0.6" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: bfdfa402f1f3298637d71ca8ecfe840b4696698213d5346e9d12d4ab647ee2ea + url: "https://pub.dev" + source: hosted + version: "2.1.3" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: cc26720eefe98c1b71d85f9dc7ef0cada5132617046369d9dc296b3ecaa5cbb4 + url: "https://pub.dev" + source: hosted + version: "2.0.18" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "7967065dd2b5fccc18c653b97958fdf839c5478c28e767c61ee879f4e7882422" + url: "https://pub.dev" + source: hosted + version: "3.0.7" uuid: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 9ac7efd..f87cecc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -89,6 +89,10 @@ dependencies: file_picker: ^5.3.1 expandable: ^5.0.1 flutter_simple_treeview: ^3.0.2 + syncfusion_flutter_pdfviewer: ^21.2.3 + url_launcher: ^6.1.11 + url_launcher_android: ^6.0.38 + share_plus: ^7.1.0 diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 362613e..7af50e4 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -11,6 +11,9 @@ #include #include #include +#include +#include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { AudioplayersWindowsPluginRegisterWithRegistrar( @@ -23,4 +26,10 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("PlatformDeviceIdWindowsPlugin")); RivePluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("RivePlugin")); + SharePlusWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); + SyncfusionPdfviewerWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SyncfusionPdfviewerWindowsPlugin")); + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 8c00e3e..6d2ce89 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -8,6 +8,9 @@ list(APPEND FLUTTER_PLUGIN_LIST permission_handler_windows platform_device_id_windows rive_common + share_plus + syncfusion_pdfviewer_windows + url_launcher_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST From 4a5d6afcbc6f96441fedb26685f2c5211fed773f Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Wed, 30 Aug 2023 09:55:03 +0800 Subject: [PATCH 84/86] fix bugs for profile and final test in live --- .../profile/education/education_bloc.dart | 4 +- .../profile/eligibility/eligibility_bloc.dart | 43 +- .../eligibility/eligibility_event.dart | 12 +- .../eligibility/eligibility_state.dart | 8 +- .../learning_development_bloc.dart | 42 +- .../learning_development_event.dart | 9 +- .../learning_development_state.dart | 9 +- .../contact/contact_bloc.dart | 62 +- .../contact/contact_event.dart | 58 +- .../profile/workHistory/workHistory_bloc.dart | 52 +- .../workHistory/workHistory_event.dart | 5 +- .../basic_information/address/add_modal.dart | 650 ++-- .../basic_information/address/edit_modal.dart | 875 +++--- .../basic_information/address_screen.dart | 2 +- .../contact_information/add_modal.dart | 366 ++- .../contact_information/edit_modal.dart | 415 ++- .../contact_information_screen.dart | 2 +- .../family/child_add_modal.dart | 2 +- .../family/child_edit_modal.dart | 4 +- .../family/father_add_modal.dart | 2 +- .../family/father_edit_modal.dart | 2 +- .../family/mother_add_modal.dart | 2 +- .../family/mother_edit_modal.dart | 2 +- .../family/related_add_modal.dart | 2 +- .../family/related_edit_modal.dart | 2 +- .../identification/add_modal.dart | 1276 ++++---- .../identification/edit_modal.dart | 699 +++-- .../identification_information_screen.dart | 6 +- .../components/education/add_modal.dart | 707 +++-- .../components/education/edit_modal.dart | 779 +++-- .../education/education_view_attachment.dart | 21 +- .../profile/components/education_screen.dart | 3 +- .../components/eligibility/add_modal.dart | 652 ++-- .../components/eligibility/edit_modal.dart | 850 +++-- .../eligibility_view_attachment.dart | 144 +- .../components/eligibility_screen.dart | 7 +- .../components/family_background_screen.dart | 26 +- .../learning_and_development_screen.dart | 6 +- .../learning_development/add_modal.dart | 2742 ++++++++--------- .../learning_development/edit_modal.dart | 2686 ++++++++-------- .../learning_development_view_attachment.dart | 140 +- .../non_academic/edit_modal.dart | 2 + .../components/reference/add_modal.dart | 612 ++-- .../components/reference/edit_modal.dart | 913 +++--- .../profile/components/references_screen.dart | 2 +- .../components/voluntary_works/add_modal.dart | 1057 ++++--- .../voluntary_works/edit_modal.dart | 31 +- .../components/work_history/add_modal.dart | 52 +- .../components/work_history/edit_modal.dart | 219 +- .../components/work_history_screen.dart | 28 +- .../profile/shared/view_attachment.dart | 62 - lib/sevices/profile/education_services.dart | 4 +- lib/sevices/profile/profile_other_info.dart | 2 +- .../profile/work_history_services.dart | 394 ++- lib/utils/url_launcher_file_downloader.dart | 16 + macos/Podfile.lock | 24 + 56 files changed, 8515 insertions(+), 8279 deletions(-) delete mode 100644 lib/screens/profile/shared/view_attachment.dart create mode 100644 lib/utils/url_launcher_file_downloader.dart diff --git a/lib/bloc/profile/education/education_bloc.dart b/lib/bloc/profile/education/education_bloc.dart index c10fa17..dd7374c 100644 --- a/lib/bloc/profile/education/education_bloc.dart +++ b/lib/bloc/profile/education/education_bloc.dart @@ -241,11 +241,11 @@ class EducationBloc extends Bloc { final result = await Share.shareXFiles( [XFile("$appDocumentPath/${event.fileName}")]); if (result.status == ShareResultStatus.success) { - Fluttertoast.showToast(msg: "Attachment shared successfully"); + Fluttertoast.showToast(msg: "Attachment shared successful"); emit(EducationAttachmentViewState( fileUrl: event.source, fileName: event.fileName)); } else { - Fluttertoast.showToast(msg: "Attachment shared unsuccessfully"); + Fluttertoast.showToast(msg: "Attachment shared unsuccessful"); emit(EducationAttachmentViewState( fileUrl: event.source, fileName: event.fileName)); } diff --git a/lib/bloc/profile/eligibility/eligibility_bloc.dart b/lib/bloc/profile/eligibility/eligibility_bloc.dart index 6710f47..a714bc9 100644 --- a/lib/bloc/profile/eligibility/eligibility_bloc.dart +++ b/lib/bloc/profile/eligibility/eligibility_bloc.dart @@ -1,5 +1,12 @@ +import 'dart:io'; + import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:share_plus/share_plus.dart'; +import 'package:unit2/screens/profile/components/eligibility/eligibility_view_attachment.dart'; import 'package:unit2/utils/attachment_services.dart'; import '../../../model/location/city.dart'; import '../../../model/location/country.dart'; @@ -11,6 +18,7 @@ import '../../../model/utils/eligibility.dart'; import '../../../sevices/profile/eligibility_services.dart'; import '../../../utils/location_utilities.dart'; import '../../../utils/profile_utilities.dart'; +import '../../../utils/request_permission.dart'; import '../../../utils/urls.dart'; part 'eligibility_event.dart'; part 'eligibility_state.dart'; @@ -283,7 +291,40 @@ class EligibilityBloc extends Bloc { }); on((event,emit){ String fileUrl = '${Url.instance.prefixHost()}://${Url.instance.host()}${event.source}'; - emit(EligibilityAttachmentViewState(fileUrl: fileUrl)); + emit(EligibilityAttachmentViewState(fileUrl: fileUrl,fileName: event.filename)); + }); + on((event, emit) async { + emit(EligibilityLoadingState()); + Directory directory; + String? appDocumentPath; + if (await requestPermission(Permission.storage)) { + directory = await getApplicationDocumentsDirectory(); + appDocumentPath = directory.path; + } + try{ + final bool success = await AttachmentServices.instance.downloadAttachment( + filename: event.fileName, + source: event.source, + downLoadDir: appDocumentPath!); + if (success) { + final result = await Share.shareXFiles( + [XFile("$appDocumentPath/${event.fileName}")]); + if (result.status == ShareResultStatus.success) { + Fluttertoast.showToast(msg: "Attachment shared successful"); + emit(EligibilityAttachmentViewState( + fileUrl: event.source, fileName: event.fileName)); + } else { + Fluttertoast.showToast(msg: "Attachment shared unsuccessful"); + emit(EligibilityAttachmentViewState( + fileUrl: event.source, fileName: event.fileName)); + } + } else { + emit(EligibilityAttachmentViewState( + fileUrl: event.source, fileName: event.fileName)); + } + }catch(e){ +emit(EligibilityErrorState(message: e.toString())); + } }); } } diff --git a/lib/bloc/profile/eligibility/eligibility_event.dart b/lib/bloc/profile/eligibility/eligibility_event.dart index 925b105..fab7746 100644 --- a/lib/bloc/profile/eligibility/eligibility_event.dart +++ b/lib/bloc/profile/eligibility/eligibility_event.dart @@ -103,13 +103,15 @@ class DeleteEligibyAttachment extends EligibilityEvent { required this.token}); } -class EligibilityViewAttachments extends EligibilityEvent { - final String source; - const EligibilityViewAttachments({required this.source}); -} class EligibiltyViewAttachmentEvent extends EligibilityEvent{ final String source; - const EligibiltyViewAttachmentEvent({required this.source}); + final String filename; + const EligibiltyViewAttachmentEvent({required this.source, required this.filename}); +} +class ShareAttachment extends EligibilityEvent{ + final String fileName; + final String source; + const ShareAttachment({required this.fileName, required this.source}); } diff --git a/lib/bloc/profile/eligibility/eligibility_state.dart b/lib/bloc/profile/eligibility/eligibility_state.dart index 6f8976c..9f6a005 100644 --- a/lib/bloc/profile/eligibility/eligibility_state.dart +++ b/lib/bloc/profile/eligibility/eligibility_state.dart @@ -109,6 +109,12 @@ class EligibilitytAttachmentDeletedState extends EligibilityState { } class EligibilityAttachmentViewState extends EligibilityState { + final String fileName; final String fileUrl; - const EligibilityAttachmentViewState({required this.fileUrl}); + const EligibilityAttachmentViewState({required this.fileUrl, required this.fileName}); } +class EligibilityAttachmentShareState extends EligibilityState{ + final bool success; + const EligibilityAttachmentShareState({required this.success,}); +} + diff --git a/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart b/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart index 9dd75b7..ad53010 100644 --- a/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart +++ b/lib/bloc/profile/learningDevelopment/learning_development_bloc.dart @@ -1,5 +1,11 @@ +import 'dart:io'; + import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:share_plus/share_plus.dart'; import 'package:unit2/model/location/country.dart'; import 'package:unit2/model/location/region.dart'; import 'package:unit2/sevices/profile/learningDevelopment_service.dart'; @@ -13,6 +19,7 @@ import '../../../model/utils/category.dart'; import '../../../utils/attachment_services.dart'; import '../../../utils/location_utilities.dart'; import '../../../utils/profile_utilities.dart'; +import '../../../utils/request_permission.dart'; import '../../../utils/urls.dart'; part 'learning_development_event.dart'; part 'learning_development_state.dart'; @@ -328,7 +335,40 @@ class LearningDevelopmentBloc }); on((event,emit){ String fileUrl = '${Url.instance.prefixHost()}://${Url.instance.host()}${event.source}'; - emit(LearningAndDevelopmentAttachmentViewState(fileUrl: fileUrl)); + emit(LearningAndDevelopmentAttachmentViewState(fileUrl: fileUrl, filename: event.filename)); + }); + on((event, emit) async { + emit(LearningDevelopmentLoadingState()); + Directory directory; + String? appDocumentPath; + if (await requestPermission(Permission.storage)) { + directory = await getApplicationDocumentsDirectory(); + appDocumentPath = directory.path; + } + try{ + final bool success = await AttachmentServices.instance.downloadAttachment( + filename: event.fileName, + source: event.source, + downLoadDir: appDocumentPath!); + if (success) { + final result = await Share.shareXFiles( + [XFile("$appDocumentPath/${event.fileName}")]); + if (result.status == ShareResultStatus.success) { + Fluttertoast.showToast(msg: "Attachment shared successful"); + emit(LearningAndDevelopmentAttachmentViewState( + fileUrl: event.source, filename: event.fileName)); + } else { + Fluttertoast.showToast(msg: "Attachment shared unsuccessful"); + emit(LearningAndDevelopmentAttachmentViewState( + fileUrl: event.source, filename: event.fileName)); + } + } else { + emit(LearningAndDevelopmentAttachmentViewState( + fileUrl: event.source, filename: event.fileName)); + } + }catch(e){ +emit(LearningDevelopmentErrorState(message: e.toString())); + } }); } diff --git a/lib/bloc/profile/learningDevelopment/learning_development_event.dart b/lib/bloc/profile/learningDevelopment/learning_development_event.dart index d450d7d..e134560 100644 --- a/lib/bloc/profile/learningDevelopment/learning_development_event.dart +++ b/lib/bloc/profile/learningDevelopment/learning_development_event.dart @@ -89,8 +89,15 @@ class DeleteLearningDevAttachment extends LearningDevelopmentEvent{ } class LearningDevelopmentViewAttachmentEvent extends LearningDevelopmentEvent{ + final String filename; final String source; - const LearningDevelopmentViewAttachmentEvent({required this.source}); + const LearningDevelopmentViewAttachmentEvent({required this.source, required this.filename}); +} + +class ShareAttachment extends LearningDevelopmentEvent{ + final String fileName; + final String source; + const ShareAttachment({required this.fileName, required this.source}); } diff --git a/lib/bloc/profile/learningDevelopment/learning_development_state.dart b/lib/bloc/profile/learningDevelopment/learning_development_state.dart index 7507e9c..a5dbb78 100644 --- a/lib/bloc/profile/learningDevelopment/learning_development_state.dart +++ b/lib/bloc/profile/learningDevelopment/learning_development_state.dart @@ -136,6 +136,11 @@ class LearningDevAttachmentDeletedState extends LearningDevelopmentState { class LearningAndDevelopmentAttachmentViewState extends LearningDevelopmentState { + final String filename; final String fileUrl; - const LearningAndDevelopmentAttachmentViewState({required this.fileUrl}); -} \ No newline at end of file + const LearningAndDevelopmentAttachmentViewState({required this.fileUrl, required this.filename}); +} +class LearningDevelopmentAttachmentShareState extends LearningDevelopmentState{ + final bool success; + const LearningDevelopmentAttachmentShareState({required this.success,}); +} diff --git a/lib/bloc/profile/primary_information/contact/contact_bloc.dart b/lib/bloc/profile/primary_information/contact/contact_bloc.dart index e2e1ba2..2d34f51 100644 --- a/lib/bloc/profile/primary_information/contact/contact_bloc.dart +++ b/lib/bloc/profile/primary_information/contact/contact_bloc.dart @@ -46,24 +46,25 @@ class ContactBloc extends Bloc { ServiceType serviceType; List commServiceProvivers; CommService serviceProvider; - try{ - if (serviceTypes.isEmpty) { - serviceTypes = await ProfileUtilities.instance.getServiceType(); - } - serviceType = serviceTypes.firstWhere((ServiceType element) { - return element.id == event.contactInfo.commService!.serviceType!.id; - }); - commServiceProvivers = await ContactService.instance - .getServiceProvider(serviceTypeId: serviceType.id!); - serviceProvider = commServiceProvivers.firstWhere((CommService element) => - element.id == event.contactInfo.commService!.id); - emit(ContactEditingState( - serviceTypes: serviceTypes, - selectedServiceType: serviceType, - commServiceProviders: commServiceProvivers, - selectedProvider: serviceProvider, - contactInfo: event.contactInfo)); - }catch(e){ + try { + if (serviceTypes.isEmpty) { + serviceTypes = await ProfileUtilities.instance.getServiceType(); + } + serviceType = serviceTypes.firstWhere((ServiceType element) { + return element.id == event.contactInfo.commService!.serviceType!.id; + }); + commServiceProvivers = await ContactService.instance + .getServiceProvider(serviceTypeId: serviceType.id!); + serviceProvider = commServiceProvivers.firstWhere( + (CommService element) => + element.id == event.contactInfo.commService!.id); + emit(ContactEditingState( + serviceTypes: serviceTypes, + selectedServiceType: serviceType, + commServiceProviders: commServiceProvivers, + selectedProvider: serviceProvider, + contactInfo: event.contactInfo)); + } catch (e) { emit(ContactErrorState(message: e.toString())); } }); @@ -81,18 +82,17 @@ class ContactBloc extends Bloc { contactInformations.removeWhere( (ContactInfo element) => element.id == event.contactInfo.id); contactInformations.add(contactInfo); - emit(ContactEditedState( - - response: responseStatus)); + emit(ContactEditedState(response: responseStatus)); } else { - emit(ContactEditedState( - - response: responseStatus)); + emit(ContactEditedState(response: responseStatus)); } } catch (e) { emit(ContactErrorState(message: e.toString())); } }); + on((event, emit) { + emit(ContactErrorState(message: event.message)); + }); //// add contact @@ -107,13 +107,9 @@ class ContactBloc extends Bloc { ContactInfo contactInfo = ContactInfo.fromJson(responseStatus['data']['contact_info']); contactInformations.add(contactInfo); - emit(ContactAddedState( - - response: responseStatus)); + emit(ContactAddedState(response: responseStatus)); } else { - emit(ContactAddedState( - - response: responseStatus)); + emit(ContactAddedState(response: responseStatus)); } } catch (e) { emit(ContactErrorState(message: e.toString())); @@ -129,11 +125,9 @@ class ContactBloc extends Bloc { if (success) { contactInformations .removeWhere((element) => element.id == event.contactInfo.id); - emit(ContactDeletedState( - succcess: success)); + emit(ContactDeletedState(succcess: success)); } else { - emit(ContactDeletedState( - succcess: success)); + emit(ContactDeletedState(succcess: success)); } } catch (e) { emit(ContactErrorState(message: e.toString())); diff --git a/lib/bloc/profile/primary_information/contact/contact_event.dart b/lib/bloc/profile/primary_information/contact/contact_event.dart index 5e7106f..48a1692 100644 --- a/lib/bloc/profile/primary_information/contact/contact_event.dart +++ b/lib/bloc/profile/primary_information/contact/contact_event.dart @@ -8,63 +8,71 @@ abstract class ContactEvent extends Equatable { } ////get contacts -class GetContacts extends ContactEvent{ - final List contactInformations; +class GetContacts extends ContactEvent { + final List contactInformations; const GetContacts({required this.contactInformations}); - @override + @override List get props => []; } //// load contacts -class LoadContacts extends ContactEvent{ - - @override +class LoadContacts extends ContactEvent { + @override List get props => []; } //// show add form -class ShowAddForm extends ContactEvent{ +class ShowAddForm extends ContactEvent {} +class CallErrorEvent extends ContactEvent { + final String message; + const CallErrorEvent({required this.message}); } //// show edit form -class ShowEditForm extends ContactEvent{ +class ShowEditForm extends ContactEvent { final ContactInfo contactInfo; const ShowEditForm({required this.contactInfo}); - @override + @override List get props => [contactInfo]; - } ////add event -class AddContactInformation extends ContactEvent{ +class AddContactInformation extends ContactEvent { final int profileId; final String token; final ContactInfo contactInfo; - const AddContactInformation({required this.contactInfo, required this.profileId, required this.token}); - @override - List get props => [profileId,token,contactInfo]; + const AddContactInformation( + {required this.contactInfo, + required this.profileId, + required this.token}); + @override + List get props => [profileId, token, contactInfo]; } ////edit event -class EditContactInformation extends ContactEvent{ +class EditContactInformation extends ContactEvent { final int profileId; final String token; final ContactInfo contactInfo; - const EditContactInformation({required this.contactInfo, required this.profileId, required this.token}); - @override - List get props => [profileId,token,contactInfo]; + const EditContactInformation( + {required this.contactInfo, + required this.profileId, + required this.token}); + @override + List get props => [profileId, token, contactInfo]; } //// delete event -class DeleteContactInformation extends ContactEvent{ - final int profileId; +class DeleteContactInformation extends ContactEvent { + final int profileId; final String token; final ContactInfo contactInfo; - const DeleteContactInformation({required this.contactInfo, required this.profileId, required this.token}); - @override - List get props => [profileId,token,contactInfo]; + const DeleteContactInformation( + {required this.contactInfo, + required this.profileId, + required this.token}); + @override + List get props => [profileId, token, contactInfo]; } - - diff --git a/lib/bloc/profile/workHistory/workHistory_bloc.dart b/lib/bloc/profile/workHistory/workHistory_bloc.dart index b751baa..c3f34f6 100644 --- a/lib/bloc/profile/workHistory/workHistory_bloc.dart +++ b/lib/bloc/profile/workHistory/workHistory_bloc.dart @@ -68,7 +68,7 @@ class WorkHistoryBloc extends Bloc { }); //// ADD WORK HISTORIES on((event, emit) async { - // try { + try { Map status = await WorkHistoryService.instance.add( accomplishment: event.accomplishment, actualDuties: event.actualDuties, @@ -83,36 +83,36 @@ class WorkHistoryBloc extends Bloc { } else { emit(WorkHistoryAddedState(response: status)); } + } catch (e) { + emit(WorkHistoryErrorState(message: e.toString())); + } + }); + +////UPDATE WORK HISTORY + on((event, emit) async { + // try { + Map status = await WorkHistoryService.instance.update( + isPrivate: event.isPrivate, + workHistory: event.workHistory, + token: event.token, + profileId: event.profileId); + if (status['success']) { + WorkHistory workHistory = WorkHistory.fromJson(status['data']); + // workExperiences.removeWhere((WorkHistory work) { + // return work.id == event.workHistory.id; + // }); + workExperiences.add(workHistory); + emit(WorkHistoryEditedState(response: status)); + } else { + emit(WorkHistoryEditedState( + response: status, + )); + } // } catch (e) { // emit(WorkHistoryErrorState(message: e.toString())); // } }); -////UPDATE WORK HISTORY - // on((event, emit) async { - // try { - // Map status = await WorkHistoryService.instance.update( - // oldWorkHistory: event.oldWorkHistory, - // newWorkHistory: event.workHistory, - // token: event.token, - // profileId: event.profileId); - // if (status['success']) { - // WorkHistory workHistory = WorkHistory.fromJson(status['data']); - // workExperiences.removeWhere((WorkHistory work) { - // return work.id == event.oldWorkHistory.id; - // }); - // workExperiences.add(workHistory); - // emit(WorkHistoryEditedState(response: status)); - // } else { - // emit(WorkHistoryEditedState( - // response: status, - // )); - // } - // } catch (e) { - // emit(WorkHistoryErrorState(message: e.toString())); - // } - // }); - ////SHOW EDIT WORK HISTORIES on((event, emit) async { try { diff --git a/lib/bloc/profile/workHistory/workHistory_event.dart b/lib/bloc/profile/workHistory/workHistory_event.dart index 6e73101..a1a8ffc 100644 --- a/lib/bloc/profile/workHistory/workHistory_event.dart +++ b/lib/bloc/profile/workHistory/workHistory_event.dart @@ -46,9 +46,8 @@ class UpdateWorkHistory extends WorkHistorytEvent{ final bool isPrivate; final int profileId; final String token; - final String? actualDuties; - final String? accomplishment; - const UpdateWorkHistory({required this.profileId, required this.token, required this.workHistory, required this.accomplishment, required this.actualDuties, required this.isPrivate}); + + const UpdateWorkHistory({required this.profileId, required this.token, required this.workHistory, required this.isPrivate}); @override List get props => [profileId,token,workHistory,]; } diff --git a/lib/screens/profile/components/basic_information/address/add_modal.dart b/lib/screens/profile/components/basic_information/address/add_modal.dart index fd8514e..1ba59f0 100644 --- a/lib/screens/profile/components/basic_information/address/add_modal.dart +++ b/lib/screens/profile/components/basic_information/address/add_modal.dart @@ -77,348 +77,341 @@ class _AddAddressScreenState extends State { const EdgeInsets.symmetric(vertical: 32, horizontal: 24), child: SizedBox( height: screenHeight * 88, - child: Column( + child: ListView( children: [ - Flexible( - child: ListView( + FormBuilderDropdown( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: + normalTextFieldStyle("Category*", "Category"), + name: "category", + onChanged: (AddressCategory? category) { + selectedAddressCategory = category; + }, + items: category + .map>( + (AddressCategory category) { + return DropdownMenuItem( + value: category, child: Text(category.name!)); + }).toList()), + const SizedBox( + height: 12, + ), + ////Area Class + FormBuilderDropdown( + decoration: normalTextFieldStyle( + "Area class *", "Area class"), + name: "area_class", + onChanged: (Area? area) { + selectedAreaClass = area!.value; + }, + items: areaClass + .map>((Area area) { + return area.group == 0 + ? DropdownMenuItem( + enabled: false, + value: area, + child: Text(area.value.toUpperCase(), + style: const TextStyle( + color: Colors.black38))) + : DropdownMenuItem( + value: area, + enabled: true, + child: Text( + " ${area.value.toUpperCase()}")); + }).toList()), + const SizedBox( + height: 12, + ), + ////stateful builder + StatefulBuilder(builder: (context, setState) { + return Column( children: [ - //// category - FormBuilderDropdown( - validator: FormBuilderValidators.required( - errorText: "This field is required"), - decoration: - normalTextFieldStyle("Category*", "Category"), - name: "category", - onChanged: (AddressCategory? category) { - selectedAddressCategory = category; - }, - items: category - .map>( - (AddressCategory category) { - return DropdownMenuItem( - value: category, child: Text(category.name!)); - }).toList()), - const SizedBox( - height: 12, - ), - ////Area Class - FormBuilderDropdown( - decoration: normalTextFieldStyle( - "Area class *", "Area class"), - name: "area_class", - onChanged: (Area? area) { - selectedAreaClass = area!.value; - }, - items: areaClass - .map>((Area area) { - return area.group == 0 - ? DropdownMenuItem( - enabled: false, - value: area, - child: Text(area.value.toUpperCase(), - style: const TextStyle( - color: Colors.black38))) - : DropdownMenuItem( - value: area, - enabled: true, - child: Text( - " ${area.value.toUpperCase()}")); - }).toList()), - const SizedBox( - height: 12, - ), - ////stateful builder - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - ////with block & Lot Switch - FormBuilderSwitch( - initialValue: hasLotandBlock, - activeColor: second, - onChanged: (value) { - setState(() { - hasLotandBlock = value!; - }); - }, - decoration: normalTextFieldStyle( - "With Lot and Block?", 'Graduated?'), - name: 'graudated', - title: Text(hasLotandBlock ? "YES" : "NO"), - ), - SizedBox( - height: hasLotandBlock ? 12 : 0, - ), - SizedBox( - width: screenWidth, - child: hasLotandBlock - ? Row( - children: [ - ////block # - Flexible( - flex: 1, - child: FormBuilderTextField( - validator: FormBuilderValidators - .compose([numericRequired]), - keyboardType: - TextInputType.number, - name: "block_number", - decoration: - normalTextFieldStyle( - "Block #*", "Block #"), - )), - const SizedBox( - width: 8, - ), - //// lot # - Flexible( - flex: 1, - child: FormBuilderTextField( - validator: FormBuilderValidators - .compose([numericRequired]), - name: "lot_number", - keyboardType: - TextInputType.number, - decoration: - normalTextFieldStyle( - "Lot #*", "Lot #"), - )), - ], - ) - : Container(), - ), - ], - ); - }), - const SizedBox( - height: 12, - ), - //// Address Line - FormBuilderTextField( - name: "address_line", + ////with block & Lot Switch + FormBuilderSwitch( + initialValue: hasLotandBlock, + activeColor: second, + onChanged: (value) { + setState(() { + hasLotandBlock = value!; + }); + }, decoration: normalTextFieldStyle( - "Address Line ", "Address Line"), + "With Lot and Block?", 'Graduated?'), + name: 'graudated', + title: Text(hasLotandBlock ? "YES" : "NO"), ), - const SizedBox( - height: 12, + SizedBox( + height: hasLotandBlock ? 12 : 0, ), - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - setState(() { - overseas = value!; - }); - }, - decoration: normalTextFieldStyle( - "Overseas Address?", ''), - name: 'overseas', - title: Text(overseas ? "YES" : "NO"), - ), - SizedBox( - height: overseas == true ? 8 : 0, - ), - SizedBox( - child: overseas == false - ? Column( - children: [ - const SizedBox( - height: 12, - ), - ////REGION DROPDOWN - FormBuilderDropdown( - autovalidateMode: AutovalidateMode - .onUserInteraction, - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - onChanged: (Region? region) async { - if (selectedRegion != region) { + SizedBox( + width: screenWidth, + child: hasLotandBlock + ? Row( + children: [ + ////block # + Flexible( + flex: 1, + child: FormBuilderTextField( + validator: FormBuilderValidators + .compose([numericRequired]), + keyboardType: + TextInputType.number, + name: "block_number", + decoration: + normalTextFieldStyle( + "Block #*", "Block #"), + )), + const SizedBox( + width: 8, + ), + //// lot # + Flexible( + flex: 1, + child: FormBuilderTextField( + validator: FormBuilderValidators + .compose([numericRequired]), + name: "lot_number", + keyboardType: + TextInputType.number, + decoration: + normalTextFieldStyle( + "Lot #*", "Lot #"), + )), + ], + ) + : Container(), + ), + ], + ); + }), + const SizedBox( + height: 12, + ), + //// Address Line + FormBuilderTextField( + name: "address_line", + decoration: normalTextFieldStyle( + "Address Line ", "Address Line"), + ), + const SizedBox( + height: 12, + ), + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: normalTextFieldStyle( + "Overseas Address?", ''), + name: 'overseas', + title: Text(overseas ? "YES" : "NO"), + ), + SizedBox( + height: overseas == true ? 8 : 0, + ), + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + FormBuilderDropdown( + autovalidateMode: AutovalidateMode + .onUserInteraction, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + onChanged: (Region? region) async { + if (selectedRegion != region) { + setState(() { + provinceCall = true; + }); + selectedRegion = region; + getProvinces(); + } + }, + initialValue: null, + decoration: normalTextFieldStyle( + "Region*", "Region"), + name: 'region', + items: state.regions + .map>( + (Region region) { + return DropdownMenuItem( + value: region, + child: Text( + region.description!)); + }).toList(), + ), + const SizedBox( + height: 8, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + value: selectedProvince, + onChanged: + (Province? province) { + if (selectedProvince != + province) { setState(() { - provinceCall = true; + cityCall = true; }); - selectedRegion = region; - getProvinces(); + selectedProvince = + province; + getCities(); } }, - initialValue: null, - decoration: normalTextFieldStyle( - "Region*", "Region"), - name: 'region', - items: state.regions - .map>( - (Region region) { - return DropdownMenuItem( - value: region, - child: Text( - region.description!)); - }).toList(), - ), - const SizedBox( - height: 8, - ), - //// PROVINCE DROPDOWN - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: provinceCall, - child: DropdownButtonFormField< - Province?>( - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - value: selectedProvince, - onChanged: - (Province? province) { - if (selectedProvince != - province) { - setState(() { - cityCall = true; - }); - selectedProvince = - province; - getCities(); - } - }, - items: provinces == null - ? [] - : provinces!.map< - DropdownMenuItem< - Province>>( - (Province province) { - return DropdownMenuItem( - value: province, - child: FittedBox( - child: Text(province - .description!), - )); - }).toList(), - decoration: - normalTextFieldStyle( - "Province*", - "Province")), - ), - ), - ////CITY MUNICIPALITY - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: cityCall, - child: DropdownButtonFormField< - CityMunicipality>( - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - isExpanded: true, - onChanged: - (CityMunicipality? city) { - if (selectedMunicipality != - city) { - setState(() { - barangayCall = true; - }); - selectedMunicipality = city; - getBarangays(); - } - }, - decoration: - normalTextFieldStyle( - "Municipality*", - "Municipality"), - value: selectedMunicipality, - items: citymuns == null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality c) { - return DropdownMenuItem( - value: c, - child: Text(c - .description!)); - }).toList(), - ), - ), - ), - //// BARANGAY - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: barangayCall, - child: DropdownButtonFormField< - Barangay>( - isExpanded: true, - onChanged: (Barangay? baragay) { - selectedBarangay = baragay; - }, - decoration: - normalTextFieldStyle( - "Barangay*", - "Barangay"), - value: selectedBarangay, - items: barangays == null - ? [] - : barangays!.map< - DropdownMenuItem< - Barangay>>( - (Barangay barangay) { - return DropdownMenuItem( - value: barangay, - child: Text(barangay - .description!)); - }).toList(), - ), - ), - ), - ], - ) - //// COUNTRY DROPDOWN - : SizedBox( - height: 60, - child: FormBuilderDropdown( - initialValue: null, - validator: - FormBuilderValidators.required( + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province province) { + return DropdownMenuItem( + value: province, + child: FittedBox( + child: Text(province + .description!), + )); + }).toList(), + decoration: + normalTextFieldStyle( + "Province*", + "Province")), + ), + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: DropdownButtonFormField< + CityMunicipality>( + validator: FormBuilderValidators + .required( errorText: "This field is required"), - items: state.countries - .map>( - (Country country) { - return DropdownMenuItem( - value: country, - child: FittedBox( - child: - Text(country.name!))); - }).toList(), - name: 'country', - decoration: normalTextFieldStyle( - "Country*", "Country"), - onChanged: (Country? value) { - selectedCountry = value; + isExpanded: true, + onChanged: + (CityMunicipality? city) { + if (selectedMunicipality != + city) { + setState(() { + barangayCall = true; + }); + selectedMunicipality = city; + getBarangays(); + } }, + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), ), ), - ), - ], - ); - }), - ////sumit button + ), + //// BARANGAY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: barangayCall, + child: DropdownButtonFormField< + Barangay>( + isExpanded: true, + onChanged: (Barangay? baragay) { + selectedBarangay = baragay; + }, + decoration: + normalTextFieldStyle( + "Barangay*", + "Barangay"), + value: selectedBarangay, + items: barangays == null + ? [] + : barangays!.map< + DropdownMenuItem< + Barangay>>( + (Barangay barangay) { + return DropdownMenuItem( + value: barangay, + child: Text(barangay + .description!)); + }).toList(), + ), + ), + ), + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( + height: 60, + child: FormBuilderDropdown( + initialValue: null, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + items: state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox( + child: + Text(country.name!))); + }).toList(), + name: 'country', + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) { + selectedCountry = value; + }, + ), + ), + ), ], - ), - ), - SizedBox( + ); + }), + const SizedBox(height: 24,), + SizedBox( width: double.infinity, height: 60, child: ElevatedButton( @@ -464,12 +457,13 @@ class _AddAddressScreenState extends State { }, child: const Text(submit)), ), + ], ), ), )); } - return Placeholder(); + return const Placeholder(); }, ); } diff --git a/lib/screens/profile/components/basic_information/address/edit_modal.dart b/lib/screens/profile/components/basic_information/address/edit_modal.dart index 83901b5..83ac0d4 100644 --- a/lib/screens/profile/components/basic_information/address/edit_modal.dart +++ b/lib/screens/profile/components/basic_information/address/edit_modal.dart @@ -106,208 +106,313 @@ class _EditAddressScreenState extends State { vertical: 32, horizontal: 24), child: SizedBox( height: screenHeight * 88, - child: Column( + child: ListView( children: [ - Flexible( - child: ListView( + //// category + FormBuilderDropdown( + initialValue: selectedAddressCategory, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: + normalTextFieldStyle("Category*", "Category"), + name: "category", + onChanged: (AddressCategory? category) { + selectedAddressCategory = category; + }, + items: category + .map>( + (AddressCategory category) { + return DropdownMenuItem( + value: category, child: Text(category.name!)); + }).toList()), + const SizedBox( + height: 12, + ), + ////Area Class + FormBuilderDropdown( + initialValue: selectedAreaClass, + decoration: normalTextFieldStyle( + "Area class *", "Area class"), + name: "area_class", + onChanged: (Area? area) { + selectedAreaClass = area; + }, + items: areaClass + .map>((Area area) { + return area.group == 0 + ? DropdownMenuItem( + enabled: false, + value: area, + child: Text(area.value.toUpperCase(), + style: const TextStyle( + color: Colors.black38))) + : DropdownMenuItem( + value: area, + enabled: true, + child: Text( + " ${area.value.toUpperCase()}")); + }).toList()), + const SizedBox( + height: 12, + ), + ////stateful builder + StatefulBuilder(builder: (context, setState) { + return Column( children: [ - //// category - FormBuilderDropdown( - initialValue: selectedAddressCategory, - validator: FormBuilderValidators.required( - errorText: "This field is required"), - decoration: - normalTextFieldStyle("Category*", "Category"), - name: "category", - onChanged: (AddressCategory? category) { - selectedAddressCategory = category; - }, - items: category - .map>( - (AddressCategory category) { - return DropdownMenuItem( - value: category, child: Text(category.name!)); - }).toList()), - const SizedBox( - height: 12, - ), - ////Area Class - FormBuilderDropdown( - initialValue: selectedAreaClass, - decoration: normalTextFieldStyle( - "Area class *", "Area class"), - name: "area_class", - onChanged: (Area? area) { - selectedAreaClass = area; - }, - items: areaClass - .map>((Area area) { - return area.group == 0 - ? DropdownMenuItem( - enabled: false, - value: area, - child: Text(area.value.toUpperCase(), - style: const TextStyle( - color: Colors.black38))) - : DropdownMenuItem( - value: area, - enabled: true, - child: Text( - " ${area.value.toUpperCase()}")); - }).toList()), - const SizedBox( - height: 12, - ), - ////stateful builder - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - ////with block & Lot Switch - FormBuilderSwitch( - initialValue: hasLotandBlock, - activeColor: second, - onChanged: (value) { - setState(() { - hasLotandBlock = value!; - }); - }, - decoration: normalTextFieldStyle( - "With Lot and Block?", 'Graduated?'), - name: 'graudated', - title: Text(hasLotandBlock ? "YES" : "NO"), - ), - SizedBox( - height: hasLotandBlock ? 12 : 0, - ), - SizedBox( - width: screenWidth, - child: hasLotandBlock - ? Row( - children: [ - ////block # - Flexible( - flex: 1, - child: FormBuilderTextField( - initialValue: state.address - .subdivision?.blockNo - ?.toString(), - validator: FormBuilderValidators - .compose([numericRequired]), - keyboardType: - TextInputType.number, - name: "block_number", - decoration: - normalTextFieldStyle( - "Block #*", "Block #"), - )), - const SizedBox( - width: 8, - ), - //// lot # - Flexible( - flex: 1, - child: FormBuilderTextField( - initialValue: state - .address.subdivision?.lotNo - ?.toString(), - validator: FormBuilderValidators - .compose([numericRequired]), - name: "lot_number", - keyboardType: - TextInputType.number, - decoration: - normalTextFieldStyle( - "Lot #*", "Lot #"), - )), - ], - ) - : Container(), - ), - ], - ); - }), - const SizedBox( - height: 12, - ), - //// Address Line - FormBuilderTextField( - initialValue: state.address.details, - name: "address_line", + ////with block & Lot Switch + FormBuilderSwitch( + initialValue: hasLotandBlock, + activeColor: second, + onChanged: (value) { + setState(() { + hasLotandBlock = value!; + }); + }, decoration: normalTextFieldStyle( - "Address Line *", "Address Line"), + "With Lot and Block?", 'Graduated?'), + name: 'graudated', + title: Text(hasLotandBlock ? "YES" : "NO"), ), - const SizedBox( - height: 12, + SizedBox( + height: hasLotandBlock ? 12 : 0, ), - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - setState(() { - overseas = value!; - }); - }, - decoration: normalTextFieldStyle( - "Overseas Address?", ''), - name: 'overseas', - title: Text(overseas ? "YES" : "NO"), - ), - SizedBox( - height: overseas == true ? 8 : 0, - ), - SizedBox( - child: overseas == false - ? Column( - children: [ - const SizedBox( - height: 12, - ), - ////REGION DROPDOWN - DropdownButtonFormField( + SizedBox( + width: screenWidth, + child: hasLotandBlock + ? Row( + children: [ + ////block # + Flexible( + flex: 1, + child: FormBuilderTextField( + initialValue: state.address + .subdivision?.blockNo + ?.toString(), + validator: FormBuilderValidators + .compose([numericRequired]), + keyboardType: + TextInputType.number, + name: "block_number", + decoration: + normalTextFieldStyle( + "Block #*", "Block #"), + )), + const SizedBox( + width: 8, + ), + //// lot # + Flexible( + flex: 1, + child: FormBuilderTextField( + initialValue: state + .address.subdivision?.lotNo + ?.toString(), + validator: FormBuilderValidators + .compose([numericRequired]), + name: "lot_number", + keyboardType: + TextInputType.number, + decoration: + normalTextFieldStyle( + "Lot #*", "Lot #"), + )), + ], + ) + : Container(), + ), + ], + ); + }), + const SizedBox( + height: 12, + ), + //// Address Line + FormBuilderTextField( + initialValue: state.address.details, + name: "address_line", + decoration: normalTextFieldStyle( + "Address Line *", "Address Line"), + ), + const SizedBox( + height: 12, + ), + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: normalTextFieldStyle( + "Overseas Address?", ''), + name: 'overseas', + title: Text(overseas ? "YES" : "NO"), + ), + SizedBox( + height: overseas == true ? 8 : 0, + ), + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + DropdownButtonFormField( + isExpanded: true, + value: selectedRegion, + autovalidateMode: AutovalidateMode + .onUserInteraction, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + onChanged: (Region? region) async { + if (selectedRegion != region) { + setState(() { + provinceCall = true; + }); + selectedRegion = region; + //// GET PROVINCES + try{ + provinces = await LocationUtils + .instance + .getProvinces( + regionCode: + selectedRegion!.code + .toString()); + }catch(e){ + NavigationService.navigatorKey.currentContext?.read().add(CallErrorState()); + } + selectedProvince = + provinces![0]; + setState(() { + provinceCall = false; + cityCall = true; + }); + //// GET CITIES + try{ + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code!); + }catch(e){ + NavigationService.navigatorKey.currentContext?.read().add(CallErrorState()); + } + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + barangayCall = true; + }); + //// GET BARANGAY + try{ + barangays = await LocationUtils + .instance + .getBarangay( + code: + selectedMunicipality! + .code!); + }catch(e){ + NavigationService.navigatorKey.currentContext?.read().add(CallErrorState()); + } + selectedBarangay = + barangays![0]; + setState(() { + barangayCall = false; + }); + ////GET CITY MUNICIPALITY + try{ + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code!); + }catch(e){ + NavigationService.navigatorKey.currentContext?.read().add(CallErrorState()); + } + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + barangayCall = true; + }); + //// GET BARANGAYS + try{ + barangays = await LocationUtils + .instance + .getBarangay( + code: + selectedMunicipality! + .code!); + }catch(e){ + NavigationService.navigatorKey.currentContext?.read().add(CallErrorState()); + } + selectedBarangay = + barangays![0]; + setState(() { + barangayCall = false; + }); + } + }, + decoration: normalTextFieldStyle( + "Region*", "Region"), + items: state.regions + .map>( + (Region region) { + return DropdownMenuItem( + value: region, + child: Text( + region.description!)); + }).toList(), + ), + const SizedBox( + height: 8, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null + ? 'required' + : null, isExpanded: true, - value: selectedRegion, - autovalidateMode: AutovalidateMode - .onUserInteraction, - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - onChanged: (Region? region) async { - if (selectedRegion != region) { - setState(() { - provinceCall = true; - }); - selectedRegion = region; - //// GET PROVINCES - try{ - provinces = await LocationUtils - .instance - .getProvinces( - regionCode: - selectedRegion!.code - .toString()); - }catch(e){ - NavigationService.navigatorKey.currentContext?.read().add(CallErrorState()); - } + value: selectedProvince, + onChanged: (Province? + province) async { + if (selectedProvince != + province) { selectedProvince = - provinces![0]; + province; setState(() { - provinceCall = false; cityCall = true; }); + //// GET CITIES try{ citymuns = await LocationUtils .instance .getCities( - code: selectedProvince! - .code!); + code: + selectedProvince! + .code!); }catch(e){ - NavigationService.navigatorKey.currentContext?.read().add(CallErrorState()); - } + NavigationService.navigatorKey.currentContext?.read().add(CallErrorState()); + } selectedMunicipality = citymuns![0]; setState(() { @@ -323,40 +428,8 @@ class _EditAddressScreenState extends State { selectedMunicipality! .code!); }catch(e){ - NavigationService.navigatorKey.currentContext?.read().add(CallErrorState()); - } - selectedBarangay = - barangays![0]; - setState(() { - barangayCall = false; - }); - ////GET CITY MUNICIPALITY - try{ - citymuns = await LocationUtils - .instance - .getCities( - code: selectedProvince! - .code!); - }catch(e){ - NavigationService.navigatorKey.currentContext?.read().add(CallErrorState()); - } - selectedMunicipality = - citymuns![0]; - setState(() { - cityCall = false; - barangayCall = true; - }); - //// GET BARANGAYS - try{ - barangays = await LocationUtils - .instance - .getBarangay( - code: - selectedMunicipality! - .code!); - }catch(e){ - NavigationService.navigatorKey.currentContext?.read().add(CallErrorState()); - } + NavigationService.navigatorKey.currentContext?.read().add(CallErrorState()); + } selectedBarangay = barangays![0]; setState(() { @@ -364,231 +437,151 @@ class _EditAddressScreenState extends State { }); } }, - decoration: normalTextFieldStyle( - "Region*", "Region"), - items: state.regions - .map>( - (Region region) { - return DropdownMenuItem( - value: region, - child: Text( - region.description!)); - }).toList(), - ), - const SizedBox( - height: 8, - ), - //// PROVINCE DROPDOWN - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: provinceCall, - child: DropdownButtonFormField< - Province?>( - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - value: selectedProvince, - onChanged: (Province? - province) async { - if (selectedProvince != - province) { - selectedProvince = - province; - setState(() { - cityCall = true; - }); - - //// GET CITIES - try{ - citymuns = await LocationUtils - .instance - .getCities( - code: - selectedProvince! - .code!); - }catch(e){ - NavigationService.navigatorKey.currentContext?.read().add(CallErrorState()); - } - selectedMunicipality = - citymuns![0]; - setState(() { - cityCall = false; - barangayCall = true; - }); - //// GET BARANGAY - try{ - barangays = await LocationUtils - .instance - .getBarangay( - code: - selectedMunicipality! - .code!); - }catch(e){ - NavigationService.navigatorKey.currentContext?.read().add(CallErrorState()); - } - selectedBarangay = - barangays![0]; - setState(() { - barangayCall = false; - }); - } - }, - items: provinces == null - ? [] - : provinces!.map< - DropdownMenuItem< - Province>>( - (Province province) { - return DropdownMenuItem( - value: province, - child: FittedBox( - child: Text(province - .description!), - )); - }).toList(), - decoration: - normalTextFieldStyle( - "Province*", - "Province")), - ), - ), - ////CITY MUNICIPALITY - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: cityCall, - child: DropdownButtonFormField< - CityMunicipality>( - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - isExpanded: true, - onChanged: (CityMunicipality? - city) async { - if (selectedMunicipality != - city) { - setState(() { - barangayCall = true; - }); - selectedMunicipality = city; - selectedMunicipality = city; - //// GET BARANGAYS - try{ - barangays = await LocationUtils - .instance - .getBarangay( - code: - selectedMunicipality! - .code!); - }catch(e){ - NavigationService.navigatorKey.currentContext?.read().add(CallErrorState()); - } - selectedBarangay = - barangays![0]; - setState(() { - barangayCall = false; - }); - } - }, - decoration: - normalTextFieldStyle( - "Municipality*", - "Municipality"), - value: selectedMunicipality, - items: citymuns == null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality c) { - return DropdownMenuItem( - value: c, - child: Text(c - .description!)); - }).toList(), - ), - ), - ), - //// BARANGAY - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: barangayCall, - child: DropdownButtonFormField< - Barangay>( - isExpanded: true, - onChanged: (Barangay? baragay) { - selectedBarangay = baragay; - }, - decoration: - normalTextFieldStyle( - "Barangay*", - "Barangay"), - value: selectedBarangay, - items: barangays == null - ? [] - : barangays!.map< - DropdownMenuItem< - Barangay>>( - (Barangay barangay) { - return DropdownMenuItem( - value: barangay, - child: Text(barangay - .description!)); - }).toList(), - ), - ), - ), - ], - ) - //// COUNTRY DROPDOWN - : SizedBox( - height: 60, - child: FormBuilderDropdown( - initialValue:selectedCountry!.id == 175?null:selectedCountry, - validator: - FormBuilderValidators.required( + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province province) { + return DropdownMenuItem( + value: province, + child: FittedBox( + child: Text(province + .description!), + )); + }).toList(), + decoration: + normalTextFieldStyle( + "Province*", + "Province")), + ), + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: DropdownButtonFormField< + CityMunicipality>( + validator: FormBuilderValidators + .required( errorText: "This field is required"), - items: state.countries - .map>( - (Country country) { - return DropdownMenuItem( - value: country, - child: FittedBox( - child: - Text(country.name!))); - }).toList(), - name: 'country', - decoration: normalTextFieldStyle( - "Country*", "Country"), - onChanged: (Country? value) { - setState((){ - selectedCountry = value; - }); + isExpanded: true, + onChanged: (CityMunicipality? + city) async { + if (selectedMunicipality != + city) { + setState(() { + barangayCall = true; + }); + selectedMunicipality = city; + selectedMunicipality = city; + //// GET BARANGAYS + try{ + barangays = await LocationUtils + .instance + .getBarangay( + code: + selectedMunicipality! + .code!); + }catch(e){ + NavigationService.navigatorKey.currentContext?.read().add(CallErrorState()); + } + selectedBarangay = + barangays![0]; + setState(() { + barangayCall = false; + }); + } }, + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), ), ), - ), - ], - ); - }), - - + ), + //// BARANGAY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: barangayCall, + child: DropdownButtonFormField< + Barangay>( + isExpanded: true, + onChanged: (Barangay? baragay) { + selectedBarangay = baragay; + }, + decoration: + normalTextFieldStyle( + "Barangay*", + "Barangay"), + value: selectedBarangay, + items: barangays == null + ? [] + : barangays!.map< + DropdownMenuItem< + Barangay>>( + (Barangay barangay) { + return DropdownMenuItem( + value: barangay, + child: Text(barangay + .description!)); + }).toList(), + ), + ), + ), + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( + height: 60, + child: FormBuilderDropdown( + initialValue:selectedCountry!.id == 175?null:selectedCountry, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + items: state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox( + child: + Text(country.name!))); + }).toList(), + name: 'country', + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) { + setState((){ + selectedCountry = value; + }); + }, + ), + ), + ), ], - ), - ), - SizedBox( + ); + }), + const SizedBox(height: 8,), + SizedBox( width: double.infinity, height: 60, child: ElevatedButton( @@ -645,13 +638,13 @@ class _EditAddressScreenState extends State { }, child: const Text(submit)), ), - + ], ), ), )); } - return Placeholder(); + return const Placeholder(); }, ); } diff --git a/lib/screens/profile/components/basic_information/address_screen.dart b/lib/screens/profile/components/basic_information/address_screen.dart index 1df1ddc..c7ebf40 100644 --- a/lib/screens/profile/components/basic_information/address_screen.dart +++ b/lib/screens/profile/components/basic_information/address_screen.dart @@ -26,7 +26,7 @@ class AddressScreen extends StatelessWidget { int? profileId; String? token; return Scaffold( - resizeToAvoidBottomInset: false, + resizeToAvoidBottomInset: true, appBar: AppBar( title: context.watch().state is AddAddressState?const Text("Add Address"):context.watch().state is EditAddressState?const Text("Edit Address"):const Text("Addresses"), centerTitle: true, diff --git a/lib/screens/profile/components/basic_information/contact_information/add_modal.dart b/lib/screens/profile/components/basic_information/contact_information/add_modal.dart index 03f75da..0d7d0c2 100644 --- a/lib/screens/profile/components/basic_information/contact_information/add_modal.dart +++ b/lib/screens/profile/components/basic_information/contact_information/add_modal.dart @@ -50,210 +50,206 @@ class _AddContactInformationScreenState builder: (context, state) { if (state is ContactAddingState) { return Padding( - padding: const EdgeInsets.symmetric(vertical: 32, horizontal: 24), + padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 24), child: FormBuilder( key: formKey, child: StatefulBuilder(builder: (context, setState) { - return SizedBox( - height: screenHeight * 90, - child: Column( - children: [ - Flexible( - child: ListView( - children: [ - - ////Service Type - FormBuilderDropdown( + return ListView( + children: [ + ////Service Type + FormBuilderDropdown( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "service_type", + items: state.serviceTypes + .map>( + (ServiceType e) { + return DropdownMenuItem( + value: e, child: Text(e.name!)); + }).toList(), + decoration: normalTextFieldStyle("Service Type*", ""), + onChanged: (var service) async { + if (selectedServiceType != service) { + selectedServiceType = service; + setState(() { + callServiceType = true; + selectedCommServiceProvider = null; + numberMailController.text = ""; + }); + + try { + commServiceProviders = await ContactService + .instance + .getServiceProvider( + serviceTypeId: + selectedServiceType!.id!); + } catch (e) { + context + .read() + .add(CallErrorEvent(message: e.toString())); + } + setState(() { + setState(() { + callServiceType = false; + }); + }); + } + }), + const SizedBox( + height: 12, + ), + ////Service Provider + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: callServiceType, + child: DropdownButtonFormField( + isExpanded: true, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + items: commServiceProviders.isEmpty + ? [] + : commServiceProviders + .map>( + (CommService e) { + return DropdownMenuItem( + value: e, + child: Text(e + .serviceProvider!.agency!.name!)); + }).toList(), + decoration: normalTextFieldStyle( + "Communication Service *", ""), + onChanged: (var serviceProvider) { + selectedCommServiceProvider = serviceProvider; + }), + ), + ), + selectedServiceType != null + ? selectedServiceType?.id == 2 + //// Landline + ? FormBuilderTextField( + controller: numberMailController, + inputFormatters: [landLineFormatter], + name: 'number-mail', validator: FormBuilderValidators.required( errorText: "This field is required"), - name: "service_type", - items: state.serviceTypes - .map>( - (ServiceType e) { - return DropdownMenuItem( - value: e, child: Text(e.name!)); - }).toList(), - decoration: normalTextFieldStyle("Service Type*", ""), - onChanged: (var service) async { - if (selectedServiceType != service) { - selectedServiceType = service; - setState(() { - callServiceType = true; - selectedCommServiceProvider = null; - numberMailController.text = ""; - }); - - commServiceProviders = await ContactService - .instance - .getServiceProvider( - serviceTypeId: selectedServiceType!.id!); - setState(() { - setState(() { - callServiceType = false; - }); - }); - } - }), - const SizedBox( - height: 12, - ), - ////Service Provider - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: callServiceType, - child: DropdownButtonFormField( - isExpanded: true, + decoration: normalTextFieldStyle( + "Landline number *", + "(area code) xxx - xxxx"), + ) + : selectedServiceType!.id == 1 || + selectedServiceType!.id == 19 + //// Mobile number + ? FormBuilderTextField( + keyboardType: TextInputType.number, + controller: numberMailController, + name: 'number-mail', + inputFormatters: [mobileFormatter], validator: FormBuilderValidators.required( errorText: "This field is required"), - items: commServiceProviders.isEmpty - ? [] - : commServiceProviders - .map>( - (CommService e) { - return DropdownMenuItem( - value: e, - child: Text(e - .serviceProvider!.agency!.name!)); - }).toList(), decoration: normalTextFieldStyle( - "Communication Service *", ""), - onChanged: (var serviceProvider) { - selectedCommServiceProvider = serviceProvider; - }), - ), - ), - selectedServiceType != null - ? selectedServiceType?.id == 2 - //// Landline + "Mobile number *", + "+63 (9xx) xxx - xxxx"), + ) + : selectedServiceType!.id == 4 + ////Social Media ? FormBuilderTextField( controller: numberMailController, - inputFormatters: [landLineFormatter], name: 'number-mail', - validator: FormBuilderValidators.required( - errorText: "This field is required"), + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), decoration: normalTextFieldStyle( - "Landline number *", - "(area code) xxx - xxxx"), + "Account ID / Username *", ""), ) - : selectedServiceType!.id == 1 || - selectedServiceType!.id == 19 - //// Mobile number + : selectedServiceType!.id == 3 + ////Email Address ? FormBuilderTextField( - keyboardType: TextInputType.number, controller: numberMailController, name: 'number-mail', - inputFormatters: [mobileFormatter], - validator: FormBuilderValidators.required( - errorText: "This field is required"), + validator: FormBuilderValidators + .compose([ + FormBuilderValidators.email( + errorText: + "Input vaild email"), + FormBuilderValidators.required( + errorText: + "This field is required") + ]), decoration: normalTextFieldStyle( - "Mobile number *", - "+63 (9xx) xxx - xxxx"), + "Email Address*", ""), ) - : selectedServiceType!.id == 4 - ////Social Media - ? FormBuilderTextField( - controller: numberMailController, - name: 'number-mail', - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - decoration: normalTextFieldStyle( - "Account ID / Username *", ""), - ) - : selectedServiceType!.id == 3 - ////Email Address - ? FormBuilderTextField( - controller: numberMailController, - name: 'number-mail', - validator: FormBuilderValidators - .compose([ - FormBuilderValidators.email( - errorText: - "Input vaild email"), - FormBuilderValidators.required( - errorText: - "This field is required") - ]), - decoration: normalTextFieldStyle( - "Email Address*", ""), - ) - : Container() - : const SizedBox(), - SizedBox( - height: selectedServiceType != null ? 12 : 0, - ), - //// Primary - FormBuilderSwitch( - initialValue: primaryaContact, - activeColor: second, - onChanged: (value) { - setState(() { - primaryaContact = value!; - }); - }, - decoration: - normalTextFieldStyle("Primary?", 'Primary?'), - name: 'overseas', - title: Text(primaryaContact ? "YES" : "NO"), - ), - //// Active - const SizedBox( - height: 12, - ), - FormBuilderSwitch( - initialValue: primaryaContact, - activeColor: second, - onChanged: (value) { - setState(() { - active = value!; - }); - }, - decoration: normalTextFieldStyle("Active?", ''), - name: 'overseas', - title: Text(active ? "YES" : "NO"), - ), - - - ], - ), - + : Container() + : const SizedBox(), + SizedBox( + height: selectedServiceType != null ? 12 : 0, + ), + //// Primary + FormBuilderSwitch( + initialValue: primaryaContact, + activeColor: second, + onChanged: (value) { + setState(() { + primaryaContact = value!; + }); + }, + decoration: + normalTextFieldStyle("Primary?", 'Primary?'), + name: 'overseas', + title: Text(primaryaContact ? "YES" : "NO"), + ), + //// Active + const SizedBox( + height: 12, + ), + FormBuilderSwitch( + initialValue: primaryaContact, + activeColor: second, + onChanged: (value) { + setState(() { + active = value!; + }); + }, + decoration: normalTextFieldStyle("Active?", ''), + name: 'overseas', + title: Text(active ? "YES" : "NO"), + ), + const SizedBox( + height: 24, + ), + SizedBox( + height: 60, + width: double.infinity, + child: ElevatedButton( + onPressed: () { + if (formKey.currentState!.saveAndValidate()) { + numberMail = + formKey.currentState!.value['number-mail']; + CommService commService = + selectedCommServiceProvider!; + ContactInfo contactInfo = ContactInfo( + id: null, + active: active, + primary: primaryaContact, + numbermail: numberMail, + commService: commService); + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add( + AddContactInformation( + contactInfo: contactInfo, + profileId: widget.profileId, + token: widget.token)); + } + }, + style: + mainBtnStyle(primary, Colors.transparent, second), + child: const Text(submit), ), - SizedBox( - height: 60, - width: double.infinity, - child: ElevatedButton( - onPressed: () { - if (formKey.currentState!.saveAndValidate()) { - numberMail = - formKey.currentState!.value['number-mail']; - CommService commService = - selectedCommServiceProvider!; - ContactInfo contactInfo = ContactInfo( - id: null, - active: active, - primary: primaryaContact, - numbermail: numberMail, - commService: commService); - final progress = ProgressHUD.of(context); - progress!.showWithText("Loading..."); - context.read().add( - AddContactInformation( - contactInfo: contactInfo, - profileId: widget.profileId, - token: widget.token)); - } - }, - style: - mainBtnStyle(primary, Colors.transparent, second), - child: const Text(submit), - ), - ), - - ], - ), + ), + ], ); })), ); diff --git a/lib/screens/profile/components/basic_information/contact_information/edit_modal.dart b/lib/screens/profile/components/basic_information/contact_information/edit_modal.dart index a372064..f2e713d 100644 --- a/lib/screens/profile/components/basic_information/contact_information/edit_modal.dart +++ b/lib/screens/profile/components/basic_information/contact_information/edit_modal.dart @@ -35,7 +35,6 @@ class _EditContactInformationScreenState bool? primaryaContact; bool? active; - var mobileFormatter = MaskTextInputFormatter( mask: "+63 (###) ###-####", filter: {"#": RegExp(r"^[1-9][0-9]*$")}, @@ -51,9 +50,10 @@ class _EditContactInformationScreenState final numberMailController = TextEditingController(); @override void dispose() { - numberMailController.dispose(); + numberMailController.dispose(); super.dispose(); } + @override Widget build(BuildContext context) { return BlocBuilder( @@ -64,221 +64,218 @@ class _EditContactInformationScreenState commServiceProviders = state.commServiceProviders; primaryaContact = state.contactInfo.primary; active = state.contactInfo.active; - numberMailController.text = state.contactInfo.numbermail!; + numberMailController.text = state.contactInfo.numbermail!; return Padding( padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 24), child: FormBuilder( key: formKey, - child: Column( + child: ListView( children: [ - Flexible( - child: ListView( - children: [ - StatefulBuilder(builder: (context, setState) { - return Column(children: [ - ////Service Type - DropdownButtonFormField( - isExpanded: true, - validator: FormBuilderValidators.required( - errorText: "This field is required"), - value: selectedServiceType, - items: state.serviceTypes - .map>( - (ServiceType e) { - return DropdownMenuItem( - value: e, child: Text(e.name!)); - }).toList(), - decoration: - normalTextFieldStyle("Service Type*", ""), - onChanged: (var service) async { - if (selectedServiceType!.id != service!.id) { - selectedServiceType = service; - setState(() { - callServiceType = true; - callServiceType = true; - - numberMailController.text = ""; - }); - commServiceProviders = await ContactService - .instance - .getServiceProvider( - serviceTypeId: - selectedServiceType!.id!); - selectedCommProvider = null; - setState(() { - setState(() { - callServiceType = false; - }); - }); - } - }), - const SizedBox( - height: 12, - ), - ////Service Provider - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: callServiceType, - child: DropdownButtonFormField( - isExpanded: true, - value: selectedCommProvider, - validator: FormBuilderValidators.required( - errorText: "This field is required"), - items: commServiceProviders.isEmpty - ? [] - : commServiceProviders - .map>( - (CommService e) { - return DropdownMenuItem( - value: e, - child: Text(e.serviceProvider! - .agency!.name!)); - }).toList(), - decoration: normalTextFieldStyle( - "Communication Service *", ""), - onChanged: (var commServiceProvider) { - selectedCommProvider = commServiceProvider; - }), - ), - ), - selectedServiceType != null - ? selectedServiceType?.id == 2 - //// Landline - ? FormBuilderTextField( - controller: numberMailController, - name: 'number-mail', - - inputFormatters: [landLineFormatter], - validator: FormBuilderValidators.required( - errorText: "This field is required"), - decoration: normalTextFieldStyle( - "Landline number *", - "(area code) xxx - xxxx"), - ) - : selectedServiceType!.id == 1 || - selectedServiceType!.id == 19 - //// Mobile number - ? FormBuilderTextField( - controller: numberMailController, - name: 'number-mail', - inputFormatters: [mobileFormatter], - - validator: + StatefulBuilder(builder: (context, setState) { + return Column(children: [ + ////Service Type + DropdownButtonFormField( + isExpanded: true, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + value: selectedServiceType, + items: state.serviceTypes + .map>( + (ServiceType e) { + return DropdownMenuItem( + value: e, child: Text(e.name!)); + }).toList(), + decoration: + normalTextFieldStyle("Service Type*", ""), + onChanged: (var service) async { + if (selectedServiceType!.id != service!.id) { + selectedServiceType = service; + setState(() { + callServiceType = true; + callServiceType = true; + + numberMailController.text = ""; + }); + try { + commServiceProviders = await ContactService + .instance + .getServiceProvider( + serviceTypeId: + selectedServiceType!.id!); + } catch (e) { + context.read().add( + CallErrorEvent(message: e.toString())); + } + selectedCommProvider = null; + setState(() { + setState(() { + callServiceType = false; + }); + }); + } + }), + const SizedBox( + height: 12, + ), + ////Service Provider + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: callServiceType, + child: DropdownButtonFormField( + isExpanded: true, + value: selectedCommProvider, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + items: commServiceProviders.isEmpty + ? [] + : commServiceProviders + .map>( + (CommService e) { + return DropdownMenuItem( + value: e, + child: Text(e.serviceProvider! + .agency!.name!)); + }).toList(), + decoration: normalTextFieldStyle( + "Communication Service *", ""), + onChanged: (var commServiceProvider) { + selectedCommProvider = commServiceProvider; + }), + ), + ), + selectedServiceType != null + ? selectedServiceType?.id == 2 + //// Landline + ? FormBuilderTextField( + controller: numberMailController, + name: 'number-mail', + inputFormatters: [landLineFormatter], + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: normalTextFieldStyle( + "Landline number *", + "(area code) xxx - xxxx"), + ) + : selectedServiceType!.id == 1 || + selectedServiceType!.id == 19 + //// Mobile number + ? FormBuilderTextField( + controller: numberMailController, + name: 'number-mail', + inputFormatters: [mobileFormatter], + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + decoration: normalTextFieldStyle( + "Mobile number *", + "+63 (9xx) xxx - xxxx"), + ) + : selectedServiceType!.id == 4 + ////Social Media + ? FormBuilderTextField( + controller: numberMailController, + name: 'number-mail', + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + decoration: normalTextFieldStyle( + "Account ID / Username *", ""), + ) + : selectedServiceType!.id == 3 + ////Email Address + ? FormBuilderTextField( + controller: + numberMailController, + name: 'number-mail', + validator: FormBuilderValidators + .compose([ + FormBuilderValidators.email( + errorText: + "Input vaild email"), FormBuilderValidators.required( errorText: - "This field is required"), - decoration: normalTextFieldStyle( - "Mobile number *", - "+63 (9xx) xxx - xxxx"), - ) - : selectedServiceType!.id == 4 - ////Social Media - ? FormBuilderTextField( - controller: numberMailController, - name: 'number-mail', - - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - decoration: normalTextFieldStyle( - "Account ID / Username *", ""), - ) - : selectedServiceType!.id == 3 - ////Email Address - ? FormBuilderTextField( - controller: numberMailController, - name: 'number-mail', - - validator: FormBuilderValidators - .compose([ - FormBuilderValidators.email( - errorText: - "Input vaild email"), - FormBuilderValidators.required( - errorText: - "This field is required") - ]), - decoration: - normalTextFieldStyle( - "Email Address*", ""), - ) - : Container() - : const SizedBox(), - ]); - }), - SizedBox( - height: selectedServiceType != null ? 12 : 0, - ), - //// Primary - StatefulBuilder(builder: (context, setState) { - return FormBuilderSwitch( - initialValue: primaryaContact, - activeColor: second, - onChanged: (value) { - setState(() { - primaryaContact = value; - }); - }, - decoration: normalTextFieldStyle("", ''), - name: 'primary', - title: const Text("Primary ?"), - ); - }), - //// Active - const SizedBox( - height: 12, - ), - StatefulBuilder(builder: (context, setState) { - return FormBuilderSwitch( - initialValue: active, - activeColor: second, - onChanged: (value) { - setState(() { - active = value; - }); - }, - decoration: normalTextFieldStyle("", ''), - name: 'active', - title: const Text("Active ?"), - ); - }), - - - ], - ), + "This field is required") + ]), + decoration: + normalTextFieldStyle( + "Email Address*", ""), + ) + : Container() + : const SizedBox(), + ]); + }), + SizedBox( + height: selectedServiceType != null ? 12 : 0, ), - SizedBox( - height: 60, - width: double.infinity, - child: ElevatedButton( - onPressed: () { - if (formKey.currentState!.saveAndValidate()) { - numberMail = - formKey.currentState!.value['number-mail']; - CommService commService = selectedCommProvider!; - ContactInfo contactInfo = ContactInfo( - id: state.contactInfo.id, - active: active, - primary: primaryaContact, - numbermail: numberMail, - commService: commService); - final progress = ProgressHUD.of(context); - progress!.showWithText("Loading..."); - context.read().add( - EditContactInformation( - contactInfo: contactInfo, - profileId: widget.profileId, - token: widget.token)); - } - }, - style: - mainBtnStyle(primary, Colors.transparent, second), - child: const Text(submit), - ), - ) + //// Primary + StatefulBuilder(builder: (context, setState) { + return FormBuilderSwitch( + initialValue: primaryaContact, + activeColor: second, + onChanged: (value) { + setState(() { + primaryaContact = value; + }); + }, + decoration: normalTextFieldStyle("", ''), + name: 'primary', + title: const Text("Primary ?"), + ); + }), + //// Active + const SizedBox( + height: 12, + ), + StatefulBuilder(builder: (context, setState) { + return FormBuilderSwitch( + initialValue: active, + activeColor: second, + onChanged: (value) { + setState(() { + active = value; + }); + }, + decoration: normalTextFieldStyle("", ''), + name: 'active', + title: const Text("Active ?"), + ); + }), + const SizedBox( + height: 24, + ), + SizedBox( + height: 60, + width: double.infinity, + child: ElevatedButton( + onPressed: () { + if (formKey.currentState!.saveAndValidate()) { + numberMail = + formKey.currentState!.value['number-mail']; + CommService commService = selectedCommProvider!; + ContactInfo contactInfo = ContactInfo( + id: state.contactInfo.id, + active: active, + primary: primaryaContact, + numbermail: numberMail, + commService: commService); + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add( + EditContactInformation( + contactInfo: contactInfo, + profileId: widget.profileId, + token: widget.token)); + } + }, + style: + mainBtnStyle(primary, Colors.transparent, second), + child: const Text(submit), + ), + ) ], )), ); diff --git a/lib/screens/profile/components/basic_information/contact_information_screen.dart b/lib/screens/profile/components/basic_information/contact_information_screen.dart index 2d666cd..05f3325 100644 --- a/lib/screens/profile/components/basic_information/contact_information_screen.dart +++ b/lib/screens/profile/components/basic_information/contact_information_screen.dart @@ -28,7 +28,7 @@ class ContactInformationScreen extends StatelessWidget { String token; return SafeArea( child: Scaffold( - resizeToAvoidBottomInset: false, + resizeToAvoidBottomInset: true, appBar: AppBar( title: context.watch().state is ContactAddingState ? const Text("Add Contact") diff --git a/lib/screens/profile/components/basic_information/family/child_add_modal.dart b/lib/screens/profile/components/basic_information/family/child_add_modal.dart index 3c1afe2..f1dac9e 100644 --- a/lib/screens/profile/components/basic_information/family/child_add_modal.dart +++ b/lib/screens/profile/components/basic_information/family/child_add_modal.dart @@ -131,7 +131,7 @@ class _ChildAlertState extends State { Icons.date_range, color: Colors.black87, )), - firstDate: DateTime(1970), + firstDate: DateTime(1900), lastDate: DateTime(2100), icon: const Icon(Icons.date_range), ), diff --git a/lib/screens/profile/components/basic_information/family/child_edit_modal.dart b/lib/screens/profile/components/basic_information/family/child_edit_modal.dart index 768f596..29f0347 100644 --- a/lib/screens/profile/components/basic_information/family/child_edit_modal.dart +++ b/lib/screens/profile/components/basic_information/family/child_edit_modal.dart @@ -7,7 +7,6 @@ import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:form_builder_validators/form_builder_validators.dart'; import 'package:unit2/bloc/profile/family/family_bloc.dart'; import 'package:unit2/model/profile/family_backround.dart'; - import '../../../../../model/utils/position.dart'; import '../../../../../theme-data.dart/btn-style.dart'; import '../../../../../theme-data.dart/colors.dart'; @@ -15,7 +14,6 @@ import '../../../../../theme-data.dart/form-style.dart'; import '../../../../../utils/formatters.dart'; import '../../../../../utils/global.dart'; import '../../../../../utils/text_container.dart'; -import '../../../../../utils/validators.dart'; class ChildEditAlert extends StatefulWidget { final FamilyBackground familyBackground; @@ -150,7 +148,7 @@ class _ChildEditAlertState extends State { Icons.date_range, color: Colors.black87, )), - firstDate: DateTime(1970), + firstDate: DateTime(1900), lastDate: DateTime(2100), icon: const Icon(Icons.date_range), ), diff --git a/lib/screens/profile/components/basic_information/family/father_add_modal.dart b/lib/screens/profile/components/basic_information/family/father_add_modal.dart index afd0032..4d8fd9e 100644 --- a/lib/screens/profile/components/basic_information/family/father_add_modal.dart +++ b/lib/screens/profile/components/basic_information/family/father_add_modal.dart @@ -129,7 +129,7 @@ class _FatherAlertState extends State { Icons.date_range, color: Colors.black87, )), - firstDate: DateTime(1970), + firstDate: DateTime(1900), lastDate: DateTime(2100), icon: const Icon(Icons.date_range), ), diff --git a/lib/screens/profile/components/basic_information/family/father_edit_modal.dart b/lib/screens/profile/components/basic_information/family/father_edit_modal.dart index 939e014..cd7aa52 100644 --- a/lib/screens/profile/components/basic_information/family/father_edit_modal.dart +++ b/lib/screens/profile/components/basic_information/family/father_edit_modal.dart @@ -149,7 +149,7 @@ class _FatherEditAlertState extends State { Icons.date_range, color: Colors.black87, )), - firstDate: DateTime(1970), + firstDate: DateTime(1900), lastDate: DateTime(2100), icon: const Icon(Icons.date_range), ), diff --git a/lib/screens/profile/components/basic_information/family/mother_add_modal.dart b/lib/screens/profile/components/basic_information/family/mother_add_modal.dart index 19295cd..e91b965 100644 --- a/lib/screens/profile/components/basic_information/family/mother_add_modal.dart +++ b/lib/screens/profile/components/basic_information/family/mother_add_modal.dart @@ -129,7 +129,7 @@ class _MotherAlertState extends State { Icons.date_range, color: Colors.black87, )), - firstDate: DateTime(1970), + firstDate: DateTime(1900), lastDate: DateTime(2100), icon: const Icon(Icons.date_range), ), diff --git a/lib/screens/profile/components/basic_information/family/mother_edit_modal.dart b/lib/screens/profile/components/basic_information/family/mother_edit_modal.dart index 73a74ed..f6d287b 100644 --- a/lib/screens/profile/components/basic_information/family/mother_edit_modal.dart +++ b/lib/screens/profile/components/basic_information/family/mother_edit_modal.dart @@ -139,7 +139,7 @@ class _MotherEditAlertState extends State { Icons.date_range, color: Colors.black87, )), - firstDate: DateTime(1970), + firstDate: DateTime(1900), lastDate: DateTime(2100), icon: const Icon(Icons.date_range), ), diff --git a/lib/screens/profile/components/basic_information/family/related_add_modal.dart b/lib/screens/profile/components/basic_information/family/related_add_modal.dart index 12f0170..4f27934 100644 --- a/lib/screens/profile/components/basic_information/family/related_add_modal.dart +++ b/lib/screens/profile/components/basic_information/family/related_add_modal.dart @@ -154,7 +154,7 @@ bdayController.dispose(); Icons.date_range, color: Colors.black87, )), - firstDate: DateTime(1970), + firstDate: DateTime(1900), lastDate: DateTime(2100), icon: const Icon(Icons.date_range), ), diff --git a/lib/screens/profile/components/basic_information/family/related_edit_modal.dart b/lib/screens/profile/components/basic_information/family/related_edit_modal.dart index 97edaf4..72849db 100644 --- a/lib/screens/profile/components/basic_information/family/related_edit_modal.dart +++ b/lib/screens/profile/components/basic_information/family/related_edit_modal.dart @@ -172,7 +172,7 @@ class _RelatedEditAlertState extends State { Icons.date_range, color: Colors.black87, )), - firstDate: DateTime(1970), + firstDate: DateTime(1900), lastDate: DateTime(2100), icon: const Icon(Icons.date_range), ), diff --git a/lib/screens/profile/components/basic_information/identification/add_modal.dart b/lib/screens/profile/components/basic_information/identification/add_modal.dart index ef9272e..3ef7946 100644 --- a/lib/screens/profile/components/basic_information/identification/add_modal.dart +++ b/lib/screens/profile/components/basic_information/identification/add_modal.dart @@ -146,508 +146,568 @@ class _AddIdentificationScreenState extends State { key: formKey, child: SizedBox( height: screenHeight * 90, - child: Column( + child: ListView( children: [ - Flexible( - child: ListView( + StatefulBuilder(builder: (context, setState) { + return Column( children: [ - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - FormBuilderDropdown( - validator: FormBuilderValidators.required( - errorText: "This field is required"), - isExpanded: true, - decoration: normalTextFieldStyle( - "Select Agency", "Select Agency"), - name: "requiredAgency", - items: requiredAgency! - .map>( - (Agency agency) { - return DropdownMenuItem( - value: agency, - child: Container( - width: double.infinity, - decoration: - box1().copyWith(boxShadow: []), - child: Text( - agency.name!, - maxLines: 3, - )), - ); - }).toList(), - onChanged: (value) { - selectedAgency = value; + FormBuilderDropdown( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + isExpanded: true, + decoration: normalTextFieldStyle( + "Select Agency", "Select Agency"), + name: "requiredAgency", + items: requiredAgency! + .map>( + (Agency agency) { + return DropdownMenuItem( + value: agency, + child: Container( + width: double.infinity, + decoration: + box1().copyWith(boxShadow: []), + child: Text( + agency.name!, + maxLines: 3, + )), + ); + }).toList(), + onChanged: (value) { + selectedAgency = value; - if (value!.id == 0) { - setState(() { - otherAgency = true; - }); - } else { - isPrivate = value.privateEntity!; - setState(() { - otherAgency = false; - }); - } - }, - ), - SizedBox( - height: otherAgency ? 12 : 0, - ), - ////Other agency - SizedBox( - child: otherAgency - ? StatefulBuilder( - builder: (context, setState) { - return Column( - children: [ - ////Company - SizedBox( - child: SearchField( - inputFormatters: [ - UpperCaseTextFormatter() - ], - itemHeight: 100, - focusNode: - agencyFocusNode, - suggestions: state - .agencies - .map((Agency - agency) => - SearchFieldListItem( - agency.name!, - item: agency, - child: - ListTile( - title: Text( - agency - .name!, - overflow: - TextOverflow - .visible, - ), - subtitle: Text(agency.privateEntity == - true - ? "Private" - : agency.privateEntity == - false - ? "Government" - : ""), - ))) - .toList(), - searchInputDecoration: + if (value!.id == 0) { + setState(() { + otherAgency = true; + }); + } else { + isPrivate = value.privateEntity!; + setState(() { + otherAgency = false; + }); + } + }, + ), + SizedBox( + height: otherAgency ? 12 : 0, + ), + ////Other agency + SizedBox( + child: otherAgency + ? StatefulBuilder( + builder: (context, setState) { + return Column( + children: [ + ////Company + SizedBox( + child: SearchField( + inputFormatters: [ + UpperCaseTextFormatter() + ], + itemHeight: 100, + focusNode: + agencyFocusNode, + suggestions: state + .agencies + .map((Agency + agency) => + SearchFieldListItem( + agency.name!, + item: agency, + child: + ListTile( + title: Text( + agency + .name!, + overflow: + TextOverflow + .visible, + ), + subtitle: Text(agency.privateEntity == + true + ? "Private" + : agency.privateEntity == + false + ? "Government" + : ""), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle( + "Agency *", "") + .copyWith( + suffixIcon: + GestureDetector( + child: const Icon( + Icons.arrow_drop_down, + ), + onTap: () => + agencyFocusNode + .unfocus(), + )), + onSuggestionTap: + (agency) { + setState(() { + selectedAgency = + agency.item; + + if (selectedAgency! + .privateEntity == + null) { + showIsPrivateRadio = + true; + } else { + showIsPrivateRadio = + false; + } + + agencyFocusNode + .unfocus(); + }); + }, + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + emptyWidget: EmptyWidget( + controller: + addAgencyController, + onpressed: () { + setState(() { + Agency newAgency = Agency( + id: null, + name: addAgencyController + .text + .toUpperCase(), + category: + null, + privateEntity: + null); + + state.agencies + .insert(0, + newAgency); + selectedAgency = + newAgency; + addAgencyController + .text = ""; + showAgency = true; + + showIsPrivateRadio = + true; + + Navigator.pop( + context); + }); + }, + title: "Add Agency")), + ), + + SizedBox( + height: showAgency ? 12 : 0, + ), + ////SHOW CATEGORY AGENCY + SizedBox( + child: showAgency + ? SearchField( + focusNode: + agencyCategoryFocusNode, + itemHeight: 70, + suggestions: state + .agencyCategory + .map((Category + category) => + SearchFieldListItem( + category + .name!, + item: + category, + child: + ListTile( + title: Text( + category.name!), + subtitle: Text(category + .industryClass! + .name!), + ))) + .toList(), + emptyWidget: + Container( + height: 100, + decoration: box1(), + child: const Center( + child: Text( + "No result found ...")), + ), + onSuggestionTap: + (agencyCategory) { + setState(() { + selectedCategoty = + agencyCategory + .item; + agencyCategoryFocusNode + .unfocus(); + selectedAgency = Agency( + id: null, + name: + selectedAgency! + .name, + category: + selectedCategoty, + privateEntity: + null); + }); + }, + searchInputDecoration: + normalTextFieldStyle( + "Category *", + "") + .copyWith( + suffixIcon: + GestureDetector( + child: const Icon( + Icons + .arrow_drop_down, + ), + onTap: () => + agencyCategoryFocusNode + .unfocus(), + )), + validator: (value) { + if (value! + .isEmpty) { + return "This field is required"; + } + return null; + }, + ) + : const SizedBox(), + ), + const SizedBox( + height: 12, + ), + ////PRVIATE SECTOR + SizedBox( + width: screenWidth, + child: showIsPrivateRadio + ? FormBuilderSwitch( + initialValue: + isPrivate, + title: Text( + isPrivate + ? "YES" + : "NO"), + decoration: normalTextFieldStyle( - "Agency *", "") - .copyWith( - suffixIcon: - GestureDetector( - child: const Icon( - Icons.arrow_drop_down, - ), - onTap: () => - agencyFocusNode - .unfocus(), - )), - onSuggestionTap: - (agency) { + "Private Entity?", + ""), + ////onvhange private sector + onChanged: (value) { setState(() { - selectedAgency = - agency.item; - - if (selectedAgency! - .privateEntity == - null) { - showIsPrivateRadio = - true; - } else { - showIsPrivateRadio = - false; - } - + isPrivate = + value!; + selectedAgency = Agency( + id: null, + name: selectedAgency! + .name, + category: + selectedCategoty, + privateEntity: + isPrivate); agencyFocusNode .unfocus(); + agencyCategoryFocusNode + .unfocus(); }); }, - validator: (agency) { - if (agency!.isEmpty) { - return "This field is required"; - } - return null; - }, - emptyWidget: EmptyWidget( - controller: - addAgencyController, - onpressed: () { - setState(() { - Agency newAgency = Agency( - id: null, - name: addAgencyController - .text - .toUpperCase(), - category: - null, - privateEntity: - null); - state.agencies - .insert(0, - newAgency); - selectedAgency = - newAgency; - addAgencyController - .text = ""; - showAgency = true; - - showIsPrivateRadio = - true; - - Navigator.pop( - context); - }); - }, - title: "Add Agency")), - ), - - SizedBox( - height: showAgency ? 12 : 0, - ), - ////SHOW CATEGORY AGENCY - SizedBox( - child: showAgency - ? SearchField( - focusNode: - agencyCategoryFocusNode, - itemHeight: 70, - suggestions: state - .agencyCategory - .map((Category - category) => - SearchFieldListItem( - category - .name!, - item: - category, - child: - ListTile( - title: Text( - category.name!), - subtitle: Text(category - .industryClass! - .name!), - ))) - .toList(), - emptyWidget: - Container( - height: 100, - decoration: box1(), - child: const Center( - child: Text( - "No result found ...")), - ), - onSuggestionTap: - (agencyCategory) { - setState(() { - selectedCategoty = - agencyCategory - .item; - agencyCategoryFocusNode - .unfocus(); - selectedAgency = Agency( - id: null, - name: - selectedAgency! - .name, - category: - selectedCategoty, - privateEntity: - null); - }); - }, - searchInputDecoration: - normalTextFieldStyle( - "Category *", - "") - .copyWith( - suffixIcon: - GestureDetector( - child: const Icon( - Icons - .arrow_drop_down, - ), - onTap: () => - agencyCategoryFocusNode - .unfocus(), - )), - validator: (value) { - if (value! - .isEmpty) { - return "This field is required"; - } - return null; - }, - ) - : const SizedBox(), - ), - const SizedBox( - height: 12, - ), - ////PRVIATE SECTOR - SizedBox( - width: screenWidth, - child: showIsPrivateRadio - ? FormBuilderSwitch( - initialValue: - isPrivate, - title: Text( - isPrivate - ? "YES" - : "NO"), - decoration: - normalTextFieldStyle( - "Private Entity?", - ""), - ////onvhange private sector - onChanged: (value) { - setState(() { - isPrivate = - value!; - selectedAgency = Agency( - id: null, - name: selectedAgency! - .name, - category: - selectedCategoty, - privateEntity: - isPrivate); - agencyFocusNode - .unfocus(); - agencyCategoryFocusNode - .unfocus(); - }); - }, - - name: 'isPrivate', - validator: - FormBuilderValidators - .required(), - ) - : const SizedBox()), - ], - ); - }) - : const SizedBox(), - ), - ], - ); - }), - SizedBox( - height: otherAgency ? 8 : 0, + name: 'isPrivate', + validator: + FormBuilderValidators + .required(), + ) + : const SizedBox()), + ], + ); + }) + : const SizedBox(), ), + ], + ); + }), + SizedBox( + height: otherAgency ? 8 : 0, + ), + const SizedBox( + height: 8, + ), + //// Identification numner + FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "identification_number", + decoration: normalTextFieldStyle( + "Identification Number *", ""), + ), + const SizedBox( + height: 12, + ), + StatefulBuilder(builder: (context, setState) { + return Row( + children: [ + //// Date Issued + Flexible( + flex: 1, + child: DateTimePicker( + type: DateTimePickerType.date, + controller: dateIssuedController, + use24HourFormat: false, + icon: const Icon(Icons.date_range), + selectableDayPredicate: (date) { + if (expirationDate != null && + expirationDate! + .microsecondsSinceEpoch <= + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, + onChanged: (value) { + setState(() { + issuedDate = DateTime.parse(value); + }); + }, + initialDate: expirationDate == null + ? DateTime.now() + : expirationDate!.subtract( + const Duration(days: 1)), + firstDate: DateTime(1990), + lastDate: DateTime(2100), + timeHintText: "Date Issued", + decoration: normalTextFieldStyle( + "Date Issued *", + "Date Issued *") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + )), const SizedBox( - height: 8, + width: 8, ), - //// Identification numner - FormBuilderTextField( - validator: FormBuilderValidators.required( - errorText: "This field is required"), - name: "identification_number", + //// Expiration Date + Flexible( + flex: 1, + child: DateTimePicker( + type: DateTimePickerType.date, + controller: expirationController, + use24HourFormat: false, + icon: const Icon(Icons.date_range), + firstDate: DateTime(1990), + lastDate: DateTime(2100), + selectableDayPredicate: (date) { + if (issuedDate != null && + issuedDate! + .microsecondsSinceEpoch >= + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, + timeHintText: "Expiration date", + decoration: normalTextFieldStyle( + "Expiration Date *", + "Expiration Date *") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + initialDate: issuedDate == null + ? DateTime.now() + : issuedDate! + .add(const Duration(days: 1)), + onChanged: (value) { + setState(() { + expirationDate = + DateTime.parse(value); + }); + }, + )), + ], + ); + }), + const SizedBox( + height: 12, + ), + //// as pdf reference + StatefulBuilder(builder: (context, setState) { + return FormBuilderSwitch( + initialValue: asPdfReference, + activeColor: second, + onChanged: (value) { + setState(() { + asPdfReference = value!; + }); + }, + decoration: normalTextFieldStyle( + "As PDF Reference?", ''), + name: 'pdf_reference', + title: Text(asPdfReference ? "YES" : "NO"), + ); + }), + const SizedBox( + height: 12, + ), + //// OVERSEAS + //// OVERSEAS + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, decoration: normalTextFieldStyle( - "Identification Number *", ""), + "Overseas Address?", ''), + name: 'overseas', + title: Text(overseas ? "YES" : "NO"), ), - const SizedBox( - height: 12, + SizedBox( + height: overseas == true ? 8 : 0, ), - StatefulBuilder(builder: (context, setState) { - return Row( - children: [ - //// Date Issued - Flexible( - flex: 1, - child: DateTimePicker( - type: DateTimePickerType.date, - controller: dateIssuedController, - use24HourFormat: false, - icon: const Icon(Icons.date_range), - selectableDayPredicate: (date) { - if (expirationDate != null && - expirationDate! - .microsecondsSinceEpoch <= - date.microsecondsSinceEpoch) { - return false; - } - return true; - }, - onChanged: (value) { - setState(() { - issuedDate = DateTime.parse(value); - }); - }, - initialDate: expirationDate == null - ? DateTime.now() - : expirationDate!.subtract( - const Duration(days: 1)), - firstDate: DateTime(1990), - lastDate: DateTime(2100), - timeHintText: "Date Issued", - decoration: normalTextFieldStyle( - "Date Issued *", - "Date Issued *") - .copyWith( - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - )), - )), - const SizedBox( - width: 8, - ), - //// Expiration Date - Flexible( - flex: 1, - child: DateTimePicker( - type: DateTimePickerType.date, - controller: expirationController, - use24HourFormat: false, - icon: const Icon(Icons.date_range), - firstDate: DateTime(1990), - lastDate: DateTime(2100), - selectableDayPredicate: (date) { - if (issuedDate != null && - issuedDate! - .microsecondsSinceEpoch >= - date.microsecondsSinceEpoch) { - return false; - } - return true; - }, - timeHintText: "Expiration date", - decoration: normalTextFieldStyle( - "Expiration Date *", - "Expiration Date *") - .copyWith( - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - )), - initialDate: issuedDate == null - ? DateTime.now() - : issuedDate! - .add(const Duration(days: 1)), - onChanged: (value) { - setState(() { - expirationDate = - DateTime.parse(value); - }); - }, - )), - ], - ); - }), - const SizedBox( - height: 12, - ), - //// as pdf reference - StatefulBuilder(builder: (context, setState) { - return FormBuilderSwitch( - initialValue: asPdfReference, - activeColor: second, - onChanged: (value) { - setState(() { - asPdfReference = value!; - }); - }, - decoration: normalTextFieldStyle( - "As PDF Reference?", ''), - name: 'pdf_reference', - title: Text(asPdfReference ? "YES" : "NO"), - ); - }), - const SizedBox( - height: 12, - ), - //// OVERSEAS - //// OVERSEAS - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - setState(() { - overseas = value!; - }); - }, - decoration: normalTextFieldStyle( - "Overseas Address?", ''), - name: 'overseas', - title: Text(overseas ? "YES" : "NO"), - ), - SizedBox( - height: overseas == true ? 8 : 0, - ), - SizedBox( - child: overseas == false - ? Column( - children: [ - const SizedBox( - height: 12, - ), - ////REGION DROPDOWN - DropdownButtonFormField( - isExpanded: true, + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + DropdownButtonFormField( + isExpanded: true, + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + onChanged: + (Region? region) async { + if (selectedRegion != + region) { + setState(() { + provinceCall = true; + }); + selectedRegion = region; + try { + provinces = await LocationUtils + .instance + .getProvinces( + regionCode: + selectedRegion! + .code + .toString()); + selectedProvince = + provinces![0]; + setState(() { + provinceCall = false; + cityCall = true; + }); + try { + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code + .toString()); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + }); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read< + IdentificationBloc>() + .add(ShowErrorState( + message: e + .toString())); + } + } catch (e) { + context + .read< + IdentificationBloc>() + .add(ShowErrorState( + message: e + .toString())); + } + } + }, + value: selectedRegion, + decoration: + normalTextFieldStyle( + "Region*", "Region"), + items: state.regions.map< + DropdownMenuItem< + Region>>( + (Region region) { + return DropdownMenuItem< + Region>( + value: region, + child: Text( + region.description!)); + }).toList(), + ), + const SizedBox( + height: 12, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( autovalidateMode: AutovalidateMode .onUserInteraction, - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - onChanged: - (Region? region) async { - if (selectedRegion != - region) { + + isExpanded: true, + value: selectedProvince, + onChanged: (Province? + province) async { + if (selectedProvince != + province) { setState(() { - provinceCall = true; + cityCall = true; }); - selectedRegion = region; + selectedProvince = + province; try { - provinces = await LocationUtils + citymuns = await LocationUtils .instance - .getProvinces( - regionCode: - selectedRegion! - .code - .toString()); - selectedProvince = - provinces![0]; + .getCities( + code: selectedProvince! + .code + .toString()); + selectedMunicipality = + citymuns![0]; setState(() { - provinceCall = false; - cityCall = true; + cityCall = false; }); - try { - citymuns = await LocationUtils - .instance - .getCities( - code: selectedProvince! - .code - .toString()); - selectedMunicipality = - citymuns![0]; - setState(() { - cityCall = false; - }); - } catch (e) { - NavigationService - .navigatorKey - .currentContext - ?.read< - IdentificationBloc>() - .add(ShowErrorState( - message: e - .toString())); - } } catch (e) { context .read< @@ -658,178 +718,113 @@ class _AddIdentificationScreenState extends State { } } }, - value: selectedRegion, + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province + province) { + return DropdownMenuItem( + value: + province, + child: + FittedBox( + child: Text( + province + .description!), + )); + }).toList(), decoration: normalTextFieldStyle( - "Region*", "Region"), - items: state.regions.map< - DropdownMenuItem< - Region>>( - (Region region) { - return DropdownMenuItem< - Region>( - value: region, - child: Text( - region.description!)); - }).toList(), - ), - const SizedBox( - height: 12, - ), - //// PROVINCE DROPDOWN - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: provinceCall, - child: DropdownButtonFormField< - Province?>( - autovalidateMode: - AutovalidateMode - .onUserInteraction, - - isExpanded: true, - value: selectedProvince, - onChanged: (Province? - province) async { - if (selectedProvince != - province) { - setState(() { - cityCall = true; - }); - selectedProvince = - province; - try { - citymuns = await LocationUtils - .instance - .getCities( - code: selectedProvince! - .code - .toString()); - selectedMunicipality = - citymuns![0]; - setState(() { - cityCall = false; - }); - } catch (e) { - context - .read< - IdentificationBloc>() - .add(ShowErrorState( - message: e - .toString())); - } - } - }, - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - items: provinces == null - ? [] - : provinces!.map< - DropdownMenuItem< - Province>>( - (Province - province) { - return DropdownMenuItem( - value: - province, - child: - FittedBox( - child: Text( - province - .description!), - )); - }).toList(), - decoration: - normalTextFieldStyle( - "Province*", - "Province")), - ), - ), - ////CITY MUNICIPALITY - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: cityCall, - child: - DropdownButtonFormField< - CityMunicipality>( - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - isExpanded: true, - onChanged: - (CityMunicipality? - city) { - if (selectedMunicipality != - city) { - selectedMunicipality = - city; - } - }, - decoration: - normalTextFieldStyle( - "Municipality*", - "Municipality"), - value: selectedMunicipality, - items: citymuns == null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality - c) { - return DropdownMenuItem( - value: c, - child: Text(c - .description!)); - }).toList(), - ), - ), - ), - ], - ) - //// COUNTRY DROPDOWN - : SizedBox( - height: 60, - child: DropdownButtonFormField< - Country>( - isExpanded: true, + "Province*", + "Province")), + ), + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: + DropdownButtonFormField< + CityMunicipality>( validator: FormBuilderValidators .required( errorText: "This field is required"), - items: state.countries.map< - DropdownMenuItem< - Country>>( - (Country country) { - return DropdownMenuItem< - Country>( - value: country, - child: FittedBox( - child: Text( - country.name!))); - }).toList(), - value: selectedCountry, - decoration: normalTextFieldStyle( - "Country*", "Country"), - onChanged: (Country? value) { - selectedCountry = value; + isExpanded: true, + onChanged: + (CityMunicipality? + city) { + if (selectedMunicipality != + city) { + selectedMunicipality = + city; + } }, + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality + c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), ), ), - ), - ], - ); - }), + ), + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( + height: 60, + child: DropdownButtonFormField< + Country>( + isExpanded: true, + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + items: state.countries.map< + DropdownMenuItem< + Country>>( + (Country country) { + return DropdownMenuItem< + Country>( + value: country, + child: FittedBox( + child: Text( + country.name!))); + }).toList(), + value: selectedCountry, + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) { + selectedCountry = value; + }, + ), + ), + ), ], - ), - ), - SizedBox( + ); + }), + const SizedBox(height: 8,), + SizedBox( width: double.infinity, height: 60, child: ElevatedButton( @@ -895,9 +890,8 @@ class _AddIdentificationScreenState extends State { }, child: const Text(submit)), ), - const SizedBox( - height: 24, - ), + + ], ), )), diff --git a/lib/screens/profile/components/basic_information/identification/edit_modal.dart b/lib/screens/profile/components/basic_information/identification/edit_modal.dart index 626458a..5a7d0b7 100644 --- a/lib/screens/profile/components/basic_information/identification/edit_modal.dart +++ b/lib/screens/profile/components/basic_information/identification/edit_modal.dart @@ -87,381 +87,375 @@ class _EditIdentificationScreenState extends State { key: formKey, child: SizedBox( height: screenHeight * 90, - child: Column( + child: ListView( children: [ - Flexible( - child: ListView( - children: [ - FormBuilderTextField( - initialValue: state.identification.agency!.name, - enabled: false, - name: "", - decoration: normalTextFieldStyle("", "").copyWith( - filled: true, fillColor: Colors.black12), - ), - SizedBox( - height: otherAgency ? 8 : 0, - ), - - const SizedBox( - height: 12, - ), - //// Identification numner - FormBuilderTextField( - initialValue: - state.identification.identificationNumber, - validator: FormBuilderValidators.required( - errorText: "This field is required"), - name: "identification_number", - decoration: normalTextFieldStyle( - "Identification Number *", ""), - ), - const SizedBox( - height: 12, - ), - StatefulBuilder( - builder: (context,setState) { - return Row( - children: [ - //// Date Issued - Flexible( - flex: 1, - child: DateTimePicker( - controller: dateIssuedController, - use24HourFormat: false, - icon: const Icon(Icons.date_range), - firstDate: DateTime(1990), - lastDate: DateTime(2100), - timeHintText: "Date Issued", - decoration: normalTextFieldStyle( - "Date Issued ", "Date Issued *") - .copyWith( - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - )), - onChanged: (value){ - setState((){ - issuedDate = value; - }); - }, - selectableDayPredicate: (date) { - if ((expDate != "null" && expDate != null) && - DateTime.tryParse(expDate!)! - .microsecondsSinceEpoch <= - date.microsecondsSinceEpoch) { - return false; - } - return true; - }, - initialDate: expDate == "null" || expDate == null - ? DateTime.now() - : DateTime.tryParse(expDate!)?.subtract( - const Duration(days: 1)), - )), - - const SizedBox( - width: 12, - ), - //// Expiration Date - Flexible( - flex: 1, - child: DateTimePicker( - controller: expirationController, - use24HourFormat: false, - icon: const Icon(Icons.date_range), - firstDate: DateTime(1990), - lastDate: DateTime(2100), - timeHintText: "Expiration date", - onChanged: (value){ - setState((){ - expDate = value; - }); - }, - decoration: normalTextFieldStyle( - "Expiration Date", - "Expiration Date") - .copyWith( - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - )), - selectableDayPredicate: (date) { - if ((issuedDate != "null" && issuedDate != null) && - DateTime.tryParse(issuedDate!)! - .microsecondsSinceEpoch >= - date.microsecondsSinceEpoch) { - return false; - } - return true; - }, - initialDate: issuedDate == null && issuedDate == "null" - ? DateTime.now() - : DateTime.tryParse(issuedDate!)?.add( - const Duration(days: 1)), - )), - ], - ); - } - ), - const SizedBox( - height: 12, - ), - - //// OVERSEAS - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - setState(() { - overseas = value!; + FormBuilderTextField( + initialValue: state.identification.agency!.name, + enabled: false, + name: "", + decoration: normalTextFieldStyle("", "").copyWith( + filled: true, fillColor: Colors.black12), + ), + SizedBox( + height: otherAgency ? 8 : 0, + ), + + const SizedBox( + height: 12, + ), + //// Identification numner + FormBuilderTextField( + initialValue: + state.identification.identificationNumber, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "identification_number", + decoration: normalTextFieldStyle( + "Identification Number *", ""), + ), + const SizedBox( + height: 12, + ), + StatefulBuilder( + builder: (context,setState) { + return Row( + children: [ + //// Date Issued + Flexible( + flex: 1, + child: DateTimePicker( + controller: dateIssuedController, + use24HourFormat: false, + icon: const Icon(Icons.date_range), + firstDate: DateTime(1990), + lastDate: DateTime(2100), + timeHintText: "Date Issued", + decoration: normalTextFieldStyle( + "Date Issued ", "Date Issued *") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + onChanged: (value){ + setState((){ + issuedDate = value; + }); + }, + selectableDayPredicate: (date) { + if ((expDate != "null" && expDate != null) && + DateTime.tryParse(expDate!)! + .microsecondsSinceEpoch <= + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, + initialDate: expDate == "null" || expDate == null + ? DateTime.now() + : DateTime.tryParse(expDate!)?.subtract( + const Duration(days: 1)), + )), + + const SizedBox( + width: 12, + ), + //// Expiration Date + Flexible( + flex: 1, + child: DateTimePicker( + controller: expirationController, + use24HourFormat: false, + icon: const Icon(Icons.date_range), + firstDate: DateTime(1990), + lastDate: DateTime(2100), + timeHintText: "Expiration date", + onChanged: (value){ + setState((){ + expDate = value; }); }, decoration: normalTextFieldStyle( - "Overseas Address?", ''), - name: 'overseas', - title: Text(overseas ? "YES" : "NO"), - ), - SizedBox( - height: overseas == true ? 12 : 0, - ), - SizedBox( - child: overseas == false - ? Column( - children: [ - const SizedBox( - height: 12, - ), - ////REGION DROPDOWN - DropdownButtonFormField( + "Expiration Date", + "Expiration Date") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + selectableDayPredicate: (date) { + if ((issuedDate != "null" && issuedDate != null) && + DateTime.tryParse(issuedDate!)! + .microsecondsSinceEpoch >= + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, + initialDate: issuedDate == null && issuedDate == "null" + ? DateTime.now() + : DateTime.tryParse(issuedDate!)?.add( + const Duration(days: 1)), + )), + ], + ); + } + ), + const SizedBox( + height: 12, + ), + + //// OVERSEAS + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: normalTextFieldStyle( + "Overseas Address?", ''), + name: 'overseas', + title: Text(overseas ? "YES" : "NO"), + ), + SizedBox( + height: overseas == true ? 12 : 0, + ), + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + DropdownButtonFormField( + isExpanded: true, + autovalidateMode: AutovalidateMode + .onUserInteraction, + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + onChanged: + (Region? region) async { + if (selectedRegion != region) { + setState(() { + provinceCall = true; + }); + selectedRegion = region; + try { + provinces = await LocationUtils + .instance + .getProvinces( + regionCode: + selectedRegion! + .code + .toString()); + } catch (e) { + context + .read< + IdentificationBloc>() + .add(ShowErrorState( + message: + e.toString())); + } + selectedProvince = + provinces![0]; + setState(() { + provinceCall = false; + cityCall = true; + }); + try { + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code + .toString()); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + }); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read< + IdentificationBloc>() + .add(ShowErrorState( + message: e + .toString())); + } + + } + }, + value: selectedRegion, + decoration: normalTextFieldStyle( + "Region*", "Region"), + items: state.regions.map< + DropdownMenuItem>( + (Region region) { + return DropdownMenuItem( + value: region, + child: Text( + region.description!)); + }).toList(), + ), + const SizedBox( + height: 12, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( + value: selectedProvince, + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null + ? 'required' + : null, isExpanded: true, - autovalidateMode: AutovalidateMode - .onUserInteraction, - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - onChanged: - (Region? region) async { - if (selectedRegion != region) { + onChanged: (Province? + province) async { + if (selectedProvince != + province) { setState(() { - provinceCall = true; + cityCall = true; }); - selectedRegion = region; + selectedProvince = + province; try { - provinces = await LocationUtils + citymuns = await LocationUtils .instance - .getProvinces( - regionCode: - selectedRegion! - .code - .toString()); - } catch (e) { + .getCities( + code: selectedProvince! + .code + .toString()); + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + }); + } catch (e) { context .read< IdentificationBloc>() .add(ShowErrorState( - message: - e.toString())); + message: e + .toString())); } - selectedProvince = - provinces![0]; - setState(() { - provinceCall = false; - cityCall = true; - }); - try { - citymuns = await LocationUtils - .instance - .getCities( - code: selectedProvince! - .code - .toString()); - selectedMunicipality = - citymuns![0]; - setState(() { - cityCall = false; - }); - } catch (e) { - NavigationService - .navigatorKey - .currentContext - ?.read< - IdentificationBloc>() - .add(ShowErrorState( - message: e - .toString())); - } - } }, - value: selectedRegion, - decoration: normalTextFieldStyle( - "Region*", "Region"), - items: state.regions.map< - DropdownMenuItem>( - (Region region) { - return DropdownMenuItem( - value: region, - child: Text( - region.description!)); - }).toList(), - ), - const SizedBox( - height: 12, - ), - //// PROVINCE DROPDOWN - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: provinceCall, - child: DropdownButtonFormField< - Province?>( - value: selectedProvince, - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - onChanged: (Province? - province) async { - if (selectedProvince != + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province province) { - setState(() { - cityCall = true; - }); - selectedProvince = - province; - try { - citymuns = await LocationUtils - .instance - .getCities( - code: selectedProvince! - .code - .toString()); - selectedMunicipality = - citymuns![0]; - setState(() { - cityCall = false; - }); - } catch (e) { - context - .read< - IdentificationBloc>() - .add(ShowErrorState( - message: e - .toString())); - } - } - }, - items: provinces == null - ? [] - : provinces!.map< - DropdownMenuItem< - Province>>( - (Province - province) { - return DropdownMenuItem( - value: province, - child: - FittedBox( - child: Text( - province - .description!), - )); - }).toList(), - decoration: - normalTextFieldStyle( - "Province *", - "Province")), - ), - ), - ////CITY MUNICIPALITY - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: cityCall, - child: DropdownButtonFormField< - CityMunicipality>( - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - isExpanded: true, - onChanged: - (CityMunicipality? city) { - if (selectedMunicipality != - city) { - selectedMunicipality = - city; - } - }, - decoration: - normalTextFieldStyle( - "Municipality *", - "Municipality"), - value: selectedMunicipality, - items: citymuns == null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality c) { - return DropdownMenuItem( - value: c, - child: Text(c - .description!)); - }).toList(), - ), - ), - ), - ], - ) - //// COUNTRY DROPDOWN - : SizedBox( - height: 60, - child: - DropdownButtonFormField( - isExpanded: true, - validator: - FormBuilderValidators.required( + return DropdownMenuItem( + value: province, + child: + FittedBox( + child: Text( + province + .description!), + )); + }).toList(), + decoration: + normalTextFieldStyle( + "Province *", + "Province")), + ), + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: DropdownButtonFormField< + CityMunicipality>( + validator: FormBuilderValidators + .required( errorText: "This field is required"), - items: state.countries - .map>( - (Country country) { - return DropdownMenuItem( - value: country, - child: FittedBox( - child: - Text(country.name!))); - }).toList(), - value: selectedCountry?.id==175?null:selectedCountry, - decoration: normalTextFieldStyle( - "Country *", "Country"), - onChanged: (Country? value) { - selectedCountry = value; + isExpanded: true, + onChanged: + (CityMunicipality? city) { + if (selectedMunicipality != + city) { + selectedMunicipality = + city; + } }, + decoration: + normalTextFieldStyle( + "Municipality *", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), ), ), - ), - ], - ); - }), - - + ), + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( + height: 60, + child: + DropdownButtonFormField( + isExpanded: true, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + items: state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox( + child: + Text(country.name!))); + }).toList(), + value: selectedCountry?.id==175?null:selectedCountry, + decoration: normalTextFieldStyle( + "Country *", "Country"), + onChanged: (Country? value) { + selectedCountry = value; + }, + ), + ), + ), ], - ), - ), - SizedBox( + ); + }), + + const SizedBox(height: 8,), + SizedBox( width: double.infinity, height: 60, child: ElevatedButton( @@ -522,7 +516,6 @@ class _EditIdentificationScreenState extends State { }, child: const Text(submit)), ), - const SizedBox(height: 24,), ], ), )), diff --git a/lib/screens/profile/components/basic_information/identification_information_screen.dart b/lib/screens/profile/components/basic_information/identification_information_screen.dart index 4fc6104..eedd08d 100644 --- a/lib/screens/profile/components/basic_information/identification_information_screen.dart +++ b/lib/screens/profile/components/basic_information/identification_information_screen.dart @@ -3,20 +3,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; -import 'package:fluttericon/font_awesome_icons.dart'; import 'package:unit2/bloc/profile/primary_information/identification/identification_bloc.dart'; import 'package:unit2/bloc/profile/profile_bloc.dart'; -import 'package:unit2/model/profile/basic_information/identification_information.dart'; import 'package:unit2/screens/profile/components/basic_information/identification/add_modal.dart'; import 'package:unit2/screens/profile/components/basic_information/identification/edit_modal.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; -import 'package:unit2/utils/global.dart'; import 'package:unit2/utils/text_container.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; import 'package:unit2/widgets/empty_data.dart'; import 'package:unit2/widgets/error_state.dart'; - import '../../../../bloc/user/user_bloc.dart'; import '../../../../utils/alerts.dart'; import '../../../../widgets/Leadings/close_leading.dart'; @@ -29,7 +25,7 @@ class IdentificationsScreen extends StatelessWidget { String? token; int? profileId; return Scaffold( - resizeToAvoidBottomInset: false, + resizeToAvoidBottomInset: true, appBar: AppBar( title: context.watch().state is IdentificationAddingState ? const Text("Add Identification"):context.watch().state is IdentificationEditingState?const Text("Edit Identification"):const Text("Identifications"), diff --git a/lib/screens/profile/components/education/add_modal.dart b/lib/screens/profile/components/education/add_modal.dart index 1e6b8dc..163f8fb 100644 --- a/lib/screens/profile/components/education/add_modal.dart +++ b/lib/screens/profile/components/education/add_modal.dart @@ -88,379 +88,369 @@ class _AddEducationScreenState extends State { child: FormBuilder( key: formKey, child: SizedBox( - height: blockSizeVertical * 85, - child: Column( - children: [ - Flexible( - child: ListView(children: [ - //// LEVEL + child: ListView(children: [ + //// LEVEL + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderDropdown( + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: + normalTextFieldStyle("level*", "level"), + name: "education_level", + onChanged: (EducationLevel? level) { + setState(() { + selectedLevel = level; + }); + }, + items: educationLevel + .map>( + (EducationLevel level) { + return level.type == "label" + ? DropdownMenuItem( + enabled: false, + value: level, + child: Text(level.value.toUpperCase(), + style: const TextStyle( + color: Colors.black38))) + : DropdownMenuItem( + value: level, + enabled: true, + child: Text( + " ${level.value.toUpperCase()}")); + }).toList()), + const SizedBox( + height: 12, + ), + ////school StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - FormBuilderDropdown( - validator: FormBuilderValidators.required( - errorText: "This field is required"), - decoration: - normalTextFieldStyle("level*", "level"), - name: "education_level", - onChanged: (EducationLevel? level) { - setState(() { - selectedLevel = level; - }); - }, - items: educationLevel - .map>( - (EducationLevel level) { - return level.type == "label" - ? DropdownMenuItem( - enabled: false, - value: level, - child: Text(level.value.toUpperCase(), - style: const TextStyle( - color: Colors.black38))) - : DropdownMenuItem( - value: level, - enabled: true, - child: Text( - " ${level.value.toUpperCase()}")); - }).toList()), - const SizedBox( - height: 12, - ), - ////school - StatefulBuilder(builder: (context, setState) { - return SearchField( - inputFormatters: [UpperCaseTextFormatter()], - itemHeight: 70, - suggestionsDecoration: box1(), - suggestions: state.schools - .map((School school) => - SearchFieldListItem(school.name!, - item: school, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10), - child: ListTile( - title: Text(school.name!,overflow: TextOverflow.visible,)), - ))) - .toList(), - validator: (agency) { - if (agency!.isEmpty) { - return "This field is required"; - } - return null; - }, - focusNode: schoolFocusNode, - searchInputDecoration: - normalTextFieldStyle("School *", "").copyWith( - suffixIcon: IconButton( - icon: const Icon(Icons.arrow_drop_down), - onPressed: () { - schoolFocusNode.unfocus(); - }, - )), - onSuggestionTap: (school) { - setState(() { - selectedSchool = school.item; - schoolFocusNode.unfocus(); - }); - }, - emptyWidget: EmptyWidget( - title: "Add School", - controller: addSchoolController, - onpressed: () { - setState(() { - School newSchool = School( - id: null, - name: addSchoolController.text - .toUpperCase()); - state.schools.insert(0, newSchool); - addSchoolController.text = ""; - - Navigator.pop(context); - }); - }), - ); - }), - const SizedBox( - height: 12, - ), - ////Programs - Container( - child: selectedLevel != null && - selectedLevel!.group != 1 - ? SearchField( - inputFormatters: [ - UpperCaseTextFormatter() - ], - itemHeight: 100, - suggestionsDecoration: box1(), - suggestions: state.programs - .map((Course program) => - SearchFieldListItem( - program.program!, - item: program, - child: Padding( - padding: const EdgeInsets - .symmetric( - horizontal: 10), - child: ListTile( - title: Text( - program.program!,overflow: TextOverflow.visible,)), - ))) - .toList(), - validator: (agency) { - if (agency!.isEmpty) { - return "This field is required"; - } - return null; - }, - focusNode: programFocusNode, - searchInputDecoration: - normalTextFieldStyle( - "Course/Programs *", "") - .copyWith( - suffixIcon: GestureDetector( - onTap: () => programFocusNode.unfocus(), - child: const Icon( - Icons.arrow_drop_down), - )), - onSuggestionTap: (position) { - setState(() { - selectedProgram = position.item; - programFocusNode.unfocus(); - }); - }, - emptyWidget: EmptyWidget( - title: "Add Program", - controller: addProgramController, - onpressed: () { - setState(() { - Course newProgram = Course( - id: null, - program: addProgramController - .text - .toUpperCase()); - state.programs - .insert(0, newProgram); - addProgramController.text = ""; - - Navigator.pop(context); - }); - }), - ) - : Container()) - ], + return SearchField( + inputFormatters: [UpperCaseTextFormatter()], + itemHeight: 70, + suggestionsDecoration: box1(), + suggestions: state.schools + .map((School school) => + SearchFieldListItem(school.name!, + item: school, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10), + child: ListTile( + title: Text(school.name!,overflow: TextOverflow.visible,)), + ))) + .toList(), + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + focusNode: schoolFocusNode, + searchInputDecoration: + normalTextFieldStyle("School *", "").copyWith( + suffixIcon: IconButton( + icon: const Icon(Icons.arrow_drop_down), + onPressed: () { + schoolFocusNode.unfocus(); + }, + )), + onSuggestionTap: (school) { + setState(() { + selectedSchool = school.item; + schoolFocusNode.unfocus(); + }); + }, + emptyWidget: EmptyWidget( + title: "Add School", + controller: addSchoolController, + onpressed: () { + setState(() { + School newSchool = School( + id: null, + name: addSchoolController.text + .toUpperCase()); + state.schools.insert(0, newSchool); + addSchoolController.text = ""; + + Navigator.pop(context); + }); + }), ); }), const SizedBox( height: 12, ), - StatefulBuilder(builder: (context, setState) { - return Column( + ////Programs + Container( + child: selectedLevel != null && + selectedLevel!.group != 1 + ? SearchField( + inputFormatters: [ + UpperCaseTextFormatter() + ], + itemHeight: 100, + suggestionsDecoration: box1(), + suggestions: state.programs + .map((Course program) => + SearchFieldListItem( + program.program!, + item: program, + child: Padding( + padding: const EdgeInsets + .symmetric( + horizontal: 10), + child: ListTile( + title: Text( + program.program!,overflow: TextOverflow.visible,)), + ))) + .toList(), + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + focusNode: programFocusNode, + searchInputDecoration: + normalTextFieldStyle( + "Course/Programs *", "") + .copyWith( + suffixIcon: GestureDetector( + onTap: () => programFocusNode.unfocus(), + child: const Icon( + Icons.arrow_drop_down), + )), + onSuggestionTap: (position) { + setState(() { + selectedProgram = position.item; + programFocusNode.unfocus(); + }); + }, + emptyWidget: EmptyWidget( + title: "Add Program", + controller: addProgramController, + onpressed: () { + setState(() { + Course newProgram = Course( + id: null, + program: addProgramController + .text + .toUpperCase()); + state.programs + .insert(0, newProgram); + addProgramController.text = ""; + + Navigator.pop(context); + }); + }), + ) + : Container()) + ], + ); + }), + const SizedBox( + height: 12, + ), + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + //// GRADUATED SWITCH + FormBuilderSwitch( + initialValue: graduated, + activeColor: second, + onChanged: (value) { + setState(() { + graduated = value!; + if (graduated) { + unitsEarned = null; + } else { + yearGraduated.text = ""; + } + }); + }, + decoration: normalTextFieldStyle( + "Graduated?", 'Graduated?'), + name: 'graudated', + title: Text(graduated ? "YES" : "NO"), + ), + const SizedBox( + height: 12, + ), + ////FROM + SizedBox( + width: screenWidth, + child: Row( children: [ - //// GRADUATED SWITCH - FormBuilderSwitch( - initialValue: graduated, - activeColor: second, - onChanged: (value) { - setState(() { - graduated = value!; - if (graduated) { - unitsEarned = null; - } else { - yearGraduated.text = ""; - } - }); - }, - decoration: normalTextFieldStyle( - "Graduated?", 'Graduated?'), - name: 'graudated', - title: Text(graduated ? "YES" : "NO"), - ), - const SizedBox( - height: 12, - ), - ////FROM - SizedBox( - width: screenWidth, - child: Row( - children: [ - Flexible( - flex: 1, - child: FormBuilderTextField( - validator: FormBuilderValidators.required( - errorText: "This fied is required"), - decoration: - normalTextFieldStyle("from *", "from"), - name: "", - controller: fromController, - onTap: () { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - content: SizedBox( - width: 300, - height: 300, - child: YearPicker( - firstDate: DateTime( - DateTime.now().year - 100, - 1), - lastDate: DateTime( - DateTime.now().year + 100, - 1), - initialDate: DateTime.now(), - selectedDate: DateTime.now(), - onChanged: (DateTime dateTime) { - fromController.text = - dateTime.year.toString(); - Navigator.pop(context); - }, - ), - ), - ); - }, - ); - }, - ), - ), - const SizedBox( - width: 8, - ), - ////UNTIL - Flexible( - flex: 1, - child: FormBuilderTextField( - validator: FormBuilderValidators.required( - errorText: "This fied is required"), - decoration: normalTextFieldStyle( - "until *", "until"), - name: "", - controller: untilController, - onTap: () { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - content: SizedBox( - width: 300, - height: 300, - child: YearPicker( - firstDate: DateTime( - DateTime.now().year - 100, - 1), - lastDate: DateTime( - DateTime.now().year + 100, - 1), - initialDate: DateTime.now(), - selectedDate: DateTime.now(), - onChanged: (DateTime dateTime) { - untilController.text = - dateTime.year.toString(); - Navigator.pop(context); - }, - ), - ), - ); - }, - ); - }, - ), - ), - const SizedBox( - width: 8, - ), - ], + Flexible( + flex: 1, + child: FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This fied is required"), + decoration: + normalTextFieldStyle("from *", "from"), + name: "", + controller: fromController, + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + content: SizedBox( + width: 300, + height: 300, + child: YearPicker( + firstDate: DateTime( + DateTime.now().year - 100, + 1), + lastDate: DateTime( + DateTime.now().year + 100, + 1), + initialDate: DateTime.now(), + selectedDate: DateTime.now(), + onChanged: (DateTime dateTime) { + fromController.text = + dateTime.year.toString(); + Navigator.pop(context); + }, + ), + ), + ); + }, + ); + }, ), ), const SizedBox( - height: 12, + width: 8, ), - SizedBox( - child: graduated - ////GRADUATED YEAR - ? FormBuilderTextField( - validator: FormBuilderValidators.required( - errorText: "This fied is required"), - onTap: () { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - content: SizedBox( - width: 300, - height: 300, - child: YearPicker( - firstDate: DateTime( - DateTime.now().year - 100, - 1), - lastDate: DateTime( - DateTime.now().year + 100, - 1), - initialDate: DateTime.now(), - selectedDate: DateTime.now(), - onChanged: (DateTime dateTime) { - yearGraduated.text = - dateTime.year.toString(); - Navigator.pop(context); - }, - ), - ), - ); - }, - ); - }, - name: "year_graduated", - controller: yearGraduated, - decoration: normalTextFieldStyle( - "Year Graduated *", "Year Graduated *"), - ) - //// HIGHEST UNITS EARNED - : FormBuilderTextField( - validator: FormBuilderValidators.required( - errorText: "This fied is required"), - name: "units_earned", - decoration: normalTextFieldStyle( - "Highest Level/Units Earned *", - "Highest Level/Units Earned *")), + ////UNTIL + Flexible( + flex: 1, + child: FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This fied is required"), + decoration: normalTextFieldStyle( + "until *", "until"), + name: "", + controller: untilController, + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + content: SizedBox( + width: 300, + height: 300, + child: YearPicker( + firstDate: DateTime( + DateTime.now().year - 100, + 1), + lastDate: DateTime( + DateTime.now().year + 100, + 1), + initialDate: DateTime.now(), + selectedDate: DateTime.now(), + onChanged: (DateTime dateTime) { + untilController.text = + dateTime.year.toString(); + Navigator.pop(context); + }, + ), + ), + ); + }, + ); + }, + ), + ), + const SizedBox( + width: 8, ), ], - ); - }), - + ), + ), const SizedBox( height: 12, ), - //// HONORS - MultiSelectDropDown( - onOptionSelected: (List selectedOptions) { - selectedValueItem = selectedOptions; - }, - borderColor: Colors.grey, - borderWidth: 1, - borderRadius: 5, - hint: "Honors", - padding: const EdgeInsets.all(8), - options: valueItemHonorList, - selectionType: SelectionType.multi, - chipConfig: const ChipConfig(wrapType: WrapType.wrap), - dropdownHeight: 300, - optionTextStyle: const TextStyle(fontSize: 16), - selectedOptionIcon: const Icon(Icons.check_circle), + SizedBox( + child: graduated + ////GRADUATED YEAR + ? FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This fied is required"), + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + content: SizedBox( + width: 300, + height: 300, + child: YearPicker( + firstDate: DateTime( + DateTime.now().year - 100, + 1), + lastDate: DateTime( + DateTime.now().year + 100, + 1), + initialDate: DateTime.now(), + selectedDate: DateTime.now(), + onChanged: (DateTime dateTime) { + yearGraduated.text = + dateTime.year.toString(); + Navigator.pop(context); + }, + ), + ), + ); + }, + ); + }, + name: "year_graduated", + controller: yearGraduated, + decoration: normalTextFieldStyle( + "Year Graduated *", "Year Graduated *"), + ) + //// HIGHEST UNITS EARNED + : FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This fied is required"), + name: "units_earned", + decoration: normalTextFieldStyle( + "Highest Level/Units Earned *", + "Highest Level/Units Earned *")), ), - const SizedBox( - height: 25, - ), - ////sumit button - - - const SizedBox( - height: 20, - ), - ]), - ), - SizedBox( + ], + ); + }), + + const SizedBox( + height: 12, + ), + //// HONORS + MultiSelectDropDown( + onOptionSelected: (List selectedOptions) { + selectedValueItem = selectedOptions; + }, + borderColor: Colors.grey, + borderWidth: 1, + borderRadius: 5, + hint: "Honors", + padding: const EdgeInsets.all(8), + options: valueItemHonorList, + selectionType: SelectionType.multi, + chipConfig: const ChipConfig(wrapType: WrapType.wrap), + dropdownHeight: 300, + optionTextStyle: const TextStyle(fontSize: 16), + selectedOptionIcon: const Icon(Icons.check_circle), + ), + const SizedBox( + height: 25, + ), + ////sumit button + + SizedBox( width: double.infinity, height: 60, child: ElevatedButton( @@ -520,9 +510,8 @@ class _AddEducationScreenState extends State { child: const Text(submit)), ), - - ], - ), + + ]), ), ), ); diff --git a/lib/screens/profile/components/education/edit_modal.dart b/lib/screens/profile/components/education/edit_modal.dart index 44812cd..e5676be 100644 --- a/lib/screens/profile/components/education/edit_modal.dart +++ b/lib/screens/profile/components/education/edit_modal.dart @@ -115,403 +115,398 @@ class _EditEducationScreenState extends State { key: formKey, child: SizedBox( height: blockSizeVertical * 85, - child: Column( - children: [ - Flexible( - child: ListView(children: [ - - //// LEVEL + child: ListView(children: [ + + //// LEVEL + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderDropdown( + initialValue: selectedLevel, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: + normalTextFieldStyle("level*", "level"), + name: "education_level", + onChanged: (EducationLevel? level) { + setState(() { + selectedLevel = level; + }); + }, + items: educationLevel + .map>( + (EducationLevel level) { + return level.type == "label" + ? DropdownMenuItem( + enabled: false, + value: level, + child: Text(level.value.toUpperCase(), + style: const TextStyle( + color: Colors.black38))) + : DropdownMenuItem( + value: level, + enabled: true, + child: Text( + " ${level.value.toUpperCase()}")); + }).toList()), + const SizedBox( + height: 12, + ), + ////school StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - FormBuilderDropdown( - initialValue: selectedLevel, - validator: FormBuilderValidators.required( - errorText: "This field is required"), - decoration: - normalTextFieldStyle("level*", "level"), - name: "education_level", - onChanged: (EducationLevel? level) { - setState(() { - selectedLevel = level; - }); - }, - items: educationLevel - .map>( - (EducationLevel level) { - return level.type == "label" - ? DropdownMenuItem( - enabled: false, - value: level, - child: Text(level.value.toUpperCase(), - style: const TextStyle( - color: Colors.black38))) - : DropdownMenuItem( - value: level, - enabled: true, - child: Text( - " ${level.value.toUpperCase()}")); - }).toList()), - const SizedBox( - height: 12, - ), - ////school - StatefulBuilder(builder: (context, setState) { - return SearchField( - inputFormatters: [ - UpperCaseTextFormatter() - ], - - suggestionAction: SuggestionAction.next, - onSubmit: (p0) { - schoolFocusNode.unfocus(); - }, - controller: currentSchoolController, - itemHeight: 70, - suggestionsDecoration: box1(), - suggestions: state.schools - .map((School school) => - SearchFieldListItem(school.name!, - item: school, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10), - child: ListTile( - title: Text(school.name!,overflow: TextOverflow.visible,)), - ))) - .toList(), - validator: (agency) { - if (agency!.isEmpty) { - return "This field is required"; - } - return null; - }, - focusNode: schoolFocusNode, - searchInputDecoration: - normalTextFieldStyle("School *", "").copyWith( - suffixIcon: GestureDetector( - child: const Icon(Icons.arrow_drop_down), - onTap: () { - schoolFocusNode.unfocus(); - }, - )), - onSuggestionTap: (school) { - setState(() { - selectedSchool = school.item; - schoolFocusNode.unfocus(); - }); - }, - emptyWidget: EmptyWidget( - title: "Add School", - controller: addSchoolController, - onpressed: () { - setState(() { - School newSchool = School( - id: null, - name: addSchoolController.text - .toUpperCase()); - state.schools.insert(0, newSchool); - addSchoolController.text = ""; - - Navigator.pop(context); - }); - }), - ); - }), - const SizedBox( - height: 12, - ), - - ////Programs - - Container( - child: selectedLevel != null && - selectedLevel!.group != 1 - ? SearchField( - inputFormatters: [UpperCaseTextFormatter()], - suggestionAction: - SuggestionAction.unfocus, - controller: currentProgramController, - itemHeight: 70, - suggestionsDecoration: box1(), - suggestions: state.programs - .map((Course program) => - SearchFieldListItem( - program.program!, - item: program, - child: Padding( - padding: const EdgeInsets - .symmetric( - horizontal: 10), - child: ListTile( - title: Text( - program.program!,overflow: TextOverflow.visible,)), - ))) - .toList(), - validator: (agency) { - if (agency!.isEmpty) { - return "This field is required"; - } - return null; - }, - focusNode: programFocusNode, - searchInputDecoration: - normalTextFieldStyle( - "Course/Programs *", "") - .copyWith( - suffixIcon: IconButton( - icon:const Icon( - Icons.arrow_drop_down), onPressed: () { programFocusNode.unfocus(); },),), - onSuggestionTap: (position) { - setState(() { - selectedProgram = position.item; - programFocusNode.unfocus(); - }); - }, - emptyWidget: EmptyWidget( - title: "Add Program", - controller: addProgramController, - onpressed: () { - setState(() { - Course newProgram = Course( - id: null, - program: addProgramController - .text - .toUpperCase()); - state.programs - .insert(0, newProgram); - addProgramController.text = ""; - - Navigator.pop(context); - }); - }), - ) - : Container()), - SizedBox(height: selectedLevel != null && - selectedLevel!.group != 1?12:0,), - ], - ); - }), - - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - //// GRADUATED SWITCH - FormBuilderSwitch( - initialValue: graduated, - activeColor: second, - onChanged: (value) { + return SearchField( + inputFormatters: [ + UpperCaseTextFormatter() + ], + + suggestionAction: SuggestionAction.next, + onSubmit: (p0) { + schoolFocusNode.unfocus(); + }, + controller: currentSchoolController, + itemHeight: 70, + suggestionsDecoration: box1(), + suggestions: state.schools + .map((School school) => + SearchFieldListItem(school.name!, + item: school, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10), + child: ListTile( + title: Text(school.name!,overflow: TextOverflow.visible,)), + ))) + .toList(), + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + focusNode: schoolFocusNode, + searchInputDecoration: + normalTextFieldStyle("School *", "").copyWith( + suffixIcon: GestureDetector( + child: const Icon(Icons.arrow_drop_down), + onTap: () { + schoolFocusNode.unfocus(); + }, + )), + onSuggestionTap: (school) { + setState(() { + selectedSchool = school.item; + schoolFocusNode.unfocus(); + }); + }, + emptyWidget: EmptyWidget( + title: "Add School", + controller: addSchoolController, + onpressed: () { setState(() { - graduated = value!; - if (graduated) { - unitsController.text = ""; - } else { - yearGraduated.text = ""; - } + School newSchool = School( + id: null, + name: addSchoolController.text + .toUpperCase()); + state.schools.insert(0, newSchool); + addSchoolController.text = ""; + + Navigator.pop(context); }); - }, - decoration: normalTextFieldStyle( - "Graduated?", 'Graduated?'), - name: 'graudated', - title: Text(graduated ? "YES" : "NO"), - ), - const SizedBox( - height: 12, - ), - ////FROM - SizedBox( - width: screenWidth, - child: Row( - children: [ - Flexible( - flex: 1, - child: FormBuilderTextField( - validator: FormBuilderValidators.required( - errorText: "This fied is required"), - decoration: - normalTextFieldStyle("from *", "from"), - name: "", - controller: fromController, - onTap: () { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - content: SizedBox( - width: 300, - height: 300, - child: YearPicker( - firstDate: DateTime( - DateTime.now().year - 100, - 1), - lastDate: DateTime( - DateTime.now().year + 100, - 1), - initialDate: DateTime.now(), - selectedDate: DateTime.now(), - onChanged: (DateTime dateTime) { - fromController.text = - dateTime.year.toString(); - Navigator.pop(context); - }, - ), - ), - ); - }, - ); - }, - ), - ), - const SizedBox( - width: 8, - ), - ////UNTIL - Flexible( - flex: 1, - child: FormBuilderTextField( - validator: FormBuilderValidators.required( - errorText: "This fied is required"), - decoration: normalTextFieldStyle( - "until *", "until"), - name: "", - controller: untilController, - onTap: () { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - content: SizedBox( - width: 300, - height: 300, - child: YearPicker( - firstDate: DateTime( - DateTime.now().year - 100, - 1), - lastDate: DateTime( - DateTime.now().year + 100, - 1), - initialDate: DateTime.now(), - selectedDate: DateTime.now(), - onChanged: (DateTime dateTime) { - untilController.text = - dateTime.year.toString(); - Navigator.pop(context); - }, - ), - ), - ); - }, - ); - }, - ), - ), - - - ], - ), - - ), const SizedBox(height: 20,), - SizedBox( - - child: graduated - ////GRADUATED YEAR - ? FormBuilderTextField( - validator: - FormBuilderValidators.required( - errorText: - "This fied is required"), - onTap: () { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - content: SizedBox( - width: 300, - height: 300, - child: YearPicker( - firstDate: DateTime( - DateTime.now().year - - 100, - 1), - lastDate: DateTime( - DateTime.now().year + - 100, - 1), - initialDate: - DateTime.now(), - selectedDate: - DateTime.now(), - onChanged: - (DateTime dateTime) { - yearGraduated.text = - dateTime.year - .toString(); - Navigator.pop(context); - }, - ), - ), - ); - }, - ); - }, - name: "year_graduated", - controller: yearGraduated, - decoration: normalTextFieldStyle( - "Year Graduated *", - "Year Graduated *"), - ) - //// HIGHEST UNITS EARNED - : FormBuilderTextField( - validator: - FormBuilderValidators.required( - errorText: - "This fied is required"), - controller: unitsController, - name: "units_earned", - decoration: normalTextFieldStyle( - "Highest Level/Units Earned *", - "Highest Level/Units Earned *")), - ) - - ], + }), ); }), const SizedBox( height: 12, ), - //// HONORS - - StatefulBuilder(builder: (context, setState) { - return MultiSelectDropDown( - onOptionSelected: (List selectedOptions) { - selectedValueItem = selectedOptions; - }, - borderColor: Colors.grey, - borderWidth: 1, - borderRadius: 5, - hint: "Honors", - padding: const EdgeInsets.all(8), - options: valueItemHonorList, - selectionType: SelectionType.multi, - chipConfig: const ChipConfig(wrapType: WrapType.wrap), - dropdownHeight: 300, - optionTextStyle: const TextStyle(fontSize: 16), - selectedOptionIcon: const Icon(Icons.check_circle), - selectedOptions: - (state.educationalBackground.honors!.isNotEmpty && - state.educationalBackground.honors != null) - ? selectedValueItem = state - .educationalBackground.honors! - .map((Honor honor) => ValueItem( - label: honor.name!, value: honor.name)) - .toList() - : [], - ); - }), - - ]), - ), - ////sumit button + + ////Programs + + Container( + child: selectedLevel != null && + selectedLevel!.group != 1 + ? SearchField( + inputFormatters: [UpperCaseTextFormatter()], + suggestionAction: + SuggestionAction.unfocus, + controller: currentProgramController, + itemHeight: 70, + suggestionsDecoration: box1(), + suggestions: state.programs + .map((Course program) => + SearchFieldListItem( + program.program!, + item: program, + child: Padding( + padding: const EdgeInsets + .symmetric( + horizontal: 10), + child: ListTile( + title: Text( + program.program!,overflow: TextOverflow.visible,)), + ))) + .toList(), + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + focusNode: programFocusNode, + searchInputDecoration: + normalTextFieldStyle( + "Course/Programs *", "") + .copyWith( + suffixIcon: IconButton( + icon:const Icon( + Icons.arrow_drop_down), onPressed: () { programFocusNode.unfocus(); },),), + onSuggestionTap: (position) { + setState(() { + selectedProgram = position.item; + programFocusNode.unfocus(); + }); + }, + emptyWidget: EmptyWidget( + title: "Add Program", + controller: addProgramController, + onpressed: () { + setState(() { + Course newProgram = Course( + id: null, + program: addProgramController + .text + .toUpperCase()); + state.programs + .insert(0, newProgram); + addProgramController.text = ""; + + Navigator.pop(context); + }); + }), + ) + : Container()), + SizedBox(height: selectedLevel != null && + selectedLevel!.group != 1?12:0,), + ], + ); + }), + + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + //// GRADUATED SWITCH + FormBuilderSwitch( + initialValue: graduated, + activeColor: second, + onChanged: (value) { + setState(() { + graduated = value!; + if (graduated) { + unitsController.text = ""; + } else { + yearGraduated.text = ""; + } + }); + }, + decoration: normalTextFieldStyle( + "Graduated?", 'Graduated?'), + name: 'graudated', + title: Text(graduated ? "YES" : "NO"), + ), + const SizedBox( + height: 12, + ), + ////FROM + SizedBox( + width: screenWidth, + child: Row( + children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This fied is required"), + decoration: + normalTextFieldStyle("from *", "from"), + name: "", + controller: fromController, + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + content: SizedBox( + width: 300, + height: 300, + child: YearPicker( + firstDate: DateTime( + DateTime.now().year - 100, + 1), + lastDate: DateTime( + DateTime.now().year + 100, + 1), + initialDate: DateTime.now(), + selectedDate: DateTime.now(), + onChanged: (DateTime dateTime) { + fromController.text = + dateTime.year.toString(); + Navigator.pop(context); + }, + ), + ), + ); + }, + ); + }, + ), + ), + const SizedBox( + width: 8, + ), + ////UNTIL + Flexible( + flex: 1, + child: FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This fied is required"), + decoration: normalTextFieldStyle( + "until *", "until"), + name: "", + controller: untilController, + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + content: SizedBox( + width: 300, + height: 300, + child: YearPicker( + firstDate: DateTime( + DateTime.now().year - 100, + 1), + lastDate: DateTime( + DateTime.now().year + 100, + 1), + initialDate: DateTime.now(), + selectedDate: DateTime.now(), + onChanged: (DateTime dateTime) { + untilController.text = + dateTime.year.toString(); + Navigator.pop(context); + }, + ), + ), + ); + }, + ); + }, + ), + ), + + + ], + ), + + ), const SizedBox(height: 20,), + SizedBox( + + child: graduated + ////GRADUATED YEAR + ? FormBuilderTextField( + validator: + FormBuilderValidators.required( + errorText: + "This fied is required"), + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + content: SizedBox( + width: 300, + height: 300, + child: YearPicker( + firstDate: DateTime( + DateTime.now().year - + 100, + 1), + lastDate: DateTime( + DateTime.now().year + + 100, + 1), + initialDate: + DateTime.now(), + selectedDate: + DateTime.now(), + onChanged: + (DateTime dateTime) { + yearGraduated.text = + dateTime.year + .toString(); + Navigator.pop(context); + }, + ), + ), + ); + }, + ); + }, + name: "year_graduated", + controller: yearGraduated, + decoration: normalTextFieldStyle( + "Year Graduated *", + "Year Graduated *"), + ) + //// HIGHEST UNITS EARNED + : FormBuilderTextField( + initialValue: state.educationalBackground.unitsEarned?.toString(), + validator: + FormBuilderValidators.required( + errorText: + "This fied is required"), + + name: "units_earned", + decoration: normalTextFieldStyle( + "Highest Level/Units Earned *", + "Highest Level/Units Earned *")), + ) + + ], + ); + }), + const SizedBox( + height: 12, + ), + //// HONORS + StatefulBuilder(builder: (context, setState) { + return MultiSelectDropDown( + onOptionSelected: (List selectedOptions) { + selectedValueItem = selectedOptions; + }, + borderColor: Colors.grey, + borderWidth: 1, + borderRadius: 5, + hint: "Honors", + padding: const EdgeInsets.all(8), + options: valueItemHonorList, + selectionType: SelectionType.multi, + chipConfig: const ChipConfig(wrapType: WrapType.wrap), + dropdownHeight: 300, + optionTextStyle: const TextStyle(fontSize: 16), + selectedOptionIcon: const Icon(Icons.check_circle), + selectedOptions: + (state.educationalBackground.honors!.isNotEmpty && + state.educationalBackground.honors != null) + ? selectedValueItem = state + .educationalBackground.honors! + .map((Honor honor) => ValueItem( + label: honor.name!, value: honor.name)) + .toList() + : [], + ); + }), + const SizedBox(height: 14,), + ////sumit button SizedBox( width: double.infinity, @@ -558,7 +553,7 @@ class _EditEducationScreenState extends State { yearGraduated: graduated ? yearGraduated.text : null, unitsEarned: !graduated - ? int.tryParse(unitsController.text) + ? int.tryParse(formKey.currentState!.value['units_earned']) : null, attachments: null, ); @@ -573,9 +568,7 @@ class _EditEducationScreenState extends State { }, child: const Text(submit)), ), - - ], - ), + ]), ), ), ); diff --git a/lib/screens/profile/components/education/education_view_attachment.dart b/lib/screens/profile/components/education/education_view_attachment.dart index 88657f9..f84ca88 100644 --- a/lib/screens/profile/components/education/education_view_attachment.dart +++ b/lib/screens/profile/components/education/education_view_attachment.dart @@ -3,13 +3,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_progress_hud/flutter_progress_hud.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; -import 'package:fluttertoast/fluttertoast.dart'; -import 'package:share_plus/share_plus.dart'; import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart'; import 'package:unit2/bloc/profile/education/education_bloc.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; +import '../../../../utils/url_launcher_file_downloader.dart'; + class EudcationViewAttachment extends StatefulWidget { const EudcationViewAttachment({super.key}); @@ -26,7 +26,7 @@ class _EudcationViewAttachmentState extends State { return Scaffold( floatingActionButton: FloatingActionButton( onPressed: () async { - await _launchInBrowser(fileUrl!); + await launchInBrowser(fileUrl!); }, child: const Icon(Icons.file_download), ), @@ -103,19 +103,4 @@ class _EudcationViewAttachmentState extends State { ), )); } - - Future _launchInBrowser(String url) async { - final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; - if (!await launcher.launch( - url, - useSafariVC: false, - useWebView: false, - enableJavaScript: false, - enableDomStorage: false, - universalLinksOnly: false, - headers: {}, - )) { - throw Exception('Could not launch $url'); - } - } } diff --git a/lib/screens/profile/components/education_screen.dart b/lib/screens/profile/components/education_screen.dart index 761f285..aec934b 100644 --- a/lib/screens/profile/components/education_screen.dart +++ b/lib/screens/profile/components/education_screen.dart @@ -11,7 +11,6 @@ import 'package:unit2/bloc/profile/profile_bloc.dart'; import 'package:unit2/bloc/user/user_bloc.dart'; import 'package:unit2/model/profile/educational_background.dart'; import 'package:unit2/screens/profile/components/education/add_modal.dart'; -import 'package:unit2/screens/profile/shared/view_attachment.dart'; import 'package:unit2/theme-data.dart/box_shadow.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import 'package:unit2/widgets/Leadings/add_leading.dart'; @@ -41,7 +40,7 @@ class EducationScreen extends StatelessWidget { int profileId; String? token; return Scaffold( - resizeToAvoidBottomInset: false, + resizeToAvoidBottomInset: true, appBar: AppBar( title: context.watch().state is AddEducationState ? const FittedBox(child: Text("Add Educational Background")) diff --git a/lib/screens/profile/components/eligibility/add_modal.dart b/lib/screens/profile/components/eligibility/add_modal.dart index 945ec9a..4c1f0a8 100644 --- a/lib/screens/profile/components/eligibility/add_modal.dart +++ b/lib/screens/profile/components/eligibility/add_modal.dart @@ -71,343 +71,337 @@ class _AddEligibilityScreenState extends State { padding: const EdgeInsets.symmetric(vertical: 32, horizontal: 28), child: FormBuilder( key: formKey, - child: SizedBox( - height: screenHeight * 90, - child: Column( - children: [ - Flexible( - child: ListView(children: [ - ////ELIGIBILITIES DROPDOWN - FormBuilderDropdown( - onChanged: (Eligibility? eligibility) { - selectedEligibility = eligibility; - }, - autovalidateMode: - AutovalidateMode.onUserInteraction, - validator: (value) => - value == null ? 'required' : null, - items: state.eligibilities - .map>( - (Eligibility eligibility) { - return DropdownMenuItem( - value: eligibility, - child: Text(eligibility.title)); - }).toList(), - name: "eligibility", - decoration: normalTextFieldStyle( - "Eligibility", "Eligibility")), - const SizedBox( - height: 8, - ), + child: ListView(children: [ + ////ELIGIBILITIES DROPDOWN + FormBuilderDropdown( + onChanged: (Eligibility? eligibility) { + selectedEligibility = eligibility; + }, + autovalidateMode: + AutovalidateMode.onUserInteraction, + validator: (value) => + value == null ? 'required' : null, + items: state.eligibilities + .map>( + (Eligibility eligibility) { + return DropdownMenuItem( + value: eligibility, + child: Text(eligibility.title)); + }).toList(), + name: "eligibility", + decoration: normalTextFieldStyle( + "Eligibility", "Eligibility")), + const SizedBox( + height: 8, + ), - SizedBox( - width: screenWidth, - child: Row( - children: [ - ////LICENSE NUMBER - Flexible( - flex: 1, - child: FormBuilderTextField( - onChanged: (value) { - license = value; - }, - name: 'license_number', - decoration: normalTextFieldStyle( - "license number", "license number"), - ), - ), - const SizedBox( - width: 8, - ), - ////RATING - Flexible( - flex: 1, - child: FormBuilderTextField( - validator: FormBuilderValidators.numeric( - errorText: "Enter a number"), - keyboardType: - const TextInputType.numberWithOptions(), - onChanged: (value) { - rating = value; - }, - name: 'rating', - decoration: normalTextFieldStyle( - 'rating %', 'rating'), - ), - ), - ], - ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + ////LICENSE NUMBER + Flexible( + flex: 1, + child: FormBuilderTextField( + onChanged: (value) { + license = value; + }, + name: 'license_number', + decoration: normalTextFieldStyle( + "license number", "license number"), ), - const SizedBox( - height: 8, + ), + const SizedBox( + width: 8, + ), + ////RATING + Flexible( + flex: 1, + child: FormBuilderTextField( + validator: FormBuilderValidators.numeric( + errorText: "Enter a number"), + keyboardType: + const TextInputType.numberWithOptions(), + onChanged: (value) { + rating = value; + }, + name: 'rating', + decoration: normalTextFieldStyle( + 'rating %', 'rating'), ), - SizedBox( - width: screenWidth, - child: StatefulBuilder(builder: (context, setState) { - return Row( - children: [ - ////EXAM DATE - Flexible( - flex: 1, - child: DateTimePicker( - use24HourFormat: false, - icon: const Icon(Icons.date_range), - controller: examDateController, - firstDate: DateTime(1990), - lastDate: DateTime(2100), - timeHintText: - "Date of Examination/Conferment", - decoration: - normalTextFieldStyle("Exam date", "") - .copyWith( - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - )), - initialDate: expireDate == null - ? DateTime.now() - : expireDate!.subtract( - const Duration(days: 1)), - selectableDayPredicate: (date) { - if (expireDate != null && - expireDate! - .microsecondsSinceEpoch <= - date.microsecondsSinceEpoch) { - return false; - } - return true; - }, - onChanged: (value) { - setState(() { - examDate = DateTime.parse(value); - }); - }, - )), - const SizedBox( - width: 8, - ), - ////VALIDITY DATE - Flexible( - flex: 1, - child: DateTimePicker( - controller: validityDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), - decoration: normalTextFieldStyle( - "Validity date", "Validity date") - .copyWith( - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - )), - selectableDayPredicate: (date) { - if (examDate != null && - examDate!.microsecondsSinceEpoch >= - date.microsecondsSinceEpoch) { - return false; - } - return true; - }, - onChanged: (value) { - setState(() { - expireDate = DateTime.parse(value); - }); - }, - initialDate: examDate == null - ? DateTime.now() - : examDate! - .add(const Duration(days: 1)), - ), - ), - ], - ); - }), - ), - const SizedBox( - height: 8, - ), - Text( - "Placement of Examination/Conferment", - style: Theme.of(context) - .textTheme - .displaySmall! - .copyWith(fontSize: blockSizeVertical * 2), - ), - const SizedBox( - height: 8, - ), - ////OVERSEAS ADDRESS SWITCH - Column( - children: [ - FormBuilderSwitch( - validator: FormBuilderValidators.required( - errorText: 'This field is required'), - initialValue: overseas, - activeColor: second, + ), + ], + ), + ), + const SizedBox( + height: 8, + ), + SizedBox( + width: screenWidth, + child: StatefulBuilder(builder: (context, setState) { + return Row( + children: [ + ////EXAM DATE + Flexible( + flex: 1, + child: DateTimePicker( + use24HourFormat: false, + icon: const Icon(Icons.date_range), + controller: examDateController, + firstDate: DateTime(1990), + lastDate: DateTime(2100), + timeHintText: + "Date of Examination/Conferment", + decoration: + normalTextFieldStyle("Exam date", "") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + initialDate: expireDate == null + ? DateTime.now() + : expireDate!.subtract( + const Duration(days: 1)), + selectableDayPredicate: (date) { + if (expireDate != null && + expireDate! + .microsecondsSinceEpoch <= + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, onChanged: (value) { setState(() { - overseas = value; + examDate = DateTime.parse(value); }); }, - decoration: normalTextFieldStyle("", ''), - name: 'overseas', - title: const Text("Overseas Address?"), - ), - const SizedBox( - height: 8, - ), - ////COUNTRY DROPDOWN - SizedBox( - child: overseas == true - ? FormBuilderDropdown( - initialValue: null, - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - items: state.countries - .map>( - (Country country) { - return DropdownMenuItem( - value: country, - child: FittedBox( - child: Text(country.name!))); - }).toList(), - name: 'country', - decoration: normalTextFieldStyle( - "Country*", "Country"), - onChanged: (Country? value) { - selectedCountry = value; - }, - ) - : Column( - children: [ - ////REGION DROPDOWN - FormBuilderDropdown( - autovalidateMode: AutovalidateMode - .onUserInteraction, - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - //// region onchange - onChanged: (Region? region) async { - if (selectedRegion != region) { - setState(() { - provinceCall = true; - }); - selectedRegion = region; - getProvinces(); - } - }, - initialValue: selectedRegion, - decoration: normalTextFieldStyle( - "Region*", "Region"), - name: 'region', - items: state.regions - .map>( - (Region region) { - return DropdownMenuItem( - value: region, - child: Text( - region.description!)); - }).toList(), - ), - const SizedBox( - height: 8, - ), - ////PROVINCE DROPDOWN - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: provinceCall, - child: DropdownButtonFormField< - Province?>( - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - value: selectedProvince, - onChanged: - (Province? province) { - if (selectedProvince != - province) { - setState(() { - cityCall = true; - }); - selectedProvince = - province; - getCities(); - } - }, - items: provinces == null - ? [] - : provinces!.map< - DropdownMenuItem< - Province>>( - (Province province) { - return DropdownMenuItem( - value: province, - child: FittedBox( - child: Text(province - .description!), - )); - }).toList(), - decoration: - normalTextFieldStyle( - "Province*", - "Province")), - ), - ), - - //// CityMunicipalities dropdown - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: cityCall, - child: DropdownButtonFormField< - CityMunicipality>( - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - onChanged: - (CityMunicipality? city) { - selectedMunicipality = city; - }, - decoration: - normalTextFieldStyle( - "Municipality*", - "Municipality"), - value: selectedMunicipality, - items: citymuns == null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality c) { - return DropdownMenuItem( - value: c, - child: Text(c - .description!)); - }).toList(), - ), - ), - ), - ], - )), - ], + )), + const SizedBox( + width: 8, ), - ]), + ////VALIDITY DATE + Flexible( + flex: 1, + child: DateTimePicker( + controller: validityDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + decoration: normalTextFieldStyle( + "Validity date", "Validity date") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + selectableDayPredicate: (date) { + if (examDate != null && + examDate!.microsecondsSinceEpoch >= + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, + onChanged: (value) { + setState(() { + expireDate = DateTime.parse(value); + }); + }, + initialDate: examDate == null + ? DateTime.now() + : examDate! + .add(const Duration(days: 1)), + ), + ), + ], + ); + }), + ), + const SizedBox( + height: 8, + ), + Text( + "Placement of Examination/Conferment", + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith(fontSize: blockSizeVertical * 2), + ), + const SizedBox( + height: 8, + ), + ////OVERSEAS ADDRESS SWITCH + Column( + children: [ + FormBuilderSwitch( + validator: FormBuilderValidators.required( + errorText: 'This field is required'), + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value; + }); + }, + decoration: normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Overseas Address?"), ), + const SizedBox( + height: 8, + ), + ////COUNTRY DROPDOWN SizedBox( + child: overseas == true + ? FormBuilderDropdown( + initialValue: null, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + items: state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox( + child: Text(country.name!))); + }).toList(), + name: 'country', + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) { + selectedCountry = value; + }, + ) + : Column( + children: [ + ////REGION DROPDOWN + FormBuilderDropdown( + autovalidateMode: AutovalidateMode + .onUserInteraction, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + //// region onchange + onChanged: (Region? region) async { + if (selectedRegion != region) { + setState(() { + provinceCall = true; + }); + selectedRegion = region; + getProvinces(); + } + }, + initialValue: selectedRegion, + decoration: normalTextFieldStyle( + "Region*", "Region"), + name: 'region', + items: state.regions + .map>( + (Region region) { + return DropdownMenuItem( + value: region, + child: Text( + region.description!)); + }).toList(), + ), + const SizedBox( + height: 8, + ), + ////PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + value: selectedProvince, + onChanged: + (Province? province) { + if (selectedProvince != + province) { + setState(() { + cityCall = true; + }); + selectedProvince = + province; + getCities(); + } + }, + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province province) { + return DropdownMenuItem( + value: province, + child: FittedBox( + child: Text(province + .description!), + )); + }).toList(), + decoration: + normalTextFieldStyle( + "Province*", + "Province")), + ), + ), + + //// CityMunicipalities dropdown + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: DropdownButtonFormField< + CityMunicipality>( + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + onChanged: + (CityMunicipality? city) { + selectedMunicipality = city; + }, + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), + ), + ), + ), + ], + )), + ], + ), + const SizedBox(height: 24,), + SizedBox( width: screenWidth, height: 60, child: ElevatedButton( @@ -464,9 +458,7 @@ class _AddEligibilityScreenState extends State { }, child: const Text(submit)), ), - ], - ), - ), + ]), ), ); } diff --git a/lib/screens/profile/components/eligibility/edit_modal.dart b/lib/screens/profile/components/eligibility/edit_modal.dart index d161084..9e0c0f4 100644 --- a/lib/screens/profile/components/eligibility/edit_modal.dart +++ b/lib/screens/profile/components/eligibility/edit_modal.dart @@ -78,8 +78,8 @@ class _EditEligibilityScreenState extends State { validityDateController.text = state.eligibityCert.validityDate == null ? '' : state.eligibityCert.validityDate.toString(); - DateTime? examDate = DateTime.tryParse(examDateController.text) ; - DateTime? expireDate = DateTime.tryParse(validityDateController.text); + DateTime? examDate = DateTime.tryParse(examDateController.text); + DateTime? expireDate = DateTime.tryParse(validityDateController.text); provinces = state.provinces; citymuns = state.cities; regions = state.regions; @@ -96,461 +96,445 @@ class _EditEligibilityScreenState extends State { padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 28), child: FormBuilder( key: formKey, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const SizedBox( - height: 24, - ), - ////ELIGIBILITIES DROPDOWN - DropdownButtonFormField( - validator: (value) => - value == null ? 'required' : null, - isExpanded: true, - onChanged: (Eligibility? eligibility) { - selectedEligibility = eligibility; - }, - value: selectedEligibility, - items: state.eligibilities - .map>( - (Eligibility eligibility) { - return DropdownMenuItem( - value: eligibility, - child: Text(eligibility.title)); - }).toList(), - decoration: normalTextFieldStyle("Eligibility", "")), - const SizedBox( - height: 12, - ), + child: ListView(children: [ + const SizedBox( + height: 24, + ), + ////ELIGIBILITIES DROPDOWN + DropdownButtonFormField( + validator: (value) => value == null ? 'required' : null, + isExpanded: true, + onChanged: (Eligibility? eligibility) { + selectedEligibility = eligibility; + }, + value: selectedEligibility, + items: state.eligibilities + .map>( + (Eligibility eligibility) { + return DropdownMenuItem( + value: eligibility, child: Text(eligibility.title)); + }).toList(), + decoration: normalTextFieldStyle("Eligibility", "")), + const SizedBox( + height: 12, + ), - SizedBox( - width: screenWidth, - child: Row( - children: [ - ////LICENSE NUMBER - Flexible( - flex: 1, - child: FormBuilderTextField( - onChanged: (value) { - license = value; - }, - name: 'license_number', - initialValue: license, - decoration: normalTextFieldStyle( - "license number", "license number"), - ), - ), - const SizedBox( - width: 12, - ), - // //RATING - Flexible( - flex: 1, - child: FormBuilderTextField( - validator: FormBuilderValidators.numeric( - errorText: "Enter a number"), - keyboardType: - const TextInputType.numberWithOptions(), - onChanged: (value) { - rating = value; - }, - name: 'rating', - initialValue: - rating == null ? 'N/A' : rating.toString(), - decoration: - normalTextFieldStyle('rating', 'rating'), - ), - ), - ], + SizedBox( + width: screenWidth, + child: Row( + children: [ + ////LICENSE NUMBER + Flexible( + flex: 1, + child: FormBuilderTextField( + onChanged: (value) { + license = value; + }, + name: 'license_number', + initialValue: license, + decoration: normalTextFieldStyle( + "license number", "license number"), + ), ), - ), - const SizedBox( - height: 12, - ), - SizedBox( - width: screenWidth, - child: StatefulBuilder(builder: (context, setState) { - return Row( - children: [ - // //EXAM DATE - Flexible( - flex: 1, - child: DateTimePicker( - use24HourFormat: false, - controller: examDateController, - firstDate: DateTime(1990), - lastDate: DateTime(2100), - decoration: - normalTextFieldStyle("Exam date", "") - .copyWith( - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - )), - initialDate: expireDate == null - ? DateTime.now() - : expireDate! - .subtract(const Duration(days: 1)), - selectableDayPredicate: (date) { - if (expireDate != null && - expireDate!.microsecondsSinceEpoch <= - date.microsecondsSinceEpoch) { - return false; - } - return true; - }, - onChanged: (value) { - setState(() { - examDate = DateTime.parse(value); - }); - }, - )), + const SizedBox( + width: 12, + ), + // //RATING + Flexible( + flex: 1, + child: FormBuilderTextField( + validator: FormBuilderValidators.numeric( + errorText: "Enter a number"), + keyboardType: + const TextInputType.numberWithOptions(), + onChanged: (value) { + rating = value; + }, + name: 'rating', + initialValue: + rating == null ? 'N/A' : rating.toString(), + decoration: + normalTextFieldStyle('rating', 'rating'), + ), + ), + ], + ), + ), + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + child: StatefulBuilder(builder: (context, setState) { + return Row( + children: [ + // //EXAM DATE + Flexible( + flex: 1, + child: DateTimePicker( + use24HourFormat: false, + controller: examDateController, + firstDate: DateTime(1990), + lastDate: DateTime(2100), + decoration: + normalTextFieldStyle("Exam date", "") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + initialDate: expireDate == null + ? DateTime.now() + : expireDate! + .subtract(const Duration(days: 1)), + selectableDayPredicate: (date) { + if (expireDate != null && + expireDate!.microsecondsSinceEpoch <= + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, + onChanged: (value) { + setState(() { + examDate = DateTime.parse(value); + }); + }, + )), - const SizedBox( - width: 12, - ), - ////VALIDITY DATE - Flexible( - flex: 1, - child: DateTimePicker( - use24HourFormat: false, - controller: validityDateController, - firstDate: DateTime(1970), - lastDate: DateTime(2100), - decoration: - normalTextFieldStyle("validity date", "") - .copyWith( - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - )), - selectableDayPredicate: (date) { - if (examDate != null && - examDate!.microsecondsSinceEpoch >= - date.microsecondsSinceEpoch) { - return false; - } - return true; - }, - onChanged: (value) { - setState(() { - expireDate = DateTime.parse(value); - }); - }, - initialDate: examDate == null - ? DateTime.now() - : examDate!.add(const Duration(days: 1)), - ), - ), - ], - ); - }), - ), - const SizedBox( - height: 20, - ), - Text( - "Placement of Examination/Confinement", - style: Theme.of(context) - .textTheme - .displaySmall! - .copyWith(fontSize: blockSizeVertical * 2), - ), - const SizedBox( - height: 12, - ), - //OVERSEAS ADDRESS SWITCH - StatefulBuilder(builder: (context, StateSetter setState) { - return Column( - children: [ - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, + const SizedBox( + width: 12, + ), + ////VALIDITY DATE + Flexible( + flex: 1, + child: DateTimePicker( + use24HourFormat: false, + controller: validityDateController, + firstDate: DateTime(1970), + lastDate: DateTime(2100), + decoration: + normalTextFieldStyle("validity date", "") + .copyWith( + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + )), + selectableDayPredicate: (date) { + if (examDate != null && + examDate!.microsecondsSinceEpoch >= + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, onChanged: (value) { setState(() { - overseas = value; + expireDate = DateTime.parse(value); }); }, - decoration: normalTextFieldStyle("", ''), - name: 'overseas', - title: const Text("Overseas Address?"), + initialDate: examDate == null + ? DateTime.now() + : examDate!.add(const Duration(days: 1)), ), - const SizedBox( - height: 12, - ), - //COUNTRY DROPDOWN - SizedBox( - child: overseas == true - ? FormBuilderDropdown( + ), + ], + ); + }), + ), + const SizedBox( + height: 20, + ), + Text( + "Placement of Examination/Confinement", + style: Theme.of(context) + .textTheme + .displaySmall! + .copyWith(fontSize: blockSizeVertical * 2), + ), + const SizedBox( + height: 12, + ), + //OVERSEAS ADDRESS SWITCH + StatefulBuilder(builder: (context, StateSetter setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value; + }); + }, + decoration: normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Overseas Address?"), + ), + const SizedBox( + height: 12, + ), + //COUNTRY DROPDOWN + SizedBox( + child: overseas == true + ? FormBuilderDropdown( + validator: (value) => + value == null ? 'required' : null, + initialValue: selectedCountry!.id == 175 + ? null + : selectedCountry, + items: state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox( + child: Text(country.name!))); + }).toList(), + name: 'country', + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) { + selectedCountry = value; + }, + ) + : Column( + children: [ + ////REGION DROPDOWN + DropdownButtonFormField( validator: (value) => value == null ? 'required' : null, - initialValue: selectedCountry!.id == 175 - ? null - : selectedCountry, - items: state.countries - .map>( - (Country country) { - return DropdownMenuItem( - value: country, - child: FittedBox( - child: Text(country.name!))); - }).toList(), - name: 'country', - decoration: normalTextFieldStyle( - "Country*", "Country"), - onChanged: (Country? value) { - selectedCountry = value; + isExpanded: true, + onChanged: (Region? region) async { + setState(() { + provinceCall = true; + }); + selectedRegion = region; + try { + provinces = await LocationUtils + .instance + .getProvinces( + regionCode: selectedRegion! + .code + .toString()); + } catch (e) { + context + .read() + .add(CallErrorState()); + } + selectedProvince = provinces![0]; + setState(() { + provinceCall = false; + cityCall = true; + }); + try { + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code!); + } catch (e) { + NavigationService + .navigatorKey.currentContext + ?.read() + .add(CallErrorState()); + } + selectedMunicipality = citymuns![0]; + setState(() { + cityCall = false; + }); }, - ) - : Column( - children: [ - ////REGION DROPDOWN - DropdownButtonFormField( + value: selectedRegion, + decoration: normalTextFieldStyle( + "Region*", "Region"), + items: regions == null + ? [] + : regions! + .map>( + (Region region) { + return DropdownMenuItem( + value: region, + child: Text( + region.description!)); + }).toList(), + ), + const SizedBox( + height: 12, + ), + ////PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + value: selectedProvince, + onChanged: + (Province? province) async { + setState(() { + cityCall = true; + }); + selectedProvince = province; + try { + citymuns = await LocationUtils + .instance + .getCities( + code: + selectedProvince! + .code + .toString()); + } catch (e) { + context + .read() + .add(CallErrorState()); + } + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + }); + }, + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province province) { + return DropdownMenuItem( + value: province, + child: FittedBox( + child: Text(province + .description!), + )); + }).toList(), + decoration: normalTextFieldStyle( + "Province*", "Province")), + ), + ), + + //// City municipality + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: cityCall, + child: DropdownButtonFormField< + CityMunicipality>( validator: (value) => value == null ? 'required' : null, isExpanded: true, - onChanged: (Region? region) async { - setState(() { - provinceCall = true; - }); - selectedRegion = region; - try{ - provinces = await LocationUtils - .instance - .getProvinces( - regionCode: - selectedRegion!.code - .toString()); - }catch(e){ - context.read().add(CallErrorState()); - } - selectedProvince = provinces![0]; - setState(() { - provinceCall = false; - cityCall = true; - }); - try{ - citymuns = await LocationUtils - .instance - .getCities( - code: selectedProvince! - .code!); - }catch(e){ - NavigationService.navigatorKey.currentContext?.read().add(CallErrorState()); - } - selectedMunicipality = - citymuns![0]; - setState(() { - cityCall = false; - }); + onChanged: + (CityMunicipality? city) { + selectedMunicipality = city; }, - value: selectedRegion, decoration: normalTextFieldStyle( - "Region*", "Region"), - items: regions == null + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null ? [] - : regions!.map< + : citymuns!.map< DropdownMenuItem< - Region>>( - (Region region) { - return DropdownMenuItem< - Region>( - value: region, - child: Text(region - .description!)); + CityMunicipality>>( + (CityMunicipality c) { + return DropdownMenuItem( + value: c, + child: Text( + c.description!)); }).toList(), ), - const SizedBox( - height: 12, - ), - ////PROVINCE DROPDOWN - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: provinceCall, - child: DropdownButtonFormField< - Province?>( - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - value: selectedProvince, - onChanged: (Province? - province) async { - setState(() { - cityCall = true; - }); - selectedProvince = province; - try{ - - - citymuns = await LocationUtils - .instance - .getCities( - code: - selectedProvince! - .code - .toString()); - }catch(e){ - context.read().add(CallErrorState()); - } - selectedMunicipality = - citymuns![0]; - setState(() { - cityCall = false; - }); - }, - items: provinces == null - ? [] - : provinces!.map< - DropdownMenuItem< - Province>>( - (Province province) { - return DropdownMenuItem( - value: province, - child: FittedBox( - child: Text(province - .description!), - )); - }).toList(), - decoration: - normalTextFieldStyle( - "Province*", - "Province")), - ), - ), - - //// City municipality - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: cityCall, - child: DropdownButtonFormField< - CityMunicipality>( - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - onChanged: - (CityMunicipality? city) { - selectedMunicipality = city; - }, - decoration: - normalTextFieldStyle( - "Municipality*", - "Municipality"), - value: selectedMunicipality, - items: citymuns == null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality c) { - return DropdownMenuItem( - value: c, - child: Text(c - .description!)); - }).toList(), - ), - ), - ), - const SizedBox( - height: 20, - ), - ], - )), - ], - ); - }), - - const Expanded( - child: SizedBox(), - ), - - SizedBox( - width: screenWidth, - height: 60, - child: ElevatedButton( - style: mainBtnStyle( - primary, Colors.transparent, second), - onPressed: () { - ExamAddress examAddress; - ////rating - double? rate = - rating == null ? null : double.parse(rating!); - ////license - String? newLicense = license; - ////city municipality - CityMunicipality? cityMunicipality = - selectedMunicipality; - ////exam date - DateTime? examDate = - examDateController.text.isEmpty - ? null - : DateTime.parse(examDateController.text); - // // validity date - DateTime? validityDate = validityDateController - .text.isEmpty + ), + ), + ], + )), + ], + ); + }), + const SizedBox( + height: 18, + ), + SizedBox( + width: screenWidth, + height: 60, + child: ElevatedButton( + style: + mainBtnStyle(primary, Colors.transparent, second), + onPressed: () { + ExamAddress examAddress; + ////rating + double? rate = + rating == null ? null : double.parse(rating!); + ////license + String? newLicense = license; + ////city municipality + CityMunicipality? cityMunicipality = + selectedMunicipality; + ////exam date + DateTime? examDate = examDateController.text.isEmpty + ? null + : DateTime.parse(examDateController.text); + // // validity date + DateTime? validityDate = + validityDateController.text.isEmpty ? null : DateTime.parse(validityDateController.text); - //// exam address - if (overseas!) { - examAddress = ExamAddress( - barangay: null, - id: state.eligibityCert.examAddress?.id, - addressCategory: state.eligibityCert - .examAddress?.addressCategory, - examAddressClass: state.eligibityCert - .examAddress?.examAddressClass, - country: selectedCountry, - cityMunicipality: null); - } else { - examAddress = ExamAddress( - barangay: state - .eligibityCert.examAddress?.barangay, - id: state.eligibityCert.examAddress?.id, - addressCategory: state.eligibityCert - .examAddress?.addressCategory, - examAddressClass: state.eligibityCert - .examAddress?.examAddressClass, - country: Country( - id: 175, - name: 'Philippines', - code: 'PH'), - cityMunicipality: cityMunicipality); - } + //// exam address + if (overseas!) { + examAddress = ExamAddress( + barangay: null, + id: state.eligibityCert.examAddress?.id, + addressCategory: state + .eligibityCert.examAddress?.addressCategory, + examAddressClass: state.eligibityCert + .examAddress?.examAddressClass, + country: selectedCountry, + cityMunicipality: null); + } else { + examAddress = ExamAddress( + barangay: + state.eligibityCert.examAddress?.barangay, + id: state.eligibityCert.examAddress?.id, + addressCategory: state + .eligibityCert.examAddress?.addressCategory, + examAddressClass: state.eligibityCert + .examAddress?.examAddressClass, + country: Country( + id: 175, name: 'Philippines', code: 'PH'), + cityMunicipality: cityMunicipality); + } - EligibityCert eligibityCert = EligibityCert( - id: state.eligibityCert.id, - rating: rate, - examDate: examDate, - attachments: null, - eligibility: selectedEligibility, - examAddress: examAddress, - validityDate: validityDate, - licenseNumber: newLicense, - overseas: overseas); - if (formKey.currentState!.saveAndValidate()) { - final progress = ProgressHUD.of(context); - progress!.showWithText("Loading..."); - context.read().add( - UpdateEligibility( - eligibityCert: eligibityCert, - oldEligibility: - state.eligibityCert.eligibility!.id, - profileId: widget.profileId.toString(), - token: widget.token)); - } - }, - child: const Text(submit)), - ), - ]), + EligibityCert eligibityCert = EligibityCert( + id: state.eligibityCert.id, + rating: rate, + examDate: examDate, + attachments: null, + eligibility: selectedEligibility, + examAddress: examAddress, + validityDate: validityDate, + licenseNumber: newLicense, + overseas: overseas); + if (formKey.currentState!.saveAndValidate()) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add( + UpdateEligibility( + eligibityCert: eligibityCert, + oldEligibility: + state.eligibityCert.eligibility!.id, + profileId: widget.profileId.toString(), + token: widget.token)); + } + }, + child: const Text(submit)), + ), + ]), ), ), ); diff --git a/lib/screens/profile/components/eligibility/eligibility_view_attachment.dart b/lib/screens/profile/components/eligibility/eligibility_view_attachment.dart index 9d21ba1..2bf26a7 100644 --- a/lib/screens/profile/components/eligibility/eligibility_view_attachment.dart +++ b/lib/screens/profile/components/eligibility/eligibility_view_attachment.dart @@ -1,73 +1,113 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart'; -import 'package:unit2/bloc/profile/education/education_bloc.dart'; import 'package:unit2/bloc/profile/eligibility/eligibility_bloc.dart'; import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/url_launcher_file_downloader.dart'; +import 'package:unit2/widgets/error_state.dart'; import 'package:url_launcher/url_launcher.dart'; -import '../../../../utils/urls.dart'; -class EligibilityViewAttachment extends StatefulWidget { +class EligibilityViewAttachment extends StatefulWidget { const EligibilityViewAttachment({super.key}); @override - State createState() => _EligibilityViewAttachmentState(); + State createState() => + _EligibilityViewAttachmentState(); } class _EligibilityViewAttachmentState extends State { @override Widget build(BuildContext context) { + String? filename; String? fileUrl; return Scaffold( - floatingActionButton: FloatingActionButton( - onPressed: ()async { - await launchUrl(Uri.parse(fileUrl!)); - }, - child: const Icon(Icons.file_download), - ), - appBar: AppBar( - title: const Text("Attachment"), - centerTitle: true, - actions: [ - IconButton(onPressed: () {}, icon: const Icon(Icons.share)), - ], - ), - body: BlocConsumer(builder: (context,state){ - if(state is EligibilityAttachmentViewState){ - fileUrl = state.fileUrl; - bool isPDF = state.fileUrl[state.fileUrl.length - 1] == 'f' ? true : false; - return SizedBox( - child: isPDF?SfPdfViewer.network( - state.fileUrl,onDocumentLoadFailed: (details) { - Center(child: Text(details.description),); - },): Center( - child: CachedNetworkImage( - progressIndicatorBuilder: (context, url, progress) { - return const SizedBox( - height: 100, - width: 100, - child: CircularProgressIndicator(color: primary,)); + floatingActionButton: FloatingActionButton( + onPressed: () async { + await launchInBrowser(fileUrl!); + }, + child: const Icon(Icons.file_download), + ), + appBar: AppBar( + title: const Text("Attachment"), + centerTitle: true, + actions: [ + IconButton( + onPressed: () { + context.read().add( + ShareAttachment(fileName: filename!, source: fileUrl!)); }, - - imageBuilder: (context, imageProvider) => Container( - decoration: BoxDecoration( - image: DecorationImage( - image: imageProvider, fit: BoxFit.fill)), - ), - imageUrl: - state.fileUrl, - width: double.infinity, - height: 220, - fit: BoxFit.cover, - ), - ), - ); - } - return Container(); - },listener: (context, state) { - - },) - ); + icon: const Icon(Icons.share)), + ], + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: BlocConsumer( + builder: (context, state) { + if (state is EligibilityAttachmentViewState) { + fileUrl = state.fileUrl; + filename = state.fileName; + bool isPDF = state.fileUrl[state.fileUrl.length - 1] == 'f' + ? true + : false; + return SizedBox( + child: isPDF + ? SfPdfViewer.network( + state.fileUrl, + onDocumentLoadFailed: (details) { + Center( + child: Text(details.description), + ); + }, + ) + : Center( + child: CachedNetworkImage( + progressIndicatorBuilder: (context, url, progress) { + return const SizedBox( + height: 100, + width: 100, + child: CircularProgressIndicator( + color: primary, + )); + }, + imageBuilder: (context, imageProvider) => Container( + decoration: BoxDecoration( + image: DecorationImage( + image: imageProvider, fit: BoxFit.fill)), + ), + imageUrl: state.fileUrl, + width: double.infinity, + height: 220, + fit: BoxFit.cover, + ), + ), + ); + } + if (state is EligibilityErrorState) { + return SomethingWentWrong( + message: state.message, + onpressed: () { + Navigator.pop(context); + }); + } + return Container(); + }, + listener: (context, state) { + if (state is EligibilityLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is EligibilityAttachmentViewState || + state is EligibilityErrorState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + }, + ), + )); } } diff --git a/lib/screens/profile/components/eligibility_screen.dart b/lib/screens/profile/components/eligibility_screen.dart index 4376fd1..2e2a548 100644 --- a/lib/screens/profile/components/eligibility_screen.dart +++ b/lib/screens/profile/components/eligibility_screen.dart @@ -28,7 +28,6 @@ import '../../../bloc/profile/eligibility/eligibility_bloc.dart'; import '../../../utils/alerts.dart'; import '../shared/multiple_attachment.dart'; import '../shared/single_attachment.dart'; -import '../shared/view_attachment.dart'; import 'eligibility/eligibility_view_attachment.dart'; class EligibiltyScreen extends StatelessWidget { @@ -48,7 +47,7 @@ class EligibiltyScreen extends StatelessWidget { return true; }, child: Scaffold( - resizeToAvoidBottomInset: false, + resizeToAvoidBottomInset: true, appBar: AppBar( title: context.watch().state is AddEligibilityState ? const Text("Add Eligiblity") @@ -637,7 +636,7 @@ class EligibiltyScreen extends StatelessWidget { context, MaterialPageRoute( builder: ((context) => BlocProvider.value( - value: EligibilityBloc()..add(EligibiltyViewAttachmentEvent(source: state.eligibilities[index].attachments!.first.source!)), + value: EligibilityBloc()..add(EligibiltyViewAttachmentEvent(source: state.eligibilities[index].attachments!.first.source!,filename: state.eligibilities[index].attachments!.first.filename!)), child: const EligibilityViewAttachment(), )))); @@ -678,7 +677,7 @@ class EligibiltyScreen extends StatelessWidget { context, MaterialPageRoute( builder: ((context) => BlocProvider.value( - value: EligibilityBloc()..add(EligibiltyViewAttachmentEvent(source: source)), + value: EligibilityBloc()..add(EligibiltyViewAttachmentEvent(source: source,filename: filename)), child: const EligibilityViewAttachment(), )))); }, diff --git a/lib/screens/profile/components/family_background_screen.dart b/lib/screens/profile/components/family_background_screen.dart index 24106e8..0e02b42 100644 --- a/lib/screens/profile/components/family_background_screen.dart +++ b/lib/screens/profile/components/family_background_screen.dart @@ -510,8 +510,8 @@ class _FamilyBackgroundScreenState extends State { ) : SizedBox( width: screenWidth, - child: const Text( - "Provide your father's primary information.", + child: Text( + "Provide your father's primary information.",style:Theme.of(context).textTheme.bodySmall, textAlign: TextAlign.center, ), @@ -808,8 +808,8 @@ class _FamilyBackgroundScreenState extends State { ) : SizedBox( width: screenWidth, - child: const Text( - "Provide your mother's primary information", + child: Text( + "Provide your mother's primary information",style: Theme.of(context).textTheme.bodySmall, textAlign: TextAlign.center, ), @@ -1238,13 +1238,13 @@ class _FamilyBackgroundScreenState extends State { ) : SizedBox( width: screenWidth, - child: const Padding( + child: Padding( padding: - EdgeInsets.symmetric( + const EdgeInsets.symmetric( vertical: 8, horizontal: 0), child: Text( - "Provide your spouse's primary and employment information. Leave empty if not applicable.", + "Provide your spouse's primary and employment information. Leave empty if not applicable.",style: Theme.of(context).textTheme.bodySmall, textAlign: TextAlign.center, ), @@ -1540,13 +1540,13 @@ class _FamilyBackgroundScreenState extends State { }).toList()) : SizedBox( width: screenWidth, - child: const Padding( + child: Padding( padding: - EdgeInsets.symmetric( + const EdgeInsets.symmetric( vertical: 8, horizontal: 0), child: Text( - "Provide your child/children's primary information. Leave empty if not applicable..", + "Provide your child/children's primary information. Leave empty if not applicable.",style: Theme.of(context).textTheme.bodySmall, textAlign: TextAlign.center, ), @@ -1854,13 +1854,13 @@ class _FamilyBackgroundScreenState extends State { }).toList()) : SizedBox( width: screenWidth, - child: const Padding( + child: Padding( padding: - EdgeInsets.symmetric( + const EdgeInsets.symmetric( vertical: 8, horizontal: 0), child: Text( - "Provide the other related person's primary information. Leave empty if not applicable.", + "Provide the other related person's primary information. Leave empty if not applicable.",style: Theme.of(context).textTheme.bodySmall, textAlign: TextAlign.center, ), diff --git a/lib/screens/profile/components/learning_and_development_screen.dart b/lib/screens/profile/components/learning_and_development_screen.dart index dbb74c8..1fe52ba 100644 --- a/lib/screens/profile/components/learning_and_development_screen.dart +++ b/lib/screens/profile/components/learning_and_development_screen.dart @@ -28,7 +28,6 @@ import '../../../utils/alerts.dart'; import '../../../widgets/Leadings/close_leading.dart'; import '../shared/multiple_attachment.dart'; import '../shared/single_attachment.dart'; -import '../shared/view_attachment.dart'; import 'learning_development/add_modal.dart'; class LearningAndDevelopmentScreen extends StatelessWidget { @@ -46,6 +45,7 @@ class LearningAndDevelopmentScreen extends StatelessWidget { AttachmentCategory? selectedAttachmentCategory; List attachmentCategories = []; return Scaffold( + resizeToAvoidBottomInset: true, appBar: AppBar( title: context.watch().state is LearningDevelopmentAddingState @@ -680,7 +680,7 @@ class LearningAndDevelopmentScreen extends StatelessWidget { context, MaterialPageRoute( builder: ((context) => BlocProvider.value( - value: LearningDevelopmentBloc()..add(LearningDevelopmentViewAttachmentEvent(source: state.learningsAndDevelopment[index].attachments!.first.source!)), + value: LearningDevelopmentBloc()..add(LearningDevelopmentViewAttachmentEvent(source: state.learningsAndDevelopment[index].attachments!.first.source!,filename: state.learningsAndDevelopment[index].attachments!.first.filename!)), child: const LearningDevelopmentViewAttachment(), )))); }, @@ -721,7 +721,7 @@ class LearningAndDevelopmentScreen extends StatelessWidget { MaterialPageRoute( builder: ((context) => BlocProvider.value( - value: LearningDevelopmentBloc()..add(LearningDevelopmentViewAttachmentEvent(source: source)), + value: LearningDevelopmentBloc()..add(LearningDevelopmentViewAttachmentEvent(source: source,filename: filename)), child: const LearningDevelopmentViewAttachment(), )))); }, diff --git a/lib/screens/profile/components/learning_development/add_modal.dart b/lib/screens/profile/components/learning_development/add_modal.dart index 697d651..e3d1e7d 100644 --- a/lib/screens/profile/components/learning_development/add_modal.dart +++ b/lib/screens/profile/components/learning_development/add_modal.dart @@ -109,15 +109,15 @@ class _AddLearningAndDevelopmentScreenState DateTime? to; @override void dispose() { - fromDateController.dispose(); - toDateController.dispose(); - addTrainingController.dispose(); - addTopicController.dispose(); - topicFocusNode.dispose(); + // fromDateController.dispose(); + // toDateController.dispose(); + // addTrainingController.dispose(); + // addTopicController.dispose(); + // topicFocusNode.dispose(); - addSponsorAgencyController.dispose(); - sponsorByFocusNode.dispose(); - sponsorAgencyCategoryFocusNode.dispose(); + // addSponsorAgencyController.dispose(); + // sponsorByFocusNode.dispose(); + // sponsorAgencyCategoryFocusNode.dispose(); super.dispose(); } @@ -128,1414 +128,1390 @@ class _AddLearningAndDevelopmentScreenState if (state is LearningDevelopmentAddingState) { return FormBuilder( key: formKey, - child: SizedBox( - height: screenHeight * 90, - child: Padding( - padding: const EdgeInsets.all(24), - child: StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - Flexible( - child: ListView( - children: [ - ////Training SearchField - SizedBox( - child: show - ? SearchableDropdownFormField.paginated( - errorWidget: (value) { - return SomethingWentWrong( - message: value, - onpressed: () { - context - .read< - LearningDevelopmentBloc>() - .add( - GetLearningDevelopments( - profileId: widget - .profileId, - token: - widget.token)); - }); - }, - noRecordTex: SizedBox( - width: double.infinity, - height: 300, - child: EmptyWidget( - controller: addTrainingController, - onpressed: () { - setState(() { - show = false; - showOtherInputs = true; - selectedTrainingController - .text = - addTrainingController.text - .toUpperCase(); - selectedTraining = - LearningDevelopmentType( - id: null, - title: - selectedTrainingController - .text); - addTrainingController.text = - ""; - Navigator.of(context).pop(); - Navigator.of(context).pop(); - }); - }, - title: "Add Training"), - ), - isDialogExpanded: false, - hintText: const Text('Search Training'), - margin: const EdgeInsets.all(15), - backgroundDecoration: (child) { - return SizedBox( - width: double.infinity, - child: Card( - child: Padding( - padding: - const EdgeInsets.all(16), - child: child), - ), - ); - }, - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - paginatedRequest: (int page, - String? searchKey) async { - List - paginatedList=[]; - try { - paginatedList = - await LearningDevelopmentServices - .instance - .getConductedTrainings( - page: page, - key: searchKey ??= ""); - } catch (e) { - context - .read() - .add(CallErrorState( - message: e.toString())); - } - return paginatedList.map((e) { - return SearchableDropdownMenuItem( - value: e, - onTap: () {}, - label: e.title!.title!, - child: TrainingDisplayDetails( - ////not what you are looking for - notWhatYourLookingFor: () { - setState(() { - if (e.learningDevelopmentType - ?.id != - null) { - selectedTraining = - LearningDevelopmentType( - id: e.title!.id, - title: null); - } else { - selectedTraining = - LearningDevelopmentType( - id: null, - title: e.title! - .title); - } - show = false; - showOtherInputs = true; - + child: Padding( + padding: const EdgeInsets.all(24), + child: StatefulBuilder(builder: (context, setState) { + return ListView( + children: [ + ////Training SearchField + SizedBox( + child: show + ? SearchableDropdownFormField.paginated( + errorWidget: (value) { + return SomethingWentWrong( + message: value, + onpressed: () { + context + .read< + LearningDevelopmentBloc>() + .add( + GetLearningDevelopments( + profileId: widget + .profileId, + token: + widget.token)); + }); + }, + noRecordTex: SizedBox( + width: double.infinity, + height: 300, + child: EmptyWidget( + controller: addTrainingController, + onpressed: () { + setState(() { + show = false; + showOtherInputs = true; + selectedTrainingController + .text = + addTrainingController.text + .toUpperCase(); + selectedTraining = + LearningDevelopmentType( + id: null, + title: selectedTrainingController - .text = - e.title!.title!; - Navigator.of(context) - .pop(); + .text); + addTrainingController.text = + ""; + Navigator.of(context).pop(); + Navigator.of(context).pop(); + }); + }, + title: "Add Training"), + ), + isDialogExpanded: false, + hintText: const Text('Search Training'), + margin: const EdgeInsets.all(15), + backgroundDecoration: (child) { + return SizedBox( + width: double.infinity, + child: Card( + child: Padding( + padding: + const EdgeInsets.all(16), + child: child), + ), + ); + }, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + paginatedRequest: (int page, + String? searchKey) async { + List + paginatedList=[]; + try { + paginatedList = + await LearningDevelopmentServices + .instance + .getConductedTrainings( + page: page, + key: searchKey ??= ""); + } catch (e) { + context + .read() + .add(CallErrorState( + message: e.toString())); + } + return paginatedList.map((e) { + return SearchableDropdownMenuItem( + value: e, + onTap: () {}, + label: e.title!.title!, + child: TrainingDisplayDetails( + ////not what you are looking for + notWhatYourLookingFor: () { + setState(() { + if (e.learningDevelopmentType + ?.id != + null) { + selectedTraining = + LearningDevelopmentType( + id: e.title!.id, + title: null); + } else { + selectedTraining = + LearningDevelopmentType( + id: null, + title: e.title! + .title); + } + show = false; + showOtherInputs = true; + + selectedTrainingController + .text = + e.title!.title!; + Navigator.of(context) + .pop(); + }); + }, + e: e, + )); + }).toList(); + }, + dropDownMaxHeight: 500, + requestItemCount: 5, + //// on chage + onChanged: (ConductedTraining? value) { + setState(() { + selectedConductedTraining = value; + selectedTrainingController.text = + selectedConductedTraining! + .title!.title!; + show = false; + showTrainingDetails = true; + }); + }, + ) + : const SizedBox(), + ), + const SizedBox( + height: 12, + ), + SizedBox( + ////Training selected Textfield + child: !show + ? FormBuilderTextField( + maxLines: 5, + readOnly: true, + controller: + selectedTrainingController, + name: "", + decoration: + normalTextFieldStyle("", "") + .copyWith( + labelText: "Training", + suffixIcon: IconButton( + onPressed: () { + setState(() { + selectedConductedTraining = + null; + selectedTrainingController + .text = ""; + show = true; + showTrainingDetails = + false; + showOtherInputs = + false; + selectedTraining = + null; }); }, - e: e, - )); - }).toList(); - }, - dropDownMaxHeight: 500, - requestItemCount: 5, - //// on chage - onChanged: (ConductedTraining? value) { + icon: const Icon( + Icons.close))), + ) + : const SizedBox()), + + ////ShowTraining Details + SizedBox( + child: showTrainingDetails + ? TrainingDetails( + trainingTitle: + selectedConductedTraining! + .learningDevelopmentType! + .title!, + totalHours: selectedConductedTraining! + .totalHours!, + trainingTopic: + selectedConductedTraining! + .topic!.title!, + toDate: selectedConductedTraining! + .toDate + .toString(), + fromDate: selectedConductedTraining! + .fromDate! + .toString(), + conductedBy: selectedConductedTraining! + .conductedBy!.name!) + : const SizedBox(), + ), + ///// end show training details + //// show other inputs + SizedBox( + child: showOtherInputs + ? SizedBox( + child: Column(children: [ + const SizedBox( + height: 12, + ), + ////learning development type + FormBuilderDropdown< + LearningDevelopmentType>( + decoration: normalTextFieldStyle( + "Learning Development Type *", + ""), + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + name: "types", + items: state.types + .map((e) => DropdownMenuItem< + LearningDevelopmentType>( + value: e, + child: Text(e.title!))) + .toList(), + onChanged: (value) { + selectedLearningDevelopmentType = + value; + }, + ), + + //// learning development topics + const SizedBox( + height: 12, + ), + StatefulBuilder( + builder: (context, setState) { + return SearchField( + inputFormatters: [ + UpperCaseTextFormatter() + ], + focusNode: topicFocusNode, + itemHeight: 100, + suggestionsDecoration: box1(), + suggestions: state.topics + .map((LearningDevelopmentType + topic) => + SearchFieldListItem( + topic.title!, + item: topic, + child: Padding( + padding: const EdgeInsets + .symmetric( + horizontal: + 10), + child: ListTile( + title: Text( + topic + .title!, + softWrap: + true, + ), + )))) + .toList(), + + searchInputDecoration: + normalTextFieldStyle( + "Topic *", "") + .copyWith( + suffixIcon: + IconButton( + icon: const Icon( + Icons.arrow_drop_down), + onPressed: () { + topicFocusNode.unfocus(); + }, + )), + onSuggestionTap: (topic) { + if (topic.item?.id != null) { + selectedTopic = + LearningDevelopmentType( + id: topic.item!.id, + title: null); + } else { + selectedTopic = + LearningDevelopmentType( + id: null, + title: topic + .item!.title); + } setState(() { - selectedConductedTraining = value; - selectedTrainingController.text = - selectedConductedTraining! - .title!.title!; - show = false; - showTrainingDetails = true; + topicFocusNode.unfocus(); }); }, - ) - : const SizedBox(), - ), - const SizedBox( - height: 12, - ), - SizedBox( - ////Training selected Textfield - child: !show - ? FormBuilderTextField( - maxLines: 5, - readOnly: true, - controller: - selectedTrainingController, - name: "", - decoration: - normalTextFieldStyle("", "") - .copyWith( - labelText: "Training", - suffixIcon: IconButton( - onPressed: () { - setState(() { - selectedConductedTraining = - null; - selectedTrainingController - .text = ""; - show = true; - showTrainingDetails = - false; - showOtherInputs = - false; - selectedTraining = - null; - }); - }, - icon: const Icon( - Icons.close))), - ) - : const SizedBox()), - - ////ShowTraining Details - SizedBox( - child: showTrainingDetails - ? TrainingDetails( - trainingTitle: - selectedConductedTraining! - .learningDevelopmentType! - .title!, - totalHours: selectedConductedTraining! - .totalHours!, - trainingTopic: - selectedConductedTraining! - .topic!.title!, - toDate: selectedConductedTraining! - .toDate - .toString(), - fromDate: selectedConductedTraining! - .fromDate! - .toString(), - conductedBy: selectedConductedTraining! - .conductedBy!.name!) - : const SizedBox(), - ), - ///// end show training details - //// show other inputs - SizedBox( - child: showOtherInputs - ? SizedBox( - child: Column(children: [ - const SizedBox( - height: 12, - ), - ////learning development type - FormBuilderDropdown< - LearningDevelopmentType>( - decoration: normalTextFieldStyle( - "Learning Development Type *", - ""), - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - name: "types", - items: state.types - .map((e) => DropdownMenuItem< - LearningDevelopmentType>( - value: e, - child: Text(e.title!))) - .toList(), - onChanged: (value) { - selectedLearningDevelopmentType = - value; - }, - ), - - //// learning development topics - const SizedBox( - height: 12, - ), - StatefulBuilder( - builder: (context, setState) { - return SearchField( - inputFormatters: [ - UpperCaseTextFormatter() - ], - focusNode: topicFocusNode, - itemHeight: 100, - suggestionsDecoration: box1(), - suggestions: state.topics - .map((LearningDevelopmentType - topic) => - SearchFieldListItem( - topic.title!, - item: topic, - child: Padding( - padding: const EdgeInsets - .symmetric( - horizontal: - 10), - child: ListTile( - title: Text( - topic - .title!, - softWrap: - true, - ), - )))) - .toList(), - - searchInputDecoration: - normalTextFieldStyle( - "Topic *", "") - .copyWith( - suffixIcon: - IconButton( - icon: const Icon( - Icons.arrow_drop_down), - onPressed: () { - topicFocusNode.unfocus(); - }, - )), - onSuggestionTap: (topic) { - if (topic.item?.id != null) { - selectedTopic = - LearningDevelopmentType( - id: topic.item!.id, - title: null); - } else { - selectedTopic = - LearningDevelopmentType( - id: null, - title: topic - .item!.title); - } - setState(() { - topicFocusNode.unfocus(); - }); - }, - ////EMPTY WIDGET - emptyWidget: EmptyWidget( - title: "Add Topic", - controller: - addTopicController, - onpressed: () { - setState(() { - LearningDevelopmentType - newTopic = - LearningDevelopmentType( - id: null, - title: addTopicController - .text - .toUpperCase()); - state.topics.insert( - 0, newTopic); - topicFocusNode - .unfocus(); - addTopicController - .text = ""; - Navigator.pop(context); - }); - }), - validator: (position) { - if (position!.isEmpty) { - return "This field is required"; - } - return null; - }, - ); + ////EMPTY WIDGET + emptyWidget: EmptyWidget( + title: "Add Topic", + controller: + addTopicController, + onpressed: () { + setState(() { + LearningDevelopmentType + newTopic = + LearningDevelopmentType( + id: null, + title: addTopicController + .text + .toUpperCase()); + state.topics.insert( + 0, newTopic); + topicFocusNode + .unfocus(); + addTopicController + .text = ""; + Navigator.pop(context); + }); }), + validator: (position) { + if (position!.isEmpty) { + return "This field is required"; + } + return null; + }, + ); + }), - const SizedBox( - height: 12, - ), - SizedBox( - width: screenWidth, - child: StatefulBuilder( - builder: (context, setState) { - return StatefulBuilder(builder: - (context, setState) { - return Row( - children: [ - //// FROM DATE - Flexible( - flex: 1, - child: DateTimePicker( - validator: - FormBuilderValidators - .required( - errorText: - "This field is required"), - use24HourFormat: - false, - icon: const Icon( - Icons - .date_range), - controller: - fromDateController, - firstDate: - DateTime(1990), - lastDate: - DateTime(2100), - selectableDayPredicate: - (date) { - if (to != null && - to!.microsecondsSinceEpoch <= - date.microsecondsSinceEpoch) { - return false; - } - return true; - }, - onChanged: (value) { - setState(() { - from = DateTime - .parse( - value); - }); - }, - initialDate: to == - null - ? DateTime.now() - : to!.subtract( - const Duration( - days: - 1)), - timeHintText: - "Date of Examination/Conferment", - decoration: normalTextFieldStyle( - "From *", - "From *") - .copyWith( - prefixIcon: - const Icon( - Icons.date_range, - color: Colors - .black87, - )), - initialValue: null, - )), - const SizedBox( - width: 12, - ), - //// TO DATE - Flexible( - flex: 1, - child: DateTimePicker( - validator: - FormBuilderValidators - .required( - errorText: - "This field is required"), - controller: - toDateController, - firstDate: - DateTime(1990), - selectableDayPredicate: - (date) { - if (from != null && - from!.microsecondsSinceEpoch >= - date.microsecondsSinceEpoch) { - return false; - } - return true; - }, - onChanged: (value) { - setState(() { - to = DateTime - .parse(value); - }); - }, - initialDate: from == - null - ? DateTime.now() - : from!.add( - const Duration( - days: 1)), - lastDate: - DateTime(2100), - decoration: - normalTextFieldStyle( - "To *", - "To *") - .copyWith( + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + child: StatefulBuilder( + builder: (context, setState) { + return StatefulBuilder(builder: + (context, setState) { + return Row( + children: [ + //// FROM DATE + Flexible( + flex: 1, + child: DateTimePicker( + validator: + FormBuilderValidators + .required( + errorText: + "This field is required"), + use24HourFormat: + false, + icon: const Icon( + Icons + .date_range), + controller: + fromDateController, + firstDate: + DateTime(1990), + lastDate: + DateTime(2100), + + onChanged: (value) { + setState(() { + from = DateTime + .parse( + value); + }); + }, + initialDate: to == + null + ? DateTime.now() + : to!.subtract( + const Duration( + days: + 1)), + timeHintText: + "Date of Examination/Conferment", + decoration: normalTextFieldStyle( + "From *", + "From *") + .copyWith( prefixIcon: const Icon( - Icons.date_range, - color: Colors - .black87, - ), + Icons.date_range, + color: Colors + .black87, + )), + initialValue: null, + )), + const SizedBox( + width: 12, + ), + //// TO DATE + Flexible( + flex: 1, + child: DateTimePicker( + validator: + FormBuilderValidators + .required( + errorText: + "This field is required"), + controller: + toDateController, + firstDate: + DateTime(1990), + + onChanged: (value) { + setState(() { + to = DateTime + .parse(value); + }); + }, + initialDate: from == + null + ? DateTime.now() + : from!.add( + const Duration( + days: 1)), + lastDate: + DateTime(2100), + decoration: + normalTextFieldStyle( + "To *", + "To *") + .copyWith( + prefixIcon: + const Icon( + Icons.date_range, + color: Colors + .black87, + ), + ), + initialValue: null, + ), + ), + ], + ); + }); + }), + ), + const SizedBox( + height: 12, + ), + //// total hours conducted + FormBuilderTextField( + validator: numericRequired, + name: "total_hours", + decoration: normalTextFieldStyle( + "Total Hours Conducted *", + "0"), + keyboardType: + TextInputType.number, + ), + const SizedBox( + height: 12, + ), + ////Address + StatefulBuilder( + builder: (context, setState) { + return Column( + children: [ + ////overseas textformfield + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: + normalTextFieldStyle( + "Overseas Address?", + ''), + name: 'overseas', + title: Text(overseas + ? "YES" + : "NO"), + ), + SizedBox( + height: overseas == true + ? 8 + : 0, + ), + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + FormBuilderDropdown< + Region?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + onChanged: (Region? + region) async { + if (selectedRegion != + region) { + setState( + () { + provinceCall = + true; + }); + selectedRegion = + region; + getProvinces(); + } + }, + initialValue: + null, + decoration: + normalTextFieldStyle( + "Region*", + "Region"), + name: 'region', + items: state.regions.map< + DropdownMenuItem< + Region>>((Region + region) { + return DropdownMenuItem< + Region>( + value: + region, + child: Text( + region + .description!)); + }).toList(), + ), + const SizedBox( + height: 12, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: + ModalProgressHUD( + color: Colors + .transparent, + inAsyncCall: + provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => value == null + ? 'required' + : null, + isExpanded: + true, + value: + selectedProvince, + onChanged: + (Province? + province) { + if (selectedProvince != + province) { + setState( + () { + cityCall = + true; + }); + selectedProvince = + province; + getCities(); + } + }, + items: provinces == + null + ? [] + : provinces!.map>((Province + province) { + return DropdownMenuItem( + value: province, + child: FittedBox( + child: Text(province.description!), + )); + }).toList(), + decoration: normalTextFieldStyle( + "Province*", + "Province")), + ), + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: + ModalProgressHUD( + color: Colors + .white, + inAsyncCall: + cityCall, + child: DropdownButtonFormField< + CityMunicipality>( + validator: FormBuilderValidators.required( + errorText: + "This field is required"), + isExpanded: + true, + onChanged: + (CityMunicipality? + city) { + if (selectedMunicipality != + city) { + setState( + () { + barangayCall = + true; + }); + selectedMunicipality = + city; + getBarangays(); + } + }, + decoration: normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: + selectedMunicipality, + items: citymuns == + null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>((CityMunicipality + c) { + return DropdownMenuItem( + value: c, + child: Text(c.description!)); + }).toList(), + ), + ), + ), + //// BARANGAY + SizedBox( + height: 60, + child: + ModalProgressHUD( + color: Colors + .white, + inAsyncCall: + barangayCall, + child: DropdownButtonFormField< + Barangay>( + isExpanded: + true, + onChanged: + (Barangay? + baragay) { + selectedBarangay = + baragay; + }, + decoration: normalTextFieldStyle( + "Barangay*", + "Barangay"), + value: + selectedBarangay, + items: barangays == + null + ? [] + : barangays!.map< + DropdownMenuItem< + Barangay>>((Barangay + barangay) { + return DropdownMenuItem( + value: barangay, + child: Text(barangay.description!)); + }).toList(), ), - initialValue: null, ), ), ], - ); - }); - }), - ), - const SizedBox( - height: 12, - ), - //// total hours conducted - FormBuilderTextField( - validator: numericRequired, - name: "total_hours", - decoration: normalTextFieldStyle( - "Total Hours Conducted *", - "0"), - keyboardType: - TextInputType.number, - ), - const SizedBox( - height: 12, - ), - ////Address - StatefulBuilder( - builder: (context, setState) { - return Column( - children: [ - ////overseas textformfield - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - setState(() { - overseas = value!; - }); - }, - decoration: - normalTextFieldStyle( - "Overseas Address?", - ''), - name: 'overseas', - title: Text(overseas - ? "YES" - : "NO"), + ) + //// COUNTRY DROPDOWN + : SizedBox( + height: 60, + child: + FormBuilderDropdown< + Country>( + initialValue: + null, + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + items: state + .countries + .map< + DropdownMenuItem< + Country>>((Country + country) { + return DropdownMenuItem< + Country>( + value: + country, + child: FittedBox( + child: Text( + country.name!))); + }).toList(), + name: 'country', + decoration: + normalTextFieldStyle( + "Country*", + "Country"), + onChanged: + (Country? + value) { + selectedCountry = + value; + }, + ), ), - SizedBox( - height: overseas == true - ? 8 - : 0, - ), - SizedBox( - child: overseas == false - ? Column( - children: [ - const SizedBox( - height: 12, - ), - ////REGION DROPDOWN - FormBuilderDropdown< - Region?>( - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - onChanged: (Region? - region) async { - if (selectedRegion != - region) { - setState( - () { - provinceCall = - true; - }); - selectedRegion = - region; - getProvinces(); - } - }, - initialValue: + ), + ], + ); + }), + + ////Conducted By + StatefulBuilder( + builder: (context, setState) { + ////has sponsor switch + return Column( + children: [ + ////Add Conducted Agency============ + SizedBox( + child: SizedBox( + child: Column(children: [ + SearchField( + inputFormatters: [ + UpperCaseTextFormatter() + ], + itemHeight: 100, + focusNode: + conductedByFocusNode, + suggestions: state + .conductedBy + .map((Agency + agency) => + SearchFieldListItem( + agency + .name!, + item: + agency, + child: + ListTile( + title: + Text( + agency + .name!, + overflow: + TextOverflow.visible, + ), + subtitle: Text(agency.privateEntity == + true + ? "Private" + : agency.privateEntity == false + ? "Government" + : ""), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle( + " Conducted By *", + "") + .copyWith( + suffixIcon: + GestureDetector( + child: const Icon( + Icons + .arrow_drop_down, + ), + onTap: () => + conductedByFocusNode + .unfocus(), + )), + ////SELETECTED + onSuggestionTap: + (agency) { + setState(() { + if (agency.item + ?.id != + null) { + selectedConductedByAgency = + Agency( + name: null, - decoration: - normalTextFieldStyle( - "Region*", - "Region"), - name: 'region', - items: state.regions.map< - DropdownMenuItem< - Region>>((Region - region) { - return DropdownMenuItem< - Region>( - value: - region, - child: Text( - region - .description!)); - }).toList(), - ), - const SizedBox( - height: 12, - ), - //// PROVINCE DROPDOWN - SizedBox( - height: 60, - child: - ModalProgressHUD( - color: Colors - .transparent, - inAsyncCall: - provinceCall, - child: DropdownButtonFormField< - Province?>( - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: (value) => value == null - ? 'required' - : null, - isExpanded: - true, - value: - selectedProvince, - onChanged: - (Province? - province) { - if (selectedProvince != - province) { - setState( - () { - cityCall = - true; - }); - selectedProvince = - province; - getCities(); - } - }, - items: provinces == - null - ? [] - : provinces!.map>((Province - province) { - return DropdownMenuItem( - value: province, - child: FittedBox( - child: Text(province.description!), - )); - }).toList(), - decoration: normalTextFieldStyle( - "Province*", - "Province")), - ), - ), - ////CITY MUNICIPALITY - SizedBox( - height: 60, - child: - ModalProgressHUD( - color: Colors - .white, - inAsyncCall: - cityCall, - child: DropdownButtonFormField< - CityMunicipality>( - validator: FormBuilderValidators.required( - errorText: - "This field is required"), - isExpanded: - true, - onChanged: - (CityMunicipality? - city) { - if (selectedMunicipality != - city) { - setState( - () { - barangayCall = - true; - }); - selectedMunicipality = - city; - getBarangays(); - } - }, - decoration: normalTextFieldStyle( - "Municipality*", - "Municipality"), - value: - selectedMunicipality, - items: citymuns == - null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>((CityMunicipality - c) { - return DropdownMenuItem( - value: c, - child: Text(c.description!)); - }).toList(), - ), - ), - ), - //// BARANGAY - SizedBox( - height: 60, - child: - ModalProgressHUD( - color: Colors - .white, - inAsyncCall: - barangayCall, - child: DropdownButtonFormField< - Barangay>( - isExpanded: - true, - onChanged: - (Barangay? - baragay) { - selectedBarangay = - baragay; - }, - decoration: normalTextFieldStyle( - "Barangay*", - "Barangay"), - value: - selectedBarangay, - items: barangays == - null - ? [] - : barangays!.map< - DropdownMenuItem< - Barangay>>((Barangay - barangay) { - return DropdownMenuItem( - value: barangay, - child: Text(barangay.description!)); - }).toList(), - ), - ), - ), - ], - ) - //// COUNTRY DROPDOWN - : SizedBox( - height: 60, - child: - FormBuilderDropdown< - Country>( - initialValue: - null, - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - items: state - .countries - .map< - DropdownMenuItem< - Country>>((Country - country) { - return DropdownMenuItem< - Country>( - value: - country, - child: FittedBox( - child: Text( - country.name!))); - }).toList(), - name: 'country', + id: agency + .item! + .id); + } else { + selectedConductedByAgency = Agency( + id: null, + name: agency + .item! + .name); + } + + if (agency.item! + .privateEntity == + null) { + showConductedByAgencyCategory = + true; + showConductedByAgencyPrivateRadio = + true; + } else { + showConductedByAgencyCategory = + false; + showConductedByAgencyPrivateRadio = + false; + } + conductedByFocusNode + .unfocus(); + }); + }, + validator: (agency) { + if (agency! + .isEmpty) { + return "This field is required"; + } + return null; + }, + ////conducter empty widget + emptyWidget: + EmptyWidget( + controller: + addConductedByController, + onpressed: + () { + setState( + () { + Agency newAgency = Agency( + id: + null, + name: addConductedByController + .text + .toUpperCase(), + category: + null, + privateEntity: + null); + state + .conductedBy + .insert( + 0, + newAgency); + + addConductedByController + .text = ""; + Navigator.pop( + context); + }); + }, + title: + "Add Conducted By Agency")), + SizedBox( + height: + showConductedByAgencyCategory + ? 12 + : 0, + ), + ////Sponsor Agency Category + SizedBox( + child: + showConductedByAgencyCategory + ? SearchField( + suggestionDirection: + SuggestionDirection + .up, + focusNode: + conductedByAgencyCategoryFocusNode, + itemHeight: + 70, + suggestions: state + .agencyCategory + .map((Category category) => SearchFieldListItem( + category + .name!, + item: + category, + child: + ListTile( + title: Text(category.name!), + subtitle: Text(category.industryClass!.name!), + ))) + .toList(), + emptyWidget: + Container( + height: + 100, decoration: - normalTextFieldStyle( - "Country*", - "Country"), - onChanged: - (Country? - value) { - selectedCountry = - value; - }, + box1(), + child: const Center( + child: + Text("No result found ...")), ), - ), - ), - ], - ); - }), - - ////Conducted By - StatefulBuilder( - builder: (context, setState) { - ////has sponsor switch - return Column( - children: [ - ////Add Conducted Agency============ - SizedBox( - child: SizedBox( - child: Column(children: [ - SearchField( - inputFormatters: [ - UpperCaseTextFormatter() - ], - itemHeight: 100, - focusNode: - conductedByFocusNode, - suggestions: state - .conductedBy - .map((Agency - agency) => - SearchFieldListItem( - agency - .name!, - item: - agency, - child: - ListTile( - title: - Text( - agency - .name!, - overflow: - TextOverflow.visible, - ), - subtitle: Text(agency.privateEntity == - true - ? "Private" - : agency.privateEntity == false - ? "Government" - : ""), - ))) - .toList(), - searchInputDecoration: - normalTextFieldStyle( - " Conducted By *", - "") - .copyWith( - suffixIcon: - GestureDetector( - child: const Icon( - Icons - .arrow_drop_down, - ), - onTap: () => - conductedByFocusNode - .unfocus(), - )), - ////SELETECTED - onSuggestionTap: - (agency) { - setState(() { - if (agency.item - ?.id != - null) { - selectedConductedByAgency = - Agency( - name: - null, - id: agency - .item! - .id); - } else { + onSuggestionTap: + (agencyCategory) { + setState( + () { + selectedConductedByAgencyCategory = + agencyCategory.item; selectedConductedByAgency = Agency( - id: null, - name: agency - .item! - .name); + id: selectedConductedByAgency + ?.id, + name: selectedConductedByAgency! + .name, + category: + selectedConductedByAgencyCategory, + privateEntity: + showConductedByAgencyPrivateRadio); + conductedByAgencyCategoryFocusNode + .unfocus(); + }); + }, + searchInputDecoration: normalTextFieldStyle( + "Category *", + "") + .copyWith( + suffixIcon: + IconButton( + icon: const Icon( + Icons + .arrow_drop_down), + onPressed: + () { + conductedByAgencyCategoryFocusNode + .unfocus(); + }, + )), + validator: + (value) { + if (value! + .isEmpty) { + return "This field is required"; } + return null; + }, + ) + : const SizedBox(), + ), - if (agency.item! - .privateEntity == - null) { - showConductedByAgencyCategory = - true; - showConductedByAgencyPrivateRadio = - true; - } else { - showConductedByAgencyCategory = - false; - showConductedByAgencyPrivateRadio = - false; - } - conductedByFocusNode + ////Sponsor Agency Private Radio + SizedBox( + height: + showConductedByAgencyPrivateRadio + ? 12 + : 0), + SizedBox( + child: showConductedByAgencyPrivateRadio + ? FormBuilderSwitch( + initialValue: + conductedByCategoryIsPrivate, + title: Text( + conductedByCategoryIsPrivate + ? "YES" + : "NO"), + decoration: normalTextFieldStyle( + "Private Entity?", + 'Private Entity?'), + + ////onvhange private sector + onChanged: + (value) { + setState( + () { + conductedByCategoryIsPrivate = + value!; + selectedConductedByAgency = Agency( + category: selectedConductedByAgency + ?.category, + id: selectedConductedByAgency + ?.id, + name: selectedConductedByAgency! + .name, + privateEntity: + conductedByCategoryIsPrivate); + conductedByAgencyCategoryFocusNode .unfocus(); }); }, - validator: (agency) { - if (agency! - .isEmpty) { - return "This field is required"; - } - return null; - }, - ////conducter empty widget - emptyWidget: - EmptyWidget( - controller: - addConductedByController, - onpressed: - () { - setState( - () { - Agency newAgency = Agency( - id: - null, - name: addConductedByController - .text - .toUpperCase(), - category: - null, - privateEntity: - null); - state - .conductedBy - .insert( - 0, - newAgency); + name: + 'sponsorAgencyPrivate', + validator: + FormBuilderValidators + .required(), + ) + : const SizedBox()), + ]), + )), + ], + ); + }), + ]), + ) + : const SizedBox()), + const SizedBox( + height: 12, + ), - addConductedByController - .text = ""; - Navigator.pop( - context); - }); - }, - title: - "Add Conducted By Agency")), - SizedBox( - height: - showConductedByAgencyCategory - ? 12 - : 0, - ), - ////Sponsor Agency Category - SizedBox( - child: - showConductedByAgencyCategory - ? SearchField( - suggestionDirection: - SuggestionDirection - .up, - focusNode: - conductedByAgencyCategoryFocusNode, - itemHeight: - 70, - suggestions: state - .agencyCategory - .map((Category category) => SearchFieldListItem( - category - .name!, - item: - category, - child: - ListTile( - title: Text(category.name!), - subtitle: Text(category.industryClass!.name!), - ))) - .toList(), - emptyWidget: - Container( - height: - 100, - decoration: - box1(), - child: const Center( - child: - Text("No result found ...")), - ), - onSuggestionTap: - (agencyCategory) { - setState( - () { - selectedConductedByAgencyCategory = - agencyCategory.item; - selectedConductedByAgency = Agency( - id: selectedConductedByAgency - ?.id, - name: selectedConductedByAgency! - .name, - category: - selectedConductedByAgencyCategory, - privateEntity: - showConductedByAgencyPrivateRadio); - conductedByAgencyCategoryFocusNode - .unfocus(); - }); - }, - searchInputDecoration: normalTextFieldStyle( - "Category *", - "") - .copyWith( - suffixIcon: - IconButton( - icon: const Icon( - Icons - .arrow_drop_down), - onPressed: - () { - conductedByAgencyCategoryFocusNode - .unfocus(); - }, - )), - validator: - (value) { - if (value! - .isEmpty) { - return "This field is required"; - } - return null; - }, - ) - : const SizedBox(), - ), - - ////Sponsor Agency Private Radio - SizedBox( - height: - showConductedByAgencyPrivateRadio - ? 12 - : 0), - SizedBox( - child: showConductedByAgencyPrivateRadio - ? FormBuilderSwitch( - initialValue: - conductedByCategoryIsPrivate, - title: Text( - conductedByCategoryIsPrivate - ? "YES" - : "NO"), - decoration: normalTextFieldStyle( - "Private Entity?", - 'Private Entity?'), - - ////onvhange private sector - onChanged: - (value) { - setState( - () { - conductedByCategoryIsPrivate = - value!; - selectedConductedByAgency = Agency( - category: selectedConductedByAgency - ?.category, - id: selectedConductedByAgency - ?.id, - name: selectedConductedByAgency! - .name, - privateEntity: - conductedByCategoryIsPrivate); - conductedByAgencyCategoryFocusNode - .unfocus(); - }); - }, - name: - 'sponsorAgencyPrivate', - validator: - FormBuilderValidators - .required(), - ) - : const SizedBox()), - ]), - )), - ], - ); - }), - ]), - ) - : const SizedBox()), - const SizedBox( - height: 12, - ), - - ////Sponsor - StatefulBuilder(builder: (context, setState) { - ////has sponsor switch - return Column( - children: [ - FormBuilderSwitch( - initialValue: hasSponsor, - activeColor: second, - onChanged: (value) { - setState(() { - hasSponsor = value!; - }); - }, - decoration: normalTextFieldStyle( - "Has Sponsor?", ''), - name: 'sponsor', - title: Text(hasSponsor ? "YES" : "NO"), - ), - ////Add Sponsor Agency============ - SizedBox( - child: hasSponsor - ? SizedBox( - child: Column(children: [ - const SizedBox( - height: 12, - ), - SearchField( - inputFormatters: [ - UpperCaseTextFormatter() - ], - itemHeight: 110, - focusNode: - sponsorByFocusNode, - suggestions: state + ////Sponsor + StatefulBuilder(builder: (context, setState) { + ////has sponsor switch + return Column( + children: [ + FormBuilderSwitch( + initialValue: hasSponsor, + activeColor: second, + onChanged: (value) { + setState(() { + hasSponsor = value!; + }); + }, + decoration: normalTextFieldStyle( + "Has Sponsor?", ''), + name: 'sponsor', + title: Text(hasSponsor ? "YES" : "NO"), + ), + ////Add Sponsor Agency============ + SizedBox( + child: hasSponsor + ? SizedBox( + child: Column(children: [ + const SizedBox( + height: 12, + ), + SearchField( + inputFormatters: [ + UpperCaseTextFormatter() + ], + itemHeight: 110, + focusNode: + sponsorByFocusNode, + suggestions: state + .sponsorAgencies + .map((Agency agency) => + SearchFieldListItem( + agency.name!, + item: agency, + child: ListTile( + title: Text( + agency + .name!, + overflow: + TextOverflow + .visible, + ), + subtitle: Text(agency + .privateEntity == + true + ? "Private" + : agency.privateEntity == + false + ? "Government" + : ""), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle( + " Sponsor Agency *", + "") + .copyWith( + suffixIcon: + GestureDetector( + child: const Icon( + Icons.arrow_drop_down, + ), + onTap: () => + sponsorByFocusNode + .unfocus(), + )), + ////SELETECTED + onSuggestionTap: (agency) { + setState(() { + if (agency.item?.id != + null) { + selectedSponsorAgency = + Agency( + name: null, + id: agency + .item! + .id); + } else { + selectedSponsorAgency = + Agency( + id: null, + name: agency + .item! + .name); + } + if (agency.item! + .privateEntity == + null) { + showSponsorCategoryAgency = + true; + showSponsorAgencyPrivateRadio = + true; + } else { + showSponsorCategoryAgency = + false; + showSponsorAgencyPrivateRadio = + false; + } + sponsorByFocusNode + .unfocus(); + }); + }, + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + ////sponsor empty widget + emptyWidget: EmptyWidget( + controller: + addSponsorAgencyController, + onpressed: () { + setState(() { + Agency newAgency = Agency( + id: null, + name: addSponsorAgencyController + .text + .toUpperCase(), + category: null, + privateEntity: + null); + state .sponsorAgencies - .map((Agency agency) => - SearchFieldListItem( - agency.name!, - item: agency, - child: ListTile( - title: Text( - agency - .name!, - overflow: - TextOverflow - .visible, - ), - subtitle: Text(agency - .privateEntity == - true - ? "Private" - : agency.privateEntity == - false - ? "Government" - : ""), - ))) - .toList(), - searchInputDecoration: - normalTextFieldStyle( - " Sponsor Agency *", - "") - .copyWith( - suffixIcon: - GestureDetector( - child: const Icon( - Icons.arrow_drop_down, - ), - onTap: () => - sponsorByFocusNode - .unfocus(), - )), - ////SELETECTED - onSuggestionTap: (agency) { - setState(() { - if (agency.item?.id != - null) { - selectedSponsorAgency = - Agency( - name: null, - id: agency - .item! - .id); - } else { - selectedSponsorAgency = - Agency( - id: null, - name: agency - .item! - .name); - } - if (agency.item! - .privateEntity == - null) { - showSponsorCategoryAgency = - true; - showSponsorAgencyPrivateRadio = - true; - } else { - showSponsorCategoryAgency = - false; - showSponsorAgencyPrivateRadio = - false; - } - sponsorByFocusNode - .unfocus(); - }); - }, - validator: (agency) { - if (agency!.isEmpty) { - return "This field is required"; - } - return null; - }, - ////sponsor empty widget - emptyWidget: EmptyWidget( - controller: - addSponsorAgencyController, - onpressed: () { - setState(() { - Agency newAgency = Agency( - id: null, - name: addSponsorAgencyController - .text - .toUpperCase(), - category: null, - privateEntity: - null); - state - .sponsorAgencies - .insert(0, - newAgency); + .insert(0, + newAgency); - addSponsorAgencyController - .text = ""; - Navigator.pop( - context); + addSponsorAgencyController + .text = ""; + Navigator.pop( + context); + }); + }, + title: + "Add Sponsor Agency")), + SizedBox( + height: + showSponsorCategoryAgency + ? 12 + : 0, + ), + ////Sponsor Agency Category + SizedBox( + child: + showSponsorCategoryAgency + ? SearchField( + suggestionDirection: + SuggestionDirection + .up, + focusNode: + sponsorAgencyCategoryFocusNode, + itemHeight: 70, + suggestions: state + .agencyCategory + .map((Category + category) => + SearchFieldListItem( + category + .name!, + item: + category, + child: + ListTile( + title: + Text(category.name!), + subtitle: + Text(category.industryClass!.name!), + ))) + .toList(), + emptyWidget: + Container( + height: 100, + decoration: + box1(), + child: const Center( + child: Text( + "No result found ...")), + ), + onSuggestionTap: + (agencyCategory) { + setState(() { + selectedSponsorAgencyCategory = + agencyCategory + .item; + selectedSponsorAgency = Agency( + id: selectedSponsorAgency + ?.id, + name: selectedSponsorAgency! + .name, + category: + selectedSponsorAgencyCategory, + privateEntity: + sponsorAgencyIsPrivate); + sponsorAgencyCategoryFocusNode + .unfocus(); + }); + }, + searchInputDecoration: + normalTextFieldStyle( + "Category *", + "") + .copyWith( + suffixIcon: + IconButton( + icon: const Icon( + Icons + .arrow_drop_down), + onPressed: () { + sponsorAgencyCategoryFocusNode + .unfocus(); + }, + )), + validator: + (value) { + if (value! + .isEmpty) { + return "This field is required"; + } + return null; + }, + ) + : const SizedBox(), + ), + + ////Sponsor Agency Private Radio + SizedBox( + height: + showSponsorAgencyPrivateRadio + ? 12 + : 0), + SizedBox( + child: + showSponsorAgencyPrivateRadio + ? FormBuilderSwitch( + initialValue: + sponsorAgencyIsPrivate, + title: Text( + sponsorAgencyIsPrivate + ? "YES" + : "NO"), + decoration: normalTextFieldStyle( + "Private Entity?", + 'Private Entity?'), + + ////onvhange private sector + onChanged: + (value) { + setState(() { + sponsorAgencyIsPrivate = + value!; + selectedSponsorAgency = Agency( + category: + selectedSponsorAgency + ?.category, + id: selectedSponsorAgency + ?.id, + name: selectedSponsorAgency! + .name, + privateEntity: + selectedSponsorAgency?.privateEntity); + sponsorAgencyCategoryFocusNode + .unfocus(); }); }, - title: - "Add Sponsor Agency")), - SizedBox( - height: - showSponsorCategoryAgency - ? 12 - : 0, - ), - ////Sponsor Agency Category - SizedBox( - child: - showSponsorCategoryAgency - ? SearchField( - suggestionDirection: - SuggestionDirection - .up, - focusNode: - sponsorAgencyCategoryFocusNode, - itemHeight: 70, - suggestions: state - .agencyCategory - .map((Category - category) => - SearchFieldListItem( - category - .name!, - item: - category, - child: - ListTile( - title: - Text(category.name!), - subtitle: - Text(category.industryClass!.name!), - ))) - .toList(), - emptyWidget: - Container( - height: 100, - decoration: - box1(), - child: const Center( - child: Text( - "No result found ...")), - ), - onSuggestionTap: - (agencyCategory) { - setState(() { - selectedSponsorAgencyCategory = - agencyCategory - .item; - selectedSponsorAgency = Agency( - id: selectedSponsorAgency - ?.id, - name: selectedSponsorAgency! - .name, - category: - selectedSponsorAgencyCategory, - privateEntity: - sponsorAgencyIsPrivate); - sponsorAgencyCategoryFocusNode - .unfocus(); - }); - }, - searchInputDecoration: - normalTextFieldStyle( - "Category *", - "") - .copyWith( - suffixIcon: - IconButton( - icon: const Icon( - Icons - .arrow_drop_down), - onPressed: () { - sponsorAgencyCategoryFocusNode - .unfocus(); - }, - )), - validator: - (value) { - if (value! - .isEmpty) { - return "This field is required"; - } - return null; - }, - ) - : const SizedBox(), - ), - ////Sponsor Agency Private Radio - SizedBox( - height: - showSponsorAgencyPrivateRadio - ? 12 - : 0), - SizedBox( - child: - showSponsorAgencyPrivateRadio - ? FormBuilderSwitch( - initialValue: - sponsorAgencyIsPrivate, - title: Text( - sponsorAgencyIsPrivate - ? "YES" - : "NO"), - decoration: normalTextFieldStyle( - "Private Entity?", - 'Private Entity?'), + name: + 'sponsorAgencyPrivate', + validator: + FormBuilderValidators + .required(), + ) + : const SizedBox()), + ]), + ) + : const SizedBox(), + ), + ], + ); + }), - ////onvhange private sector - onChanged: - (value) { - setState(() { - sponsorAgencyIsPrivate = - value!; - selectedSponsorAgency = Agency( - category: - selectedSponsorAgency - ?.category, - id: selectedSponsorAgency - ?.id, - name: selectedSponsorAgency! - .name, - privateEntity: - selectedSponsorAgency?.privateEntity); - sponsorAgencyCategoryFocusNode - .unfocus(); - }); - }, - - name: - 'sponsorAgencyPrivate', - validator: - FormBuilderValidators - .required(), - ) - : const SizedBox()), - ]), - ) - : const SizedBox(), - ), - ], - ); - }), - - const SizedBox( - height: 12, - ), - FormBuilderTextField( - validator: numericRequired, - name: "total_hours_attended", - keyboardType: TextInputType.number, - decoration: normalTextFieldStyle( - "Total Hours Attended *", - "Total Hours Attended *"), - ), - ], - ), - ), - SizedBox( - width: double.infinity, - height: 60, - child: ElevatedButton( - style: mainBtnStyle( - primary, Colors.transparent, second), - child: const Text(submit), - onPressed: () { - if (formKey.currentState!.saveAndValidate()) { - ConductedTraining? training; - Agency? sponsor; - Venue venue; - ////Address - if (overseas) { - venue = Venue( - id: null, - country: selectedCountry, - barangay: null, - category: null, - areaClass: null, - cityMunicipality: null); - } else { - venue = Venue( - id: null, - country: Country( - id: 175, - name: 'Philippines', - code: 'PH'), - barangay: selectedBarangay, - areaClass: null, - category: null, - cityMunicipality: selectedMunicipality); - } - if (showTrainingDetails) { - training = ConductedTraining( - locked: false, - id: selectedConductedTraining!.id, - venue: Venue( - country: selectedConductedTraining! - .venue!.country)); - } else { - training = ConductedTraining( - title: selectedTraining, - topic: selectedTopic, - id: null, - locked: false, - venue: venue, - toDate: - DateTime.parse(toDateController.text), - fromDate: DateTime.parse( - fromDateController.text), - totalHours: double.parse(formKey - .currentState!.value['total_hours']), - conductedBy: selectedConductedByAgency, - sessionsAttended: [], - learningDevelopmentType: - selectedLearningDevelopmentType); - } - if (hasSponsor) { - sponsor = selectedSponsorAgency; - } - LearningDevelopement learningDevelopement = - LearningDevelopement( - attachments: null, - sponsoredBy: sponsor, - conductedTraining: training, - totalHoursAttended: double.parse(formKey - .currentState! - .value['total_hours_attended'])); - final progress = ProgressHUD.of(context); - progress!.showWithText("Loading..."); - context.read().add( - AddLearningAndDevelopment( - learningDevelopement: - learningDevelopement, - profileId: widget.profileId, - token: widget.token)); + const SizedBox( + height: 12, + ), + FormBuilderTextField( + validator: numericRequired, + name: "total_hours_attended", + keyboardType: TextInputType.number, + decoration: normalTextFieldStyle( + "Total Hours Attended *", + "Total Hours Attended *"), + ), + const SizedBox(height: 16,), + SizedBox( + width: double.infinity, + height: 60, + child: ElevatedButton( + style: mainBtnStyle( + primary, Colors.transparent, second), + child: const Text(submit), + onPressed: () { + if (formKey.currentState!.saveAndValidate()) { + ConductedTraining? training; + Agency? sponsor; + Venue venue; + ////Address + if (overseas) { + venue = Venue( + id: null, + country: selectedCountry, + barangay: null, + category: null, + areaClass: null, + cityMunicipality: null); + } else { + venue = Venue( + id: null, + country: Country( + id: 175, + name: 'Philippines', + code: 'PH'), + barangay: selectedBarangay, + areaClass: null, + category: null, + cityMunicipality: selectedMunicipality); } - }, - ), + if (showTrainingDetails) { + training = ConductedTraining( + locked: false, + id: selectedConductedTraining!.id, + venue: Venue( + country: selectedConductedTraining! + .venue!.country)); + } else { + training = ConductedTraining( + title: selectedTraining, + topic: selectedTopic, + id: null, + locked: false, + venue: venue, + toDate: + DateTime.parse(toDateController.text), + fromDate: DateTime.parse( + fromDateController.text), + totalHours: double.parse(formKey + .currentState!.value['total_hours']), + conductedBy: selectedConductedByAgency, + sessionsAttended: [], + learningDevelopmentType: + selectedLearningDevelopmentType); + } + if (hasSponsor) { + sponsor = selectedSponsorAgency; + } + LearningDevelopement learningDevelopement = + LearningDevelopement( + attachments: null, + sponsoredBy: sponsor, + conductedTraining: training, + totalHoursAttended: double.parse(formKey + .currentState! + .value['total_hours_attended'])); + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + context.read().add( + AddLearningAndDevelopment( + learningDevelopement: + learningDevelopement, + profileId: widget.profileId, + token: widget.token)); + } + }, ), - ], - ); - }), - ), + ), + ], + ); + }), )); } return const Center( diff --git a/lib/screens/profile/components/learning_development/edit_modal.dart b/lib/screens/profile/components/learning_development/edit_modal.dart index ea5e04e..6937150 100644 --- a/lib/screens/profile/components/learning_development/edit_modal.dart +++ b/lib/screens/profile/components/learning_development/edit_modal.dart @@ -185,1387 +185,1380 @@ class _EditLearningAndDevelopmentScreenState to = state.learningDevelopement.conductedTraining?.toDate; return FormBuilder( key: formKey, - child: SizedBox( - height: screenHeight * 90, - child: Padding( - padding: const EdgeInsets.all(24), - child: Column( - children: [ - Flexible( - child: ListView( - children: [ - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - const SizedBox( - height: 10, - ), - SizedBox( - ////Training selected Textfield - child: FormBuilderTextField( - inputFormatters: [UpperCaseTextFormatter()], - onChanged: (value) { - selectedTraining = + child: Padding( + padding: const EdgeInsets.all(24), + child: ListView( + children: [ + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + const SizedBox( + height: 10, + ), + SizedBox( + ////Training selected Textfield + child: FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], + onChanged: (value) { + selectedTraining = + LearningDevelopmentType( + id: null, + title: selectedTrainingController + .text); + }, + enabled: enabled, + maxLines: 5, + controller: selectedTrainingController, + name: "", + decoration: normalTextFieldStyle("", "") + .copyWith( + labelText: "Training", + filled: !enabled, + fillColor: Colors.grey.shade300), + )), + SizedBox( + child: SizedBox( + child: Column(children: [ + const SizedBox( + height: 12, + ), + ////learning development type + FormBuilderDropdown< + LearningDevelopmentType>( + enabled: enabled, + name: "types", + decoration: normalTextFieldStyle( + "Learning Development Type *", + "") + .copyWith( + filled: !enabled, + fillColor: + Colors.grey.shade300), + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + items: state.types + .map((e) => DropdownMenuItem< + LearningDevelopmentType>( + value: e, + child: Text(e.title!))) + .toList(), + onChanged: (value) { + selectedLearningDevelopmentType = + value; + }, + initialValue: + selectedLearningDevelopmentType, + ), + + //// learning development topics + const SizedBox( + height: 12, + ), + StatefulBuilder( + builder: (context, setState) { + return SearchField( + inputFormatters: [ + UpperCaseTextFormatter() + ], + + enabled: enabled, + controller: currentTopicController, + focusNode: topicFocusNode, + itemHeight: 100, + suggestionsDecoration: box1(), + suggestions: state.topics + .map((LearningDevelopmentType + topic) => + SearchFieldListItem( + topic.title!, + item: topic, + child: Padding( + padding: + const EdgeInsets + .symmetric( + horizontal: + 10), + child: ListTile( + title: Text( + topic.title!, + softWrap: true, + ), + )))) + .toList(), + + searchInputDecoration: + normalTextFieldStyle("Topic *", + "") + .copyWith( + filled: !enabled, + fillColor: + Colors.grey.shade300, + suffixIcon: IconButton( + icon: const Icon(Icons + .arrow_drop_down), + onPressed: () { + topicFocusNode + .unfocus(); + }, + )), + onSuggestionTap: (topic) { + if (topic.item?.id != null) { + selectedTopic = + LearningDevelopmentType( + id: topic.item!.id, + title: null); + } else { + selectedTopic = LearningDevelopmentType( id: null, - title: selectedTrainingController - .text); - }, - enabled: enabled, - maxLines: 5, - controller: selectedTrainingController, - name: "", - decoration: normalTextFieldStyle("", "") - .copyWith( - labelText: "Training", - filled: !enabled, - fillColor: Colors.grey.shade300), - )), - SizedBox( - child: SizedBox( - child: Column(children: [ - const SizedBox( - height: 12, - ), - ////learning development type - FormBuilderDropdown< - LearningDevelopmentType>( - enabled: enabled, - name: "types", - decoration: normalTextFieldStyle( - "Learning Development Type *", - "") - .copyWith( - filled: !enabled, - fillColor: - Colors.grey.shade300), - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - items: state.types - .map((e) => DropdownMenuItem< - LearningDevelopmentType>( - value: e, - child: Text(e.title!))) - .toList(), - onChanged: (value) { - selectedLearningDevelopmentType = - value; - }, - initialValue: - selectedLearningDevelopmentType, - ), + title: topic.item!.title); + } + setState(() { + topicFocusNode.unfocus(); + }); + }, + ////EMPTY WIDGET + emptyWidget: EmptyWidget( + title: "Add Topic", + controller: addTopicController, + onpressed: () { + setState(() { + LearningDevelopmentType + newTopic = + LearningDevelopmentType( + id: null, + title: + addTopicController + .text + .toUpperCase()); + state.topics + .insert(0, newTopic); + topicFocusNode.unfocus(); + addTopicController.text = ""; + Navigator.pop(context); + }); + }), + validator: (position) { + if (position!.isEmpty) { + return "This field is required"; + } + return null; + }, + ); + }), - //// learning development topics - const SizedBox( - height: 12, - ), - StatefulBuilder( - builder: (context, setState) { - return SearchField( - inputFormatters: [ - UpperCaseTextFormatter() - ], - - enabled: enabled, - controller: currentTopicController, - focusNode: topicFocusNode, - itemHeight: 100, - suggestionsDecoration: box1(), - suggestions: state.topics - .map((LearningDevelopmentType - topic) => - SearchFieldListItem( - topic.title!, - item: topic, - child: Padding( - padding: - const EdgeInsets - .symmetric( - horizontal: - 10), - child: ListTile( - title: Text( - topic.title!, - softWrap: true, - ), - )))) - .toList(), - - searchInputDecoration: - normalTextFieldStyle("Topic *", - "") + const SizedBox( + height: 12, + ), + SizedBox( + width: screenWidth, + child: Row( + children: [ + //// FROM DATE + Flexible( + flex: 1, + child: DateTimePicker( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + use24HourFormat: false, + icon: const Icon( + Icons.date_range), + controller: + fromDateController, + firstDate: DateTime(1990), + lastDate: DateTime(2100), + selectableDayPredicate: + (date) { + if (to != null && + to!.microsecondsSinceEpoch < + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, + onChanged: (value) { + setState(() { + from = + DateTime.tryParse(value); + }); + }, + initialDate: to??=DateTime.now(), + timeHintText: + "Date of Examination/Conferment", + decoration: + normalTextFieldStyle( + "From *", "From *") .copyWith( filled: !enabled, - fillColor: - Colors.grey.shade300, - suffixIcon: IconButton( - icon: const Icon(Icons - .arrow_drop_down), + fillColor: Colors + .grey + .shade300, + prefixIcon: + const Icon( + Icons + .date_range, + color: Colors + .black87, + )), + )), + const SizedBox( + width: 12, + ), + //// TO DATE + Flexible( + flex: 1, + child: DateTimePicker( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + controller: toDateController, + firstDate: DateTime(1990), + lastDate: DateTime(2100), + decoration: + normalTextFieldStyle( + "To *", "To *") + .copyWith( + filled: !enabled, + fillColor: + Colors.grey.shade300, + prefixIcon: const Icon( + Icons.date_range, + color: Colors.black87, + ), + ), + selectableDayPredicate: (date) { + if (from != null && + from!.microsecondsSinceEpoch > + date.microsecondsSinceEpoch) { + return false; + } + return true; + }, + onChanged: (value) { + setState(() { + to = DateTime.tryParse(value); + }); + }, + initialDate: from??=DateTime.now() + ), + ), + ], + ), + ), + const SizedBox( + height: 12, + ), + //// total hours conducted + FormBuilderTextField( + initialValue: state.learningDevelopement + .conductedTraining!.totalHours + .toString(), + validator: numericRequired, + name: "total_hours", + decoration: normalTextFieldStyle( + "Total Hours Conducted *", + "0", + ).copyWith( + filled: !enabled, + fillColor: Colors.grey.shade300), + keyboardType: TextInputType.number, + ), + const SizedBox( + height: 12, + ), + ////Address + + const SizedBox( + height: 12, + ), + StatefulBuilder( + builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: normalTextFieldStyle( + "Overseas Address?", ''), + name: 'overseas', + title: + Text(overseas ? "YES" : "NO"), + ), + SizedBox( + height: overseas == true ? 8 : 0, + ), + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + FormBuilderDropdown< + Region?>( + enabled: !enabled + ? overseas + : true, + name: "region", + isExpanded: true, + initialValue: + selectedRegion, + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: + FormBuilderValidators + .required( + errorText: + "This field is required"), + onChanged: (Region? + region) async { + if (selectedRegion != + region) { + setState(() { + provinceCall = + true; + }); + selectedRegion = + region; + //// GET PROVINCES + try { + provinces = await LocationUtils + .instance + .getProvinces( + regionCode: selectedRegion! + .code + .toString()); + } catch (e) { + context + .read< + LearningDevelopmentBloc>() + .add(CallErrorState( + message: + e.toString())); + } + selectedProvince = + provinces![0]; + setState(() { + provinceCall = + false; + cityCall = true; + }); + //// GET CITIES + try { + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code!); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read< + LearningDevelopmentBloc>() + .add(CallErrorState( + message: + e.toString())); + } + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = + false; + barangayCall = + true; + }); + //// GET BARANGAY + try { + barangays = await LocationUtils + .instance + .getBarangay( + code: selectedMunicipality! + .code!); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read< + LearningDevelopmentBloc>() + .add(CallErrorState( + message: + e.toString())); + } + selectedBarangay = + barangays![0]; + setState(() { + barangayCall = + false; + }); + ////GET CITY MUNICIPALITY + try { + citymuns = await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code!); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read< + LearningDevelopmentBloc>() + .add(CallErrorState( + message: + e.toString())); + } + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = + false; + barangayCall = + true; + }); + //// GET BARANGAYS + try { + barangays = await LocationUtils + .instance + .getBarangay( + code: selectedMunicipality! + .code!); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read< + LearningDevelopmentBloc>() + .add(CallErrorState( + message: + e.toString())); + } + selectedBarangay = + barangays![0]; + setState(() { + barangayCall = + false; + }); + } + }, + decoration: normalTextFieldStyle( + "Region*", + "Region") + .copyWith( + filled: !enabled + ? !overseas + : false, + fillColor: Colors + .grey + .shade300), + items: state.regions.map< + DropdownMenuItem< + Region>>((Region + region) { + return DropdownMenuItem< + Region>( + value: region, + child: Text(region + .description!)); + }).toList(), + ), + const SizedBox( + height: 8, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: + ModalProgressHUD( + color: Colors + .transparent, + inAsyncCall: + provinceCall, + child: + DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => value == null + ? 'required' + : null, + isExpanded: + true, + value: + selectedProvince, + onChanged: + (Province? + province) async { + if (selectedProvince != + province) { + selectedProvince = + province; + setState( + () { + cityCall = + true; + }); + + //// GET CITIES + try { + citymuns = await LocationUtils + .instance + .getCities(code: selectedProvince!.code!); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read() + .add(CallErrorState(message: e.toString())); + } + selectedMunicipality = + citymuns![0]; + setState( + () { + cityCall = + false; + barangayCall = + true; + }); + //// GET BARANGAY + try { + barangays = await LocationUtils + .instance + .getBarangay(code: selectedMunicipality!.code!); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read() + .add(CallErrorState(message: e.toString())); + } + selectedBarangay = + barangays![0]; + setState( + () { + barangayCall = + false; + }); + } + }, + items: provinces == + null + ? [] + : provinces!.map>((Province + province) { + return DropdownMenuItem( + enabled: !enabled ? overseas : true, + value: province, + child: FittedBox( + child: Text(province.description!), + )); + }).toList(), + decoration: normalTextFieldStyle("Province*", "Province").copyWith( + filled: !enabled + ? !overseas + : false, + fillColor: Colors + .grey + .shade300)), + ), + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: + ModalProgressHUD( + color: Colors.white, + inAsyncCall: + cityCall, + child: DropdownButtonFormField< + CityMunicipality>( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + isExpanded: true, + onChanged: + (CityMunicipality? + city) async { + if (selectedMunicipality != + city) { + setState(() { + barangayCall = + true; + }); + selectedMunicipality = + city; + + //// GET BARANGAYS + try { + barangays = await LocationUtils + .instance + .getBarangay( + code: + selectedMunicipality!.code!); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read< + LearningDevelopmentBloc>() + .add(CallErrorState( + message: + e.toString())); + } + selectedBarangay = + barangays![ + 0]; + setState(() { + barangayCall = + false; + }); + } + }, + decoration: normalTextFieldStyle( + "Municipality*", + "Municipality") + .copyWith( + filled: !enabled + ? !overseas + : false, + fillColor: Colors + .grey + .shade300), + value: + selectedMunicipality, + items: citymuns == + null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality + c) { + return DropdownMenuItem( + enabled: !enabled + ? overseas + : true, + value: + c, + child: + Text(c.description!)); + }).toList(), + ), + ), + ), + //// BARANGAY + SizedBox( + height: 60, + child: + ModalProgressHUD( + color: Colors.white, + inAsyncCall: + barangayCall, + child: + DropdownButtonFormField< + Barangay>( + isExpanded: true, + onChanged: + (Barangay? + baragay) { + selectedBarangay = + baragay; + }, + decoration: normalTextFieldStyle( + "Barangay*", + "Barangay") + .copyWith( + filled: !enabled + ? !overseas + : false, + fillColor: Colors + .grey + .shade300), + value: + selectedBarangay, + items: barangays == + null + ? [] + : barangays!.map< + DropdownMenuItem< + Barangay>>((Barangay + barangay) { + return DropdownMenuItem( + enabled: !enabled + ? overseas + : true, + value: + barangay, + child: + Text(barangay.description!)); + }).toList(), + ), + ), + ), + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( + height: 60, + child: + FormBuilderDropdown< + Country>( + enabled: overseas, + initialValue: + selectedCountry + ?.id == + 175 + ? null + : selectedCountry, + validator: + FormBuilderValidators + .required( + errorText: + "This field is required"), + items: state.countries.map< + DropdownMenuItem< + Country>>( + (Country country) { + return DropdownMenuItem< + Country>( + value: country, + child: FittedBox( + child: Text( + country + .name!))); + }).toList(), + name: 'country', + decoration: normalTextFieldStyle( + "Country*", + "Country") + .copyWith( + filled: + !overseas, + fillColor: Colors + .grey + .shade300), + onChanged: + (Country? value) { + selectedCountry = + value; + }, + ), + ), + ), + ], + ); + }), + + ////Conducted By + StatefulBuilder( + builder: (context, setState) { + ////has sponsor switch + return Column( + children: [ + ////Add Conducted Agency============ + SizedBox( + child: SizedBox( + child: Column(children: [ + SearchField( + inputFormatters: [ + UpperCaseTextFormatter() + ], + enabled: enabled, + controller: + selectedConductedByController, + itemHeight: 100, + focusNode: + conductedByFocusNode, + suggestions: state + .conductedBy + .map((Agency agency) => + SearchFieldListItem( + agency.name!, + item: agency, + child: ListTile( + title: Text( + agency + .name!, + overflow: + TextOverflow + .visible, + ), + subtitle: Text(agency + .privateEntity == + true + ? "Private" + : agency.privateEntity == + false + ? "Government" + : ""), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle( + " Conducted By *", + "") + .copyWith( + suffixIcon: + GestureDetector( + child: + const Icon( + Icons + .arrow_drop_down, + ), + onTap: () => + conductedByFocusNode + .unfocus(), + ), + filled: + !enabled, + fillColor: Colors + .grey + .shade300), + ////SELETECTED + onSuggestionTap: (agency) { + setState(() { + if (agency.item?.id != + null) { + selectedConductedByAgency = + Agency( + name: null, + id: agency + .item! + .id); + } else { + selectedConductedByAgency = + Agency( + id: null, + name: agency + .item! + .name); + } + + if (agency.item! + .privateEntity == + null) { + showConductedByAgencyCategory = + true; + showConductedByAgencyPrivateRadio = + true; + } else { + showConductedByAgencyCategory = + false; + showConductedByAgencyPrivateRadio = + false; + } + conductedByFocusNode + .unfocus(); + }); + }, + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + ////conducter empty widget + emptyWidget: EmptyWidget( + controller: + addConductedByController, + onpressed: () { + setState(() { + Agency newAgency = Agency( + id: null, + name: addConductedByController + .text + .toUpperCase(), + category: null, + privateEntity: + null); + state.conductedBy + .insert(0, + newAgency); + + addConductedByController + .text = ""; + Navigator.pop( + context); + }); + }, + title: + "Add Conducted By Agency")), + SizedBox( + height: + showConductedByAgencyCategory + ? 12 + : 0, + ), + ////Conducted By Agency Category + SizedBox( + child: + showConductedByAgencyCategory + ? SearchField( + focusNode: + conductedByAgencyCategoryFocusNode, + itemHeight: 70, + suggestions: state + .agencyCategory + .map((Category + category) => + SearchFieldListItem( + category + .name!, + item: + category, + child: + ListTile( + title: + Text(category.name!), + subtitle: + Text(category.industryClass!.name!), + ))) + .toList(), + emptyWidget: + Container( + height: 100, + decoration: + box1(), + child: const Center( + child: Text( + "No result found ...")), + ), + onSuggestionTap: + (agencyCategory) { + setState(() { + selectedConductedByAgencyCategory = + agencyCategory + .item; + selectedConductedByAgency = Agency( + id: selectedConductedByAgency + ?.id, + name: selectedConductedByAgency! + .name, + category: + selectedConductedByAgencyCategory, + privateEntity: + showConductedByAgencyPrivateRadio); + conductedByAgencyCategoryFocusNode + .unfocus(); + }); + }, + searchInputDecoration: + normalTextFieldStyle( + "Category *", + "") + .copyWith( + suffixIcon: + IconButton( + icon: const Icon( + Icons + .arrow_drop_down), onPressed: () { - topicFocusNode + conductedByAgencyCategoryFocusNode .unfocus(); }, )), - onSuggestionTap: (topic) { - if (topic.item?.id != null) { - selectedTopic = - LearningDevelopmentType( - id: topic.item!.id, - title: null); - } else { - selectedTopic = - LearningDevelopmentType( - id: null, - title: topic.item!.title); - } - setState(() { - topicFocusNode.unfocus(); - }); - }, - ////EMPTY WIDGET - emptyWidget: EmptyWidget( - title: "Add Topic", - controller: addTopicController, - onpressed: () { - setState(() { - LearningDevelopmentType - newTopic = - LearningDevelopmentType( - id: null, - title: - addTopicController - .text - .toUpperCase()); - state.topics - .insert(0, newTopic); - topicFocusNode.unfocus(); - addTopicController.text = ""; - Navigator.pop(context); - }); - }), - validator: (position) { - if (position!.isEmpty) { - return "This field is required"; - } - return null; - }, - ); - }), - - const SizedBox( - height: 12, - ), - SizedBox( - width: screenWidth, - child: Row( - children: [ - //// FROM DATE - Flexible( - flex: 1, - child: DateTimePicker( - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - use24HourFormat: false, - icon: const Icon( - Icons.date_range), - controller: - fromDateController, - firstDate: DateTime(1990), - lastDate: DateTime(2100), - selectableDayPredicate: - (date) { - if (to != null && - to!.microsecondsSinceEpoch < - date.microsecondsSinceEpoch) { - return false; - } - return true; - }, - onChanged: (value) { - setState(() { - from = - DateTime.tryParse(value); - }); - }, - initialDate: to??=DateTime.now(), - timeHintText: - "Date of Examination/Conferment", - decoration: - normalTextFieldStyle( - "From *", "From *") - .copyWith( - filled: !enabled, - fillColor: Colors - .grey - .shade300, - prefixIcon: - const Icon( - Icons - .date_range, - color: Colors - .black87, - )), - )), - const SizedBox( - width: 12, - ), - //// TO DATE - Flexible( - flex: 1, - child: DateTimePicker( - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - controller: toDateController, - firstDate: DateTime(1990), - lastDate: DateTime(2100), - decoration: - normalTextFieldStyle( - "To *", "To *") - .copyWith( - filled: !enabled, - fillColor: - Colors.grey.shade300, - prefixIcon: const Icon( - Icons.date_range, - color: Colors.black87, - ), - ), - selectableDayPredicate: (date) { - if (from != null && - from!.microsecondsSinceEpoch > - date.microsecondsSinceEpoch) { - return false; - } - return true; - }, - onChanged: (value) { - setState(() { - to = DateTime.tryParse(value); - }); - }, - initialDate: from??=DateTime.now() - ), - ), - ], - ), - ), - const SizedBox( - height: 12, - ), - //// total hours conducted - FormBuilderTextField( - initialValue: state.learningDevelopement - .conductedTraining!.totalHours - .toString(), - validator: numericRequired, - name: "total_hours", - decoration: normalTextFieldStyle( - "Total Hours Conducted *", - "0", - ).copyWith( - filled: !enabled, - fillColor: Colors.grey.shade300), - keyboardType: TextInputType.number, - ), - const SizedBox( - height: 12, - ), - ////Address - - const SizedBox( - height: 12, - ), - StatefulBuilder( - builder: (context, setState) { - return Column( - children: [ - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - setState(() { - overseas = value!; - }); - }, - decoration: normalTextFieldStyle( - "Overseas Address?", ''), - name: 'overseas', - title: - Text(overseas ? "YES" : "NO"), - ), - SizedBox( - height: overseas == true ? 8 : 0, - ), - SizedBox( - child: overseas == false - ? Column( - children: [ - const SizedBox( - height: 12, - ), - ////REGION DROPDOWN - FormBuilderDropdown< - Region?>( - enabled: !enabled - ? overseas - : true, - name: "region", - isExpanded: true, - initialValue: - selectedRegion, - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: - FormBuilderValidators - .required( - errorText: - "This field is required"), - onChanged: (Region? - region) async { - if (selectedRegion != - region) { - setState(() { - provinceCall = - true; - }); - selectedRegion = - region; - //// GET PROVINCES - try { - provinces = await LocationUtils - .instance - .getProvinces( - regionCode: selectedRegion! - .code - .toString()); - } catch (e) { - context - .read< - LearningDevelopmentBloc>() - .add(CallErrorState( - message: - e.toString())); - } - selectedProvince = - provinces![0]; - setState(() { - provinceCall = - false; - cityCall = true; - }); - //// GET CITIES - try { - citymuns = await LocationUtils - .instance - .getCities( - code: selectedProvince! - .code!); - } catch (e) { - NavigationService - .navigatorKey - .currentContext - ?.read< - LearningDevelopmentBloc>() - .add(CallErrorState( - message: - e.toString())); - } - selectedMunicipality = - citymuns![0]; - setState(() { - cityCall = - false; - barangayCall = - true; - }); - //// GET BARANGAY - try { - barangays = await LocationUtils - .instance - .getBarangay( - code: selectedMunicipality! - .code!); - } catch (e) { - NavigationService - .navigatorKey - .currentContext - ?.read< - LearningDevelopmentBloc>() - .add(CallErrorState( - message: - e.toString())); - } - selectedBarangay = - barangays![0]; - setState(() { - barangayCall = - false; - }); - ////GET CITY MUNICIPALITY - try { - citymuns = await LocationUtils - .instance - .getCities( - code: selectedProvince! - .code!); - } catch (e) { - NavigationService - .navigatorKey - .currentContext - ?.read< - LearningDevelopmentBloc>() - .add(CallErrorState( - message: - e.toString())); - } - selectedMunicipality = - citymuns![0]; - setState(() { - cityCall = - false; - barangayCall = - true; - }); - //// GET BARANGAYS - try { - barangays = await LocationUtils - .instance - .getBarangay( - code: selectedMunicipality! - .code!); - } catch (e) { - NavigationService - .navigatorKey - .currentContext - ?.read< - LearningDevelopmentBloc>() - .add(CallErrorState( - message: - e.toString())); - } - selectedBarangay = - barangays![0]; - setState(() { - barangayCall = - false; - }); - } - }, - decoration: normalTextFieldStyle( - "Region*", - "Region") - .copyWith( - filled: !enabled - ? !overseas - : false, - fillColor: Colors - .grey - .shade300), - items: state.regions.map< - DropdownMenuItem< - Region>>((Region - region) { - return DropdownMenuItem< - Region>( - value: region, - child: Text(region - .description!)); - }).toList(), - ), - const SizedBox( - height: 8, - ), - //// PROVINCE DROPDOWN - SizedBox( - height: 60, - child: - ModalProgressHUD( - color: Colors - .transparent, - inAsyncCall: - provinceCall, - child: - DropdownButtonFormField< - Province?>( - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: (value) => value == null - ? 'required' - : null, - isExpanded: - true, - value: - selectedProvince, - onChanged: - (Province? - province) async { - if (selectedProvince != - province) { - selectedProvince = - province; - setState( - () { - cityCall = - true; - }); - - //// GET CITIES - try { - citymuns = await LocationUtils - .instance - .getCities(code: selectedProvince!.code!); - } catch (e) { - NavigationService - .navigatorKey - .currentContext - ?.read() - .add(CallErrorState(message: e.toString())); - } - selectedMunicipality = - citymuns![0]; - setState( - () { - cityCall = - false; - barangayCall = - true; - }); - //// GET BARANGAY - try { - barangays = await LocationUtils - .instance - .getBarangay(code: selectedMunicipality!.code!); - } catch (e) { - NavigationService - .navigatorKey - .currentContext - ?.read() - .add(CallErrorState(message: e.toString())); - } - selectedBarangay = - barangays![0]; - setState( - () { - barangayCall = - false; - }); - } - }, - items: provinces == - null - ? [] - : provinces!.map>((Province - province) { - return DropdownMenuItem( - enabled: !enabled ? overseas : true, - value: province, - child: FittedBox( - child: Text(province.description!), - )); - }).toList(), - decoration: normalTextFieldStyle("Province*", "Province").copyWith( - filled: !enabled - ? !overseas - : false, - fillColor: Colors - .grey - .shade300)), - ), - ), - ////CITY MUNICIPALITY - SizedBox( - height: 60, - child: - ModalProgressHUD( - color: Colors.white, - inAsyncCall: - cityCall, - child: DropdownButtonFormField< - CityMunicipality>( - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - isExpanded: true, - onChanged: - (CityMunicipality? - city) async { - if (selectedMunicipality != - city) { - setState(() { - barangayCall = - true; - }); - selectedMunicipality = - city; - - //// GET BARANGAYS - try { - barangays = await LocationUtils - .instance - .getBarangay( - code: - selectedMunicipality!.code!); - } catch (e) { - NavigationService - .navigatorKey - .currentContext - ?.read< - LearningDevelopmentBloc>() - .add(CallErrorState( - message: - e.toString())); - } - selectedBarangay = - barangays![ - 0]; - setState(() { - barangayCall = - false; - }); - } - }, - decoration: normalTextFieldStyle( - "Municipality*", - "Municipality") - .copyWith( - filled: !enabled - ? !overseas - : false, - fillColor: Colors - .grey - .shade300), - value: - selectedMunicipality, - items: citymuns == - null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality - c) { - return DropdownMenuItem( - enabled: !enabled - ? overseas - : true, - value: - c, - child: - Text(c.description!)); - }).toList(), - ), - ), - ), - //// BARANGAY - SizedBox( - height: 60, - child: - ModalProgressHUD( - color: Colors.white, - inAsyncCall: - barangayCall, - child: - DropdownButtonFormField< - Barangay>( - isExpanded: true, - onChanged: - (Barangay? - baragay) { - selectedBarangay = - baragay; - }, - decoration: normalTextFieldStyle( - "Barangay*", - "Barangay") - .copyWith( - filled: !enabled - ? !overseas - : false, - fillColor: Colors - .grey - .shade300), - value: - selectedBarangay, - items: barangays == - null - ? [] - : barangays!.map< - DropdownMenuItem< - Barangay>>((Barangay - barangay) { - return DropdownMenuItem( - enabled: !enabled - ? overseas - : true, - value: - barangay, - child: - Text(barangay.description!)); - }).toList(), - ), - ), - ), - ], + validator: + (value) { + if (value! + .isEmpty) { + return "This field is required"; + } + return null; + }, ) - //// COUNTRY DROPDOWN - : SizedBox( - height: 60, - child: - FormBuilderDropdown< - Country>( - enabled: overseas, + : const SizedBox(), + ), + + ////Sponsor Agency Private Radio + SizedBox( + height: + showConductedByAgencyPrivateRadio + ? 12 + : 0), + SizedBox( + child: + showConductedByAgencyPrivateRadio + ? FormBuilderSwitch( initialValue: - selectedCountry - ?.id == - 175 - ? null - : selectedCountry, + conductedByCategoryIsPrivate, + title: Text( + conductedByCategoryIsPrivate + ? "YES" + : "NO"), + decoration: normalTextFieldStyle( + "Private Entity?", + 'Private Entity?'), + + ////onvhange private sector + onChanged: + (value) { + setState(() { + conductedByCategoryIsPrivate = + value!; + selectedConductedByAgency = Agency( + category: + selectedConductedByAgency + ?.category, + id: selectedConductedByAgency + ?.id, + name: selectedConductedByAgency! + .name, + privateEntity: + conductedByCategoryIsPrivate); + conductedByAgencyCategoryFocusNode + .unfocus(); + }); + }, + name: + 'sponsorAgencyPrivate', validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - items: state.countries.map< - DropdownMenuItem< - Country>>( - (Country country) { - return DropdownMenuItem< - Country>( - value: country, - child: FittedBox( - child: Text( - country - .name!))); - }).toList(), - name: 'country', - decoration: normalTextFieldStyle( - "Country*", - "Country") - .copyWith( - filled: - !overseas, - fillColor: Colors - .grey - .shade300), - onChanged: - (Country? value) { - selectedCountry = - value; - }, - ), - ), + .required(), + ) + : const SizedBox()), + ]), + )), + ], + ); + }), + ]), + )), + const SizedBox( + height: 12, + ), + + ////Sponsor + StatefulBuilder(builder: (context, setState) { + ////has sponsor switch + return Column( + children: [ + FormBuilderSwitch( + initialValue: hasSponsor, + activeColor: second, + onChanged: (value) { + setState(() { + hasSponsor = value!; + }); + }, + decoration: normalTextFieldStyle( + "Has Sponsor?", ''), + name: 'sponsor', + title: + Text(hasSponsor ? "YES" : "NO"), + ), + ////Add Sponsor Agency============ + SizedBox( + child: hasSponsor + ? SizedBox( + child: Column(children: [ + const SizedBox( + height: 12, ), - ], - ); - }), - - ////Conducted By - StatefulBuilder( - builder: (context, setState) { - ////has sponsor switch - return Column( - children: [ - ////Add Conducted Agency============ - SizedBox( - child: SizedBox( - child: Column(children: [ - SearchField( - inputFormatters: [ - UpperCaseTextFormatter() - ], - enabled: enabled, - controller: - selectedConductedByController, - itemHeight: 100, - focusNode: - conductedByFocusNode, - suggestions: state - .conductedBy - .map((Agency agency) => - SearchFieldListItem( - agency.name!, - item: agency, - child: ListTile( - title: Text( - agency - .name!, - overflow: - TextOverflow - .visible, - ), - subtitle: Text(agency - .privateEntity == - true - ? "Private" - : agency.privateEntity == - false - ? "Government" - : ""), - ))) - .toList(), - searchInputDecoration: - normalTextFieldStyle( - " Conducted By *", - "") - .copyWith( - suffixIcon: - GestureDetector( - child: - const Icon( - Icons - .arrow_drop_down, - ), - onTap: () => - conductedByFocusNode - .unfocus(), - ), - filled: - !enabled, - fillColor: Colors - .grey - .shade300), - ////SELETECTED - onSuggestionTap: (agency) { - setState(() { - if (agency.item?.id != - null) { - selectedConductedByAgency = - Agency( - name: null, - id: agency - .item! - .id); - } else { - selectedConductedByAgency = - Agency( - id: null, - name: agency - .item! - .name); - } - - if (agency.item! - .privateEntity == - null) { - showConductedByAgencyCategory = - true; - showConductedByAgencyPrivateRadio = - true; - } else { - showConductedByAgencyCategory = - false; - showConductedByAgencyPrivateRadio = - false; - } - conductedByFocusNode - .unfocus(); - }); - }, - validator: (agency) { - if (agency!.isEmpty) { - return "This field is required"; - } - return null; - }, - ////conducter empty widget - emptyWidget: EmptyWidget( + SearchField( + inputFormatters: [ + UpperCaseTextFormatter() + ], + controller: + selectedSponsorAgencyController, + itemHeight: 70, + focusNode: + sponsorByFocusNode, + suggestions: state + .sponsorAgencies + .map((Agency + agency) => + SearchFieldListItem( + agency + .name!, + item: + agency, + child: + ListTile( + title: + Text( + agency + .name!, + overflow: + TextOverflow.ellipsis, + ), + subtitle: Text(agency.privateEntity == + true + ? "Private" + : agency.privateEntity == false + ? "Government" + : ""), + ))) + .toList(), + searchInputDecoration: + normalTextFieldStyle( + " Sponsor Agency *", + "") + .copyWith( + suffixIcon: + GestureDetector( + child: const Icon( + Icons + .arrow_drop_down, + ), + onTap: () => + sponsorByFocusNode + .unfocus(), + )), + ////SELETECTED + onSuggestionTap: + (agency) { + setState(() { + if (agency + .item?.id != + null) { + selectedSponsorAgency = + Agency( + name: + null, + id: agency + .item! + .id); + } else { + selectedSponsorAgency = + Agency( + id: null, + name: agency + .item! + .name); + } + if (agency.item! + .privateEntity == + null) { + showSponsorCategoryAgency = + true; + showSponsorAgencyPrivateRadio = + true; + } else { + showSponsorCategoryAgency = + false; + showSponsorAgencyPrivateRadio = + false; + } + sponsorByFocusNode + .unfocus(); + }); + }, + validator: (agency) { + if (agency!.isEmpty) { + return "This field is required"; + } + return null; + }, + ////sponsor empty widget + emptyWidget: + EmptyWidget( controller: - addConductedByController, + addSponsorAgencyController, onpressed: () { setState(() { Agency newAgency = Agency( - id: null, - name: addConductedByController + id: + null, + name: addSponsorAgencyController .text .toUpperCase(), - category: null, + category: + null, privateEntity: null); - state.conductedBy - .insert(0, + state + .sponsorAgencies + .insert( + 0, newAgency); - addConductedByController + addSponsorAgencyController .text = ""; Navigator.pop( context); }); }, title: - "Add Conducted By Agency")), - SizedBox( - height: - showConductedByAgencyCategory - ? 12 - : 0, - ), - ////Conducted By Agency Category - SizedBox( - child: - showConductedByAgencyCategory - ? SearchField( - focusNode: - conductedByAgencyCategoryFocusNode, - itemHeight: 70, - suggestions: state - .agencyCategory - .map((Category - category) => - SearchFieldListItem( - category - .name!, - item: - category, - child: - ListTile( - title: - Text(category.name!), - subtitle: - Text(category.industryClass!.name!), - ))) - .toList(), - emptyWidget: - Container( - height: 100, - decoration: - box1(), - child: const Center( - child: Text( - "No result found ...")), - ), - onSuggestionTap: - (agencyCategory) { - setState(() { - selectedConductedByAgencyCategory = - agencyCategory - .item; - selectedConductedByAgency = Agency( - id: selectedConductedByAgency - ?.id, - name: selectedConductedByAgency! - .name, - category: - selectedConductedByAgencyCategory, - privateEntity: - showConductedByAgencyPrivateRadio); - conductedByAgencyCategoryFocusNode - .unfocus(); - }); - }, - searchInputDecoration: - normalTextFieldStyle( - "Category *", - "") - .copyWith( - suffixIcon: - IconButton( - icon: const Icon( - Icons - .arrow_drop_down), - onPressed: () { - conductedByAgencyCategoryFocusNode - .unfocus(); - }, - )), - validator: - (value) { - if (value! - .isEmpty) { - return "This field is required"; - } - return null; - }, - ) - : const SizedBox(), - ), - - ////Sponsor Agency Private Radio - SizedBox( - height: - showConductedByAgencyPrivateRadio - ? 12 - : 0), - SizedBox( - child: - showConductedByAgencyPrivateRadio - ? FormBuilderSwitch( - initialValue: - conductedByCategoryIsPrivate, - title: Text( - conductedByCategoryIsPrivate - ? "YES" - : "NO"), - decoration: normalTextFieldStyle( - "Private Entity?", - 'Private Entity?'), - - ////onvhange private sector - onChanged: - (value) { - setState(() { - conductedByCategoryIsPrivate = - value!; - selectedConductedByAgency = Agency( - category: - selectedConductedByAgency - ?.category, - id: selectedConductedByAgency - ?.id, - name: selectedConductedByAgency! - .name, - privateEntity: - conductedByCategoryIsPrivate); - conductedByAgencyCategoryFocusNode - .unfocus(); - }); - }, - name: - 'sponsorAgencyPrivate', - validator: - FormBuilderValidators - .required(), - ) - : const SizedBox()), - ]), - )), - ], - ); - }), - ]), - )), - const SizedBox( - height: 12, - ), - - ////Sponsor - StatefulBuilder(builder: (context, setState) { - ////has sponsor switch - return Column( - children: [ - FormBuilderSwitch( - initialValue: hasSponsor, - activeColor: second, - onChanged: (value) { - setState(() { - hasSponsor = value!; - }); - }, - decoration: normalTextFieldStyle( - "Has Sponsor?", ''), - name: 'sponsor', - title: - Text(hasSponsor ? "YES" : "NO"), - ), - ////Add Sponsor Agency============ - SizedBox( - child: hasSponsor - ? SizedBox( - child: Column(children: [ - const SizedBox( - height: 12, - ), - SearchField( - inputFormatters: [ - UpperCaseTextFormatter() - ], - controller: - selectedSponsorAgencyController, - itemHeight: 70, - focusNode: - sponsorByFocusNode, - suggestions: state - .sponsorAgencies - .map((Agency - agency) => - SearchFieldListItem( - agency - .name!, - item: - agency, - child: - ListTile( - title: - Text( - agency - .name!, - overflow: - TextOverflow.ellipsis, - ), - subtitle: Text(agency.privateEntity == - true - ? "Private" - : agency.privateEntity == false - ? "Government" - : ""), - ))) - .toList(), - searchInputDecoration: - normalTextFieldStyle( - " Sponsor Agency *", - "") - .copyWith( - suffixIcon: - GestureDetector( - child: const Icon( - Icons - .arrow_drop_down, + "Add Sponsor Agency")), + SizedBox( + height: + showSponsorCategoryAgency + ? 12 + : 0, + ), + ////Sponsor Agency Category + SizedBox( + child: + showSponsorCategoryAgency + ? SearchField( + suggestionDirection: + SuggestionDirection + .up, + focusNode: + sponsorAgencyCategoryFocusNode, + itemHeight: + 70, + suggestions: state + .agencyCategory + .map((Category category) => SearchFieldListItem( + category + .name!, + item: + category, + child: + ListTile( + title: + Text(category.name!), + subtitle: + Text(category.industryClass!.name!), + ))) + .toList(), + emptyWidget: + Container( + height: 100, + decoration: + box1(), + child: const Center( + child: Text( + "No result found ...")), ), - onTap: () => - sponsorByFocusNode - .unfocus(), - )), - ////SELETECTED - onSuggestionTap: - (agency) { + onSuggestionTap: + (agencyCategory) { + setState( + () { + selectedSponsorAgencyCategory = + agencyCategory + .item; + selectedSponsorAgency = Agency( + id: selectedSponsorAgency + ?.id, + name: selectedSponsorAgency! + .name, + category: + selectedSponsorAgencyCategory, + privateEntity: + sponsorAgencyIsPrivate); + sponsorAgencyCategoryFocusNode + .unfocus(); + }); + }, + searchInputDecoration: normalTextFieldStyle( + "Category *", + "") + .copyWith( + suffixIcon: + IconButton( + icon: const Icon( + Icons + .arrow_drop_down), + onPressed: + () { + sponsorAgencyCategoryFocusNode + .unfocus(); + }, + )), + validator: + (value) { + if (value! + .isEmpty) { + return "This field is required"; + } + return null; + }, + ) + : const SizedBox(), + ), + + ////Sponsor Agency Private Radio + SizedBox( + height: + showSponsorAgencyPrivateRadio + ? 12 + : 0), + SizedBox( + child: showSponsorAgencyPrivateRadio + ? FormBuilderSwitch( + initialValue: + sponsorAgencyIsPrivate, + title: Text( + sponsorAgencyIsPrivate + ? "YES" + : "NO"), + decoration: normalTextFieldStyle( + "Private Entity?", + 'Private Entity?'), + + ////onvhange private sector + onChanged: + (value) { setState(() { - if (agency - .item?.id != - null) { - selectedSponsorAgency = - Agency( - name: - null, - id: agency - .item! - .id); - } else { - selectedSponsorAgency = - Agency( - id: null, - name: agency - .item! - .name); - } - if (agency.item! - .privateEntity == - null) { - showSponsorCategoryAgency = - true; - showSponsorAgencyPrivateRadio = - true; - } else { - showSponsorCategoryAgency = - false; - showSponsorAgencyPrivateRadio = - false; - } - sponsorByFocusNode + sponsorAgencyIsPrivate = + value!; + selectedSponsorAgency = Agency( + category: + selectedSponsorAgency + ?.category, + id: selectedSponsorAgency + ?.id, + name: selectedSponsorAgency! + .name, + privateEntity: + selectedSponsorAgency?.privateEntity); + sponsorAgencyCategoryFocusNode .unfocus(); }); }, - validator: (agency) { - if (agency!.isEmpty) { - return "This field is required"; - } - return null; - }, - ////sponsor empty widget - emptyWidget: - EmptyWidget( - controller: - addSponsorAgencyController, - onpressed: () { - setState(() { - Agency newAgency = Agency( - id: - null, - name: addSponsorAgencyController - .text - .toUpperCase(), - category: - null, - privateEntity: - null); - state - .sponsorAgencies - .insert( - 0, - newAgency); - addSponsorAgencyController - .text = ""; - Navigator.pop( - context); - }); - }, - title: - "Add Sponsor Agency")), - SizedBox( - height: - showSponsorCategoryAgency - ? 12 - : 0, - ), - ////Sponsor Agency Category - SizedBox( - child: - showSponsorCategoryAgency - ? SearchField( - suggestionDirection: - SuggestionDirection - .up, - focusNode: - sponsorAgencyCategoryFocusNode, - itemHeight: - 70, - suggestions: state - .agencyCategory - .map((Category category) => SearchFieldListItem( - category - .name!, - item: - category, - child: - ListTile( - title: - Text(category.name!), - subtitle: - Text(category.industryClass!.name!), - ))) - .toList(), - emptyWidget: - Container( - height: 100, - decoration: - box1(), - child: const Center( - child: Text( - "No result found ...")), - ), - onSuggestionTap: - (agencyCategory) { - setState( - () { - selectedSponsorAgencyCategory = - agencyCategory - .item; - selectedSponsorAgency = Agency( - id: selectedSponsorAgency - ?.id, - name: selectedSponsorAgency! - .name, - category: - selectedSponsorAgencyCategory, - privateEntity: - sponsorAgencyIsPrivate); - sponsorAgencyCategoryFocusNode - .unfocus(); - }); - }, - searchInputDecoration: normalTextFieldStyle( - "Category *", - "") - .copyWith( - suffixIcon: - IconButton( - icon: const Icon( - Icons - .arrow_drop_down), - onPressed: - () { - sponsorAgencyCategoryFocusNode - .unfocus(); - }, - )), - validator: - (value) { - if (value! - .isEmpty) { - return "This field is required"; - } - return null; - }, - ) - : const SizedBox(), - ), - - ////Sponsor Agency Private Radio - SizedBox( - height: - showSponsorAgencyPrivateRadio - ? 12 - : 0), - SizedBox( - child: showSponsorAgencyPrivateRadio - ? FormBuilderSwitch( - initialValue: - sponsorAgencyIsPrivate, - title: Text( - sponsorAgencyIsPrivate - ? "YES" - : "NO"), - decoration: normalTextFieldStyle( - "Private Entity?", - 'Private Entity?'), - - ////onvhange private sector - onChanged: - (value) { - setState(() { - sponsorAgencyIsPrivate = - value!; - selectedSponsorAgency = Agency( - category: - selectedSponsorAgency - ?.category, - id: selectedSponsorAgency - ?.id, - name: selectedSponsorAgency! - .name, - privateEntity: - selectedSponsorAgency?.privateEntity); - sponsorAgencyCategoryFocusNode - .unfocus(); - }); - }, - - name: - 'sponsorAgencyPrivate', - validator: - FormBuilderValidators - .required(), - ) - : const SizedBox()), - ]), - ) - : const SizedBox(), - ), - ], - ); - }), - const SizedBox( - height: 12, - ), - const SizedBox( - height: 12, - ), - FormBuilderTextField( - initialValue: state - .learningDevelopement.totalHoursAttended - .toString(), - validator: numericRequired, - name: "total_hours_attended", - keyboardType: TextInputType.number, - decoration: normalTextFieldStyle( - "Total Hours Attended *", - "Total Hours Attended *"), - ), - ], - ); - }), - ], - ), - ), - SizedBox( + name: + 'sponsorAgencyPrivate', + validator: + FormBuilderValidators + .required(), + ) + : const SizedBox()), + ]), + ) + : const SizedBox(), + ), + ], + ); + }), + const SizedBox( + height: 12, + ), + const SizedBox( + height: 12, + ), + FormBuilderTextField( + initialValue: state + .learningDevelopement.totalHoursAttended + .toString(), + validator: numericRequired, + name: "total_hours_attended", + keyboardType: TextInputType.number, + decoration: normalTextFieldStyle( + "Total Hours Attended *", + "Total Hours Attended *"), + ), + ], + ); + }), + const SizedBox(height: 16,), + SizedBox( height: 60, width: double.infinity, child: ElevatedButton( @@ -1642,8 +1635,7 @@ class _EditLearningAndDevelopmentScreenState }, ), ) - ], - ), + ], ), )); } diff --git a/lib/screens/profile/components/learning_development/learning_development_view_attachment.dart b/lib/screens/profile/components/learning_development/learning_development_view_attachment.dart index 7d07997..e86086a 100644 --- a/lib/screens/profile/components/learning_development/learning_development_view_attachment.dart +++ b/lib/screens/profile/components/learning_development/learning_development_view_attachment.dart @@ -1,71 +1,107 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_progress_hud/flutter_progress_hud.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart'; import 'package:unit2/bloc/profile/learningDevelopment/learning_development_bloc.dart'; import 'package:unit2/theme-data.dart/colors.dart'; +import 'package:unit2/utils/url_launcher_file_downloader.dart'; import 'package:url_launcher/url_launcher.dart'; -class LearningDevelopmentViewAttachment extends StatefulWidget { +class LearningDevelopmentViewAttachment extends StatefulWidget { const LearningDevelopmentViewAttachment({super.key}); @override - State createState() => _LearningDevelopmentViewAttachmentState(); + State createState() => + _LearningDevelopmentViewAttachmentState(); } -class _LearningDevelopmentViewAttachmentState extends State { +class _LearningDevelopmentViewAttachmentState + extends State { @override Widget build(BuildContext context) { String? fileUrl; + String? filename; + return Scaffold( - floatingActionButton: FloatingActionButton( - onPressed: ()async { - await launchUrl(Uri.parse(fileUrl!)); - }, - child: const Icon(Icons.file_download), - ), - appBar: AppBar( - title: const Text("Attachment"), - centerTitle: true, - actions: [ - IconButton(onPressed: () {}, icon: const Icon(Icons.share)), - ], - ), - body: BlocConsumer(builder: (context,state){ - if(state is LearningAndDevelopmentAttachmentViewState){ - fileUrl = state.fileUrl; - bool isPDF = state.fileUrl[state.fileUrl.length - 1] == 'f' ? true : false; - return SizedBox( - child: isPDF?SfPdfViewer.network( - state.fileUrl,onDocumentLoadFailed: (details) { - Center(child: Text(details.description),); - },): Center( - child: CachedNetworkImage( - progressIndicatorBuilder: (context, url, progress) { - return const SizedBox( - height: 100, - width: 100, - child: CircularProgressIndicator(color: primary,)); - }, - - imageBuilder: (context, imageProvider) => Container( - decoration: BoxDecoration( - image: DecorationImage( - image: imageProvider, fit: BoxFit.fill)), - ), - imageUrl: - state.fileUrl, - width: double.infinity, - height: 220, - fit: BoxFit.cover, - ), - ), - ); - } - return Container(); - },listener: (context, state) { - - },) - ); + floatingActionButton: FloatingActionButton( + onPressed: () async { + await launchInBrowser(fileUrl!); + }, + child: const Icon(Icons.file_download), + ), + appBar: AppBar( + title: const Text("Attachment"), + centerTitle: true, + actions: context.watch().state is LearningAndDevelopmentAttachmentViewState ? [ + IconButton(onPressed: () { + context.read().add(ShareAttachment(fileName: filename!, source: fileUrl!)); + }, icon: const Icon(Icons.share)), + ]:[] + ), + body: ProgressHUD( + padding: const EdgeInsets.all(24), + backgroundColor: Colors.black87, + indicatorWidget: const SpinKitFadingCircle(color: Colors.white), + child: + BlocConsumer( + builder: (context, state) { + if (state is LearningAndDevelopmentAttachmentViewState) { + fileUrl = state.fileUrl; + filename = state.filename; + bool isPDF = state.fileUrl[state.fileUrl.length - 1] == 'f' + ? true + : false; + return SizedBox( + child: isPDF + ? SfPdfViewer.network( + + state.fileUrl, + onDocumentLoadFailed: (details) { + Center( + child: Text(details.description), + ); + }, + ) + : Center( + child: CachedNetworkImage( + progressIndicatorBuilder: (context, url, progress) { + return const SizedBox( + height: 100, + width: 100, + child: CircularProgressIndicator( + color: primary, + )); + }, + imageBuilder: (context, imageProvider) => Container( + decoration: BoxDecoration( + image: DecorationImage( + image: imageProvider, fit: BoxFit.fill)), + ), + imageUrl: state.fileUrl, + width: double.infinity, + height: 220, + fit: BoxFit.cover, + ), + ), + ); + } + return Container(); + }, + listener: (context, state) { + + if (state is LearningDevelopmentLoadingState) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Please wait..."); + } + if (state is LearningAndDevelopmentAttachmentViewState || + state is LearningDevelopmentErrorState) { + final progress = ProgressHUD.of(context); + progress!.dismiss(); + } + }, + ), + )); } } diff --git a/lib/screens/profile/components/other_information/non_academic/edit_modal.dart b/lib/screens/profile/components/other_information/non_academic/edit_modal.dart index 91a8a9c..87610a8 100644 --- a/lib/screens/profile/components/other_information/non_academic/edit_modal.dart +++ b/lib/screens/profile/components/other_information/non_academic/edit_modal.dart @@ -83,6 +83,7 @@ class _EditNonAcademicRecognitionScreenState child: Column( children: [ FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], name: 'title', initialValue: state.nonAcademicRecognition.title, @@ -205,6 +206,7 @@ class _EditNonAcademicRecognitionScreenState Column( children: [ TextFormField( + inputFormatters: [UpperCaseTextFormatter()], controller: addAgencyController, decoration: diff --git a/lib/screens/profile/components/reference/add_modal.dart b/lib/screens/profile/components/reference/add_modal.dart index 5140b44..25508a8 100644 --- a/lib/screens/profile/components/reference/add_modal.dart +++ b/lib/screens/profile/components/reference/add_modal.dart @@ -53,316 +53,303 @@ class _AddReferenceScreenState extends State { return BlocBuilder( builder: (context, state) { if (state is AddReferenceState) { - return SingleChildScrollView( - child: FormBuilder( - key: formKey, - child: SizedBox( - height: screenHeight * .90, - child: Padding( - padding: const EdgeInsets.all(28), - child: Column( - + return FormBuilder( + key: formKey, + child: Padding( + padding: const EdgeInsets.all(28), + child: ListView( + children: [ + + Row( children: [ - const SizedBox(height: 15,), + ////LAST NAME Flexible( - child: ListView( - children: [ - - Row( - children: [ - ////LAST NAME - Flexible( - flex: 1, - child: FormBuilderTextField( - inputFormatters: [UpperCaseTextFormatter()], - decoration: normalTextFieldStyle( - "Last name *", "Last name *"), - name: "lastname", - validator: FormBuilderValidators.required( - errorText: "This field is required"), - ), - ), - const SizedBox( - width: 8, - ), - ////FIRST NAME - Flexible( - flex: 1, - child: FormBuilderTextField( - inputFormatters: [UpperCaseTextFormatter()], - decoration: normalTextFieldStyle( - "First name *", "First name *"), - name: "firstname", - validator: FormBuilderValidators.required( - errorText: "This field is required"), - ), - ), - ], - ), - const SizedBox( - height: 12, - ), - Row( - children: [ - Flexible( - flex: 1, - child: FormBuilderTextField( - inputFormatters: [ - UpperCaseTextFormatter() - ], - decoration: normalTextFieldStyle( - "Middle name ", ""), - name: "middlename", - - ), - ), - const SizedBox( - width: 8, - ), - ////Mobile - Flexible( - flex: 1, - child: FormBuilderTextField( - keyboardType: TextInputType.number, - inputFormatters: [mobileFormatter], - name: "mobile", - decoration: normalTextFieldStyle( - "Mobile *", - "+63 (9xx) xxx - xxxx"), - validator: FormBuilderValidators.required( - errorText: "This field is required"), - ), - ), - ], - ), - const SizedBox( - height: 12, - ), - FormBuilderDropdown( - name: 'category', - validator: - FormBuilderValidators.required(errorText: "This field is required"), - decoration: normalTextFieldStyle( - "Address Category", "Address Category"), - items: state.categories - .map>( - (AddressCategory cat) { - return DropdownMenuItem( - value: cat, - child: Text(cat.name!), - ); - }).toList(), - onChanged: (value) { - setState(() { - selectedCategory = value; - }); - }, - ), - const SizedBox( - height: 12, - ), - ////OVERSEAS ADDRESS - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - setState(() { - overseas = value!; - }); - }, - decoration: normalTextFieldStyle("", ''), - name: 'overseas', - title: const Text("Overseas Address?"), - ), - SizedBox( - height: overseas == true ? 12 : 0, - ), - SizedBox( - child: overseas == false - ? Column( - children: [ - const SizedBox( - height: 12, - ), - ////REGION DROPDOWN - FormBuilderDropdown( - autovalidateMode: - AutovalidateMode.onUserInteraction, - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - onChanged: (Region? region) async { - if (selectedRegion != region) { - setState(() { - provinceCall = true; - }); - selectedRegion = region; - getProvinces(); - } - }, - initialValue: null, - decoration: normalTextFieldStyle( - "Region*", "Region"), - name: 'region', - items: state.regions - .map>( - (Region region) { - return DropdownMenuItem( - value: region, - child: Text(region.description!)); - }).toList(), - ), - const SizedBox( - height: 12, - ), - //// PROVINCE DROPDOWN - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: provinceCall, - child: DropdownButtonFormField< - Province?>( - autovalidateMode: AutovalidateMode - .onUserInteraction, - validator: (value) => - value == null - ? 'This field is required' - : null, - isExpanded: true, - value: selectedProvince, - onChanged: (Province? province) { - if (selectedProvince != - province) { - setState(() { - cityCall = true; - }); - selectedProvince = province; - getCities(); - } - }, - items: provinces == null - ? [] - : provinces!.map< - DropdownMenuItem< - Province>>( - (Province province) { - return DropdownMenuItem( - value: province, - child: FittedBox( - child: Text(province - .description!), - )); - }).toList(), - decoration: normalTextFieldStyle( - "Province*", "Province")), - ), - ), - ////CITY MUNICIPALITY - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: cityCall, - child: DropdownButtonFormField< - CityMunicipality>( - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - isExpanded: true, - onChanged: - (CityMunicipality? city) { - if (selectedMunicipality != - city) { - setState(() { - barangayCall = true; - }); - selectedMunicipality = city; - getBarangays(); - } - }, - decoration: normalTextFieldStyle( - "Municipality*", - "Municipality"), - value: selectedMunicipality, - items: citymuns == null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality c) { - return DropdownMenuItem( - value: c, - child: Text( - c.description!)); - }).toList(), - ), - ), - ), - //// BARANGAY - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: barangayCall, - child: - DropdownButtonFormField( - isExpanded: true, - onChanged: (Barangay? baragay) { - selectedBarangay = baragay; - }, - decoration: normalTextFieldStyle( - "Barangay*", "Barangay"), - value: selectedBarangay, - items: barangays == null - ? [] - : barangays!.map< - DropdownMenuItem< - Barangay>>( - (Barangay barangay) { - return DropdownMenuItem( - value: barangay, - child: Text(barangay - .description!)); - }).toList(), - ), - ), - ), - ], - ) - //// COUNTRY DROPDOWN - : SizedBox( - height: 60, - child: FormBuilderDropdown( - initialValue: null, - validator: FormBuilderValidators.required( - errorText: "This field is required"), - items: state.countries - .map>( - (Country country) { - return DropdownMenuItem( - value: country, - child: FittedBox( - child: Text(country.name!))); - }).toList(), - name: 'country', - decoration: normalTextFieldStyle( - "Country*", "Country"), - onChanged: (Country? value) { - selectedCountry = value; - }, - ), - ), - ), - - - - ], + flex: 1, + child: FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], + decoration: normalTextFieldStyle( + "Last name *", "Last name *"), + name: "lastname", + validator: FormBuilderValidators.required( + errorText: "This field is required"), ), ), - SizedBox( + const SizedBox( + width: 8, + ), + ////FIRST NAME + Flexible( + flex: 1, + child: FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], + decoration: normalTextFieldStyle( + "First name *", "First name *"), + name: "firstname", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + ), + ), + ], + ), + const SizedBox( + height: 12, + ), + Row( + children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + inputFormatters: [ + UpperCaseTextFormatter() + ], + decoration: normalTextFieldStyle( + "Middle name ", ""), + name: "middlename", + + ), + ), + const SizedBox( + width: 8, + ), + ////Mobile + Flexible( + flex: 1, + child: FormBuilderTextField( + keyboardType: TextInputType.number, + inputFormatters: [mobileFormatter], + name: "mobile", + decoration: normalTextFieldStyle( + "Mobile *", + "+63 (9xx) xxx - xxxx"), + validator: FormBuilderValidators.required( + errorText: "This field is required"), + ), + ), + ], + ), + const SizedBox( + height: 12, + ), + FormBuilderDropdown( + name: 'category', + validator: + FormBuilderValidators.required(errorText: "This field is required"), + decoration: normalTextFieldStyle( + "Address Category", "Address Category"), + items: state.categories + .map>( + (AddressCategory cat) { + return DropdownMenuItem( + value: cat, + child: Text(cat.name!), + ); + }).toList(), + onChanged: (value) { + setState(() { + selectedCategory = value; + }); + }, + ), + const SizedBox( + height: 12, + ), + ////OVERSEAS ADDRESS + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Overseas Address?"), + ), + SizedBox( + height: overseas == true ? 12 : 0, + ), + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + FormBuilderDropdown( + autovalidateMode: + AutovalidateMode.onUserInteraction, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + onChanged: (Region? region) async { + if (selectedRegion != region) { + setState(() { + provinceCall = true; + }); + selectedRegion = region; + getProvinces(); + } + }, + initialValue: null, + decoration: normalTextFieldStyle( + "Region*", "Region"), + name: 'region', + items: state.regions + .map>( + (Region region) { + return DropdownMenuItem( + value: region, + child: Text(region.description!)); + }).toList(), + ), + const SizedBox( + height: 12, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null + ? 'This field is required' + : null, + isExpanded: true, + value: selectedProvince, + onChanged: (Province? province) { + if (selectedProvince != + province) { + setState(() { + cityCall = true; + }); + selectedProvince = province; + getCities(); + } + }, + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province province) { + return DropdownMenuItem( + value: province, + child: FittedBox( + child: Text(province + .description!), + )); + }).toList(), + decoration: normalTextFieldStyle( + "Province*", "Province")), + ), + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: DropdownButtonFormField< + CityMunicipality>( + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + isExpanded: true, + onChanged: + (CityMunicipality? city) { + if (selectedMunicipality != + city) { + setState(() { + barangayCall = true; + }); + selectedMunicipality = city; + getBarangays(); + } + }, + decoration: normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality c) { + return DropdownMenuItem( + value: c, + child: Text( + c.description!)); + }).toList(), + ), + ), + ), + //// BARANGAY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: barangayCall, + child: + DropdownButtonFormField( + isExpanded: true, + onChanged: (Barangay? baragay) { + selectedBarangay = baragay; + }, + decoration: normalTextFieldStyle( + "Barangay*", "Barangay"), + value: selectedBarangay, + items: barangays == null + ? [] + : barangays!.map< + DropdownMenuItem< + Barangay>>( + (Barangay barangay) { + return DropdownMenuItem( + value: barangay, + child: Text(barangay + .description!)); + }).toList(), + ), + ), + ), + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( + height: 60, + child: FormBuilderDropdown( + initialValue: null, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + items: state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox( + child: Text(country.name!))); + }).toList(), + name: 'country', + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) { + selectedCountry = value; + }, + ), + ), + ), + const SizedBox(height: 16,), + SizedBox( width: double.infinity, height: 60, child: ElevatedButton( @@ -438,13 +425,12 @@ class _AddReferenceScreenState extends State { } }, ), - ), - - ], - ), - ), - )), - ); + ), + + + ], + ), + )); } return Container(); }, diff --git a/lib/screens/profile/components/reference/edit_modal.dart b/lib/screens/profile/components/reference/edit_modal.dart index b233d94..658db44 100644 --- a/lib/screens/profile/components/reference/edit_modal.dart +++ b/lib/screens/profile/components/reference/edit_modal.dart @@ -72,473 +72,461 @@ class _EditReferenceScreenState extends State { return FormBuilder( key: formKey, - child: SizedBox( - height: screenHeight * .90, - child: Padding( - padding: const EdgeInsets.all(28), - child: Column( - children: [ - Flexible( - child: Column( - children: [ - const SizedBox(height: 25), - Row( - children: [ - ////LAST NAME - Flexible( - flex: 1, - child: FormBuilderTextField( - inputFormatters: [UpperCaseTextFormatter(),mobileFormatter], - initialValue: state.ref.lastName, - decoration: normalTextFieldStyle( - "Last name *", "Last name *"), - name: "lastname", - validator: FormBuilderValidators.required( - errorText: "This field is required"), - ), - ), - const SizedBox( - width: 8, - ), - ////FIRST NAME - Flexible( - flex: 1, - child: FormBuilderTextField( - inputFormatters: [UpperCaseTextFormatter()], - initialValue: state.ref.firstName, - decoration: normalTextFieldStyle( - "First name *", "First name *"), - name: "firstname", - validator: FormBuilderValidators.required( - errorText: "This field is required"), - ), - ), - ], - ), - const SizedBox( - height: 12, - ), - Row( - children: [ - Flexible( - flex: 1, - child: FormBuilderTextField( - inputFormatters: [UpperCaseTextFormatter()], - initialValue: state.ref.middleName, - decoration: normalTextFieldStyle( - "Middle name *", "Midlle name *"), - name: "middlename", - ), - ), - const SizedBox( - width: 8, - ), - ////CATEGORY - Flexible( - flex: 1, - child: FormBuilderTextField( - initialValue: state.ref.contactNo, - name: "mobile", - decoration: normalTextFieldStyle( - "Tel./Mobile *", "Tel./Mobile"), - validator: FormBuilderValidators.required( - errorText: "This field is required"), - ), - ), - ], - ), - const SizedBox( - height: 12, - ), - FormBuilderDropdown( - name: 'category', - validator: FormBuilderValidators.required( - errorText: "This field is required"), - decoration: normalTextFieldStyle( - "Address Category", "Address Category"), - items: state.categories - .map>( - (AddressCategory cat) { - return DropdownMenuItem( - value: cat, - child: Text(cat.name!), - ); - }).toList(), - initialValue: selectedCategory, - onChanged: (value) { - selectedCategory = value; - }, - ), - const SizedBox( - height: 12, - ), - ////OVERSEAS ADDRESS - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - FormBuilderSwitch( - initialValue: overseas, - activeColor: second, - onChanged: (value) { - setState(() { - overseas = value!; - }); - }, - decoration: normalTextFieldStyle("", ''), - name: 'overseas', - title: const Text("Overseas Address?"), - ), - SizedBox( - height: overseas == true ? 12 : 0, - ), - SizedBox( - child: overseas == false - ? Column( - children: [ - const SizedBox( - height: 12, - ), - ////REGION DROPDOWN - DropdownButtonFormField( - isExpanded: true, - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - onChanged: - (Region? region) async { - setState(() { - provinceCall = true; + child: Padding( + padding: const EdgeInsets.all(28), + child: ListView( + children: [ + Row( + children: [ + ////LAST NAME + Flexible( + flex: 1, + child: FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter(),mobileFormatter], + initialValue: state.ref.lastName, + decoration: normalTextFieldStyle( + "Last name *", "Last name *"), + name: "lastname", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + ), + ), + const SizedBox( + width: 8, + ), + ////FIRST NAME + Flexible( + flex: 1, + child: FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], + initialValue: state.ref.firstName, + decoration: normalTextFieldStyle( + "First name *", "First name *"), + name: "firstname", + validator: FormBuilderValidators.required( + errorText: "This field is required"), + ), + ), + ], + ), + const SizedBox( + height: 12, + ), + Row( + children: [ + Flexible( + flex: 1, + child: FormBuilderTextField( + inputFormatters: [UpperCaseTextFormatter()], + initialValue: state.ref.middleName, + decoration: normalTextFieldStyle( + "Middle name *", "Midlle name *"), + name: "middlename", + ), + ), + const SizedBox( + width: 8, + ), + ////CATEGORY + Flexible( + flex: 1, + child: FormBuilderTextField( + initialValue: state.ref.contactNo, + name: "mobile", + decoration: normalTextFieldStyle( + "Tel./Mobile *", "Tel./Mobile"), + validator: FormBuilderValidators.required( + errorText: "This field is required"), + ), + ), + ], + ), + const SizedBox( + height: 12, + ), + FormBuilderDropdown( + name: 'category', + validator: FormBuilderValidators.required( + errorText: "This field is required"), + decoration: normalTextFieldStyle( + "Address Category", "Address Category"), + items: state.categories + .map>( + (AddressCategory cat) { + return DropdownMenuItem( + value: cat, + child: Text(cat.name!), + ); + }).toList(), + initialValue: selectedCategory, + onChanged: (value) { + selectedCategory = value; + }, + ), + const SizedBox( + height: 12, + ), + ////OVERSEAS ADDRESS + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: normalTextFieldStyle("", ''), + name: 'overseas', + title: const Text("Overseas Address?"), + ), + SizedBox( + height: overseas == true ? 12 : 0, + ), + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + DropdownButtonFormField( + isExpanded: true, + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + onChanged: + (Region? region) async { + setState(() { + provinceCall = true; - selectedRegion = region; - }); - //// GET PROVINCES - try { - provinces = await LocationUtils - .instance - .getProvinces( - regionCode: - selectedRegion! - .code - .toString()); - } catch (e) { - context - .read() - .add(CallErrorState()); - } - selectedProvince = - provinces![0]; - setState(() { - provinceCall = false; - cityCall = true; - }); - ////GET CITY MUNICIPALITY - try { - citymuns = await LocationUtils - .instance - .getCities( - code: - selectedProvince! - .code!); - } catch (e) { - NavigationService - .navigatorKey - .currentContext - ?.read() - .add(CallErrorState()); - } - selectedMunicipality = - citymuns![0]; - setState(() { - cityCall = false; - barangayCall = true; - }); - //// GET BARANGAYS - try { - barangays = await LocationUtils + selectedRegion = region; + }); + //// GET PROVINCES + try { + provinces = await LocationUtils + .instance + .getProvinces( + regionCode: + selectedRegion! + .code + .toString()); + } catch (e) { + context + .read() + .add(CallErrorState()); + } + selectedProvince = + provinces![0]; + setState(() { + provinceCall = false; + cityCall = true; + }); + ////GET CITY MUNICIPALITY + try { + citymuns = await LocationUtils + .instance + .getCities( + code: + selectedProvince! + .code!); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read() + .add(CallErrorState()); + } + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + barangayCall = true; + }); + //// GET BARANGAYS + try { + barangays = await LocationUtils + .instance + .getBarangay( + code: + selectedMunicipality! + .code!); + selectedBarangay = + barangays![0]; + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read() + .add(CallErrorState()); + } + setState(() { + barangayCall = false; + }); + }, + value: selectedRegion, + decoration: + normalTextFieldStyle( + "Region*", "Region"), + items: state.regions.map< + DropdownMenuItem< + Region>>( + (Region region) { + return DropdownMenuItem< + Region>( + value: region, + child: Text( + region.description!)); + }).toList(), + ), + const SizedBox( + height: 12, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + onChanged: (Province? + province) async { + selectedProvince = + province; + setState(() { + cityCall = true; + }); + //// GET CITIES + try { + citymuns = + await LocationUtils + .instance + .getCities( + code: selectedProvince! + .code!); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read< + ReferencesBloc>() + .add( + CallErrorState()); + } + selectedMunicipality = + citymuns![0]; + setState(() { + cityCall = false; + barangayCall = true; + }); + //// GET BARANGAY + try { + barangays = + await LocationUtils + .instance + .getBarangay( + code: selectedMunicipality! + .code!); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read< + ReferencesBloc>() + .add( + CallErrorState()); + } + selectedBarangay = + barangays![0]; + setState(() { + barangayCall = false; + }); + }, + value: selectedProvince, + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province + province) { + return DropdownMenuItem( + value: + province, + child: + FittedBox( + child: Text( + province + .description!), + )); + }).toList(), + decoration: + normalTextFieldStyle( + "Province*", + "Province")), + ), + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: + DropdownButtonFormField< + CityMunicipality>( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + isExpanded: true, + onChanged: + (CityMunicipality? + city) async { + setState(() { + barangayCall = true; + }); + selectedMunicipality = + city; + //// GET BARANGAYS + try { + barangays = + await LocationUtils .instance .getBarangay( - code: - selectedMunicipality! - .code!); - selectedBarangay = - barangays![0]; - } catch (e) { - NavigationService - .navigatorKey - .currentContext - ?.read() - .add(CallErrorState()); - } - setState(() { - barangayCall = false; - }); - }, - value: selectedRegion, - decoration: - normalTextFieldStyle( - "Region*", "Region"), - items: state.regions.map< + code: selectedMunicipality! + .code!); + } catch (e) { + NavigationService + .navigatorKey + .currentContext + ?.read< + ReferencesBloc>() + .add( + CallErrorState()); + } + selectedBarangay = + barangays![0]; + setState(() { + barangayCall = false; + }); + }, + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< DropdownMenuItem< - Region>>( - (Region region) { - return DropdownMenuItem< - Region>( - value: region, - child: Text( - region.description!)); - }).toList(), - ), - const SizedBox( - height: 12, - ), - //// PROVINCE DROPDOWN - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: provinceCall, - child: DropdownButtonFormField< - Province?>( - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - onChanged: (Province? - province) async { - selectedProvince = - province; - setState(() { - cityCall = true; - }); - //// GET CITIES - try { - citymuns = - await LocationUtils - .instance - .getCities( - code: selectedProvince! - .code!); - } catch (e) { - NavigationService - .navigatorKey - .currentContext - ?.read< - ReferencesBloc>() - .add( - CallErrorState()); - } - selectedMunicipality = - citymuns![0]; - setState(() { - cityCall = false; - barangayCall = true; - }); - //// GET BARANGAY - try { - barangays = - await LocationUtils - .instance - .getBarangay( - code: selectedMunicipality! - .code!); - } catch (e) { - NavigationService - .navigatorKey - .currentContext - ?.read< - ReferencesBloc>() - .add( - CallErrorState()); - } - selectedBarangay = - barangays![0]; - setState(() { - barangayCall = false; - }); - }, - value: selectedProvince, - items: provinces == null - ? [] - : provinces!.map< - DropdownMenuItem< - Province>>( - (Province - province) { - return DropdownMenuItem( - value: - province, - child: - FittedBox( - child: Text( - province - .description!), - )); - }).toList(), - decoration: - normalTextFieldStyle( - "Province*", - "Province")), - ), - ), - ////CITY MUNICIPALITY - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: cityCall, - child: - DropdownButtonFormField< - CityMunicipality>( - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - isExpanded: true, - onChanged: - (CityMunicipality? - city) async { - setState(() { - barangayCall = true; - }); - selectedMunicipality = - city; - //// GET BARANGAYS - try { - barangays = - await LocationUtils - .instance - .getBarangay( - code: selectedMunicipality! - .code!); - } catch (e) { - NavigationService - .navigatorKey - .currentContext - ?.read< - ReferencesBloc>() - .add( - CallErrorState()); - } - selectedBarangay = - barangays![0]; - setState(() { - barangayCall = false; - }); - }, - decoration: - normalTextFieldStyle( - "Municipality*", - "Municipality"), - value: selectedMunicipality, - items: citymuns == null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality - c) { - return DropdownMenuItem( - value: c, - child: Text(c - .description!)); - }).toList(), - ), - ), - ), - //// BARANGAY - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: barangayCall, - child: - DropdownButtonFormField< - Barangay>( - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - isExpanded: true, - onChanged: - (Barangay? baragay) { - selectedBarangay = - baragay; - }, - decoration: - normalTextFieldStyle( - "Barangay*", - "Barangay"), - value: selectedBarangay, - items: barangays == null - ? [] - : barangays!.map< - DropdownMenuItem< - Barangay>>( - (Barangay - barangay) { - return DropdownMenuItem( - value: barangay, - child: Text(barangay - .description!)); - }).toList(), - ), - ), - ), - ], - ) - //// COUNTRY DROPDOWN - : SizedBox( - height: 60, - child: DropdownButtonFormField< - Country>( - isExpanded: true, - value: selectedCountry?.id == 175 - ? null - : selectedCountry, - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - items: state.countries.map< - DropdownMenuItem< - Country>>( - (Country country) { - return DropdownMenuItem< - Country>( - value: country, - child: FittedBox( - child: Text( - country.name!))); - }).toList(), - - decoration: normalTextFieldStyle( - "Country*", "Country"), - //// country dropdown - onChanged: (Country? value) { - selectedCountry = value; - }, - ), + CityMunicipality>>( + (CityMunicipality + c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), ), - ), - ], - ); - }), + ), + ), + //// BARANGAY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: barangayCall, + child: + DropdownButtonFormField< + Barangay>( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + isExpanded: true, + onChanged: + (Barangay? baragay) { + selectedBarangay = + baragay; + }, + decoration: + normalTextFieldStyle( + "Barangay*", + "Barangay"), + value: selectedBarangay, + items: barangays == null + ? [] + : barangays!.map< + DropdownMenuItem< + Barangay>>( + (Barangay + barangay) { + return DropdownMenuItem( + value: barangay, + child: Text(barangay + .description!)); + }).toList(), + ), + ), + ), + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( + height: 60, + child: DropdownButtonFormField< + Country>( + isExpanded: true, + value: selectedCountry?.id == 175 + ? null + : selectedCountry, + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + items: state.countries.map< + DropdownMenuItem< + Country>>( + (Country country) { + return DropdownMenuItem< + Country>( + value: country, + child: FittedBox( + child: Text( + country.name!))); + }).toList(), - const SizedBox( - height: 20, - ), - ], - ), - ), - SizedBox( + decoration: normalTextFieldStyle( + "Country*", "Country"), + //// country dropdown + onChanged: (Country? value) { + selectedCountry = value; + }, + ), + ), + ), + ], + ); + }), + const SizedBox(height: 16,), + SizedBox( width: double.infinity, height: 60, child: ElevatedButton( @@ -616,8 +604,7 @@ class _EditReferenceScreenState extends State { }, ), ), - ], - ), + ], ), )); } diff --git a/lib/screens/profile/components/references_screen.dart b/lib/screens/profile/components/references_screen.dart index ab349c4..a9731fe 100644 --- a/lib/screens/profile/components/references_screen.dart +++ b/lib/screens/profile/components/references_screen.dart @@ -23,7 +23,7 @@ class ReferencesScreen extends StatelessWidget { int? profileId; String? token; return Scaffold( - resizeToAvoidBottomInset: false, + resizeToAvoidBottomInset: true, appBar: AppBar( title: context.watch().state is AddReferenceState ? const Text("Add Personal Reference") diff --git a/lib/screens/profile/components/voluntary_works/add_modal.dart b/lib/screens/profile/components/voluntary_works/add_modal.dart index 88c5198..ffea1bf 100644 --- a/lib/screens/profile/components/voluntary_works/add_modal.dart +++ b/lib/screens/profile/components/voluntary_works/add_modal.dart @@ -85,597 +85,592 @@ class _AddVoluntaryWorkScreenState extends State { padding: const EdgeInsets.symmetric(vertical: 32, horizontal: 28), child: FormBuilder( key: formKey, - child: Column( + child: ListView( children: [ - Flexible( - child: ListView( + ////POSITIONS + StatefulBuilder(builder: (context, setState) { + return SearchField( + inputFormatters: [UpperCaseTextFormatter()], + itemHeight: 70, + suggestionsDecoration: box1(), + suggestions: state.positions + .map((PositionTitle position) => + SearchFieldListItem(position.title!, + item: position, + child: Padding( + padding: + const EdgeInsets.symmetric( + horizontal: 10), + child: ListTile( + title: Text( + position.title!, + overflow: + TextOverflow.visible, + ), + )))) + .toList(), + focusNode: positionFocusNode, + searchInputDecoration: + normalTextFieldStyle("Position *", "") + .copyWith( + suffixIcon: GestureDetector( + child: const Icon(Icons.arrow_drop_down), + onTap: () => positionFocusNode.unfocus(), + )), + onSuggestionTap: (position) { + setState(() { + selectedPosition = position.item; + positionFocusNode.unfocus(); + }); + }, + ////EMPTY WIDGET + emptyWidget: EmptyWidget( + title: "Add Position", + controller: addPositionController, + onpressed: () { + setState(() { + PositionTitle newAgencyPosition = PositionTitle( + id: null, + title: addPositionController.text + .toUpperCase()); + + state.positions + .insert(0, newAgencyPosition); + + addPositionController.text = ""; + Navigator.pop(context); + }); + }), + validator: (position) { + if (position!.isEmpty) { + return "This field is required"; + } + return null; + }, + ); + }), + const SizedBox( + height: 12, + ), + ////AGENCY + StatefulBuilder(builder: (context, setState) { + return Column( children: [ - ////POSITIONS - StatefulBuilder(builder: (context, setState) { - return SearchField( + SearchField( inputFormatters: [UpperCaseTextFormatter()], - itemHeight: 70, - suggestionsDecoration: box1(), - suggestions: state.positions - .map((PositionTitle position) => - SearchFieldListItem(position.title!, - item: position, - child: Padding( - padding: - const EdgeInsets.symmetric( - horizontal: 10), - child: ListTile( - title: Text( - position.title!, - overflow: - TextOverflow.visible, - ), - )))) + itemHeight: 100, + focusNode: agencyFocusNode, + suggestions: state.agencies + .map((Agency agency) => + SearchFieldListItem(agency.name!, + item: agency, + child: ListTile( + title: Text( + agency.name!, + overflow: + TextOverflow.visible, + ), + subtitle: Text(agency + .privateEntity == + true + ? "Private" + : agency.privateEntity == + false + ? "Government" + : ""), + ))) .toList(), - focusNode: positionFocusNode, searchInputDecoration: - normalTextFieldStyle("Position *", "") + normalTextFieldStyle("Agency *", "") .copyWith( suffixIcon: GestureDetector( - child: const Icon(Icons.arrow_drop_down), - onTap: () => positionFocusNode.unfocus(), + child: const Icon( + Icons.arrow_drop_down, + ), + onTap: () => agencyFocusNode.unfocus(), )), - onSuggestionTap: (position) { + ////SELETECTED + onSuggestionTap: (agency) { setState(() { - selectedPosition = position.item; - positionFocusNode.unfocus(); + selectedAgency = agency.item; + if (selectedAgency!.privateEntity == + null) { + showAgency = true; + showIsPrivateRadio = true; + } else { + showAgency = false; + showIsPrivateRadio = false; + } + agencyFocusNode.unfocus(); }); }, - ////EMPTY WIDGET - emptyWidget: EmptyWidget( - title: "Add Position", - controller: addPositionController, - onpressed: () { - setState(() { - PositionTitle newAgencyPosition = PositionTitle( - id: null, - title: addPositionController.text - .toUpperCase()); - - state.positions - .insert(0, newAgencyPosition); - - addPositionController.text = ""; - Navigator.pop(context); - }); - }), - validator: (position) { - if (position!.isEmpty) { + validator: (agency) { + if (agency!.isEmpty) { return "This field is required"; } return null; }, - ); - }), - const SizedBox( - height: 12, + emptyWidget: EmptyWidget( + controller: addAgencyController, + onpressed: () { + setState(() { + Agency newAgency = Agency( + id: null, + name: addAgencyController.text + .toUpperCase(), + category: null, + privateEntity: null); + state.agencies.insert(0, newAgency); + + addAgencyController.text = ""; + Navigator.pop(context); + }); + }, + title: "Add Agency")), + + SizedBox( + height: showAgency ? 12 : 0, ), - ////AGENCY - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - SearchField( - inputFormatters: [UpperCaseTextFormatter()], - itemHeight: 100, - focusNode: agencyFocusNode, - suggestions: state.agencies - .map((Agency agency) => - SearchFieldListItem(agency.name!, - item: agency, + ////SHOW CATEGORY AGENCY + SizedBox( + child: showAgency + ? SearchField( + focusNode: agencyCategoryFocusNode, + itemHeight: 70, + suggestions: state.agencyCategory + .map((Category category) => + SearchFieldListItem( + category.name!, + item: category, child: ListTile( title: Text( - agency.name!, - overflow: - TextOverflow.visible, - ), - subtitle: Text(agency - .privateEntity == - true - ? "Private" - : agency.privateEntity == - false - ? "Government" - : ""), + category.name!), + subtitle: Text(category + .industryClass! + .name!), ))) .toList(), - searchInputDecoration: - normalTextFieldStyle("Agency *", "") - .copyWith( - suffixIcon: GestureDetector( - child: const Icon( - Icons.arrow_drop_down, - ), - onTap: () => agencyFocusNode.unfocus(), - )), - ////SELETECTED - onSuggestionTap: (agency) { + emptyWidget: Container( + height: 100, + decoration: box1(), + child: const Center( + child: Text( + "No result found ...")), + ), + onSuggestionTap: (agencyCategory) { setState(() { - selectedAgency = agency.item; - if (selectedAgency!.privateEntity == - null) { - showAgency = true; - showIsPrivateRadio = true; - } else { - showAgency = false; - showIsPrivateRadio = false; - } - agencyFocusNode.unfocus(); + selectedCategoty = + agencyCategory.item; + agencyCategoryFocusNode.unfocus(); }); }, - validator: (agency) { - if (agency!.isEmpty) { + searchInputDecoration: + normalTextFieldStyle( + "Category *", "") + .copyWith( + suffixIcon: IconButton( + icon: const Icon( + Icons.arrow_drop_down), + onPressed: () { + agencyCategoryFocusNode.unfocus(); + }, + )), + validator: (value) { + if (value!.isEmpty) { return "This field is required"; } return null; }, - emptyWidget: EmptyWidget( - controller: addAgencyController, - onpressed: () { - setState(() { - Agency newAgency = Agency( - id: null, - name: addAgencyController.text - .toUpperCase(), - category: null, - privateEntity: null); - state.agencies.insert(0, newAgency); + ) + : const SizedBox(), + ), + SizedBox( + height: showIsPrivateRadio ? 12 : 0, + ), + ////PRVIATE SECTOR + SizedBox( + child: showIsPrivateRadio + ? FormBuilderSwitch( + initialValue: false, + title: + Text(isPrivate ? "YES" : "NO"), + decoration: normalTextFieldStyle( + "Private Entity?", + 'Private Entity?'), - addAgencyController.text = ""; - Navigator.pop(context); - }); - }, - title: "Add Agency")), + ////onvhange private sector + onChanged: (value) { + setState(() { + isPrivate = value!; + agencyCategoryFocusNode + .unfocus(); + }); + }, - SizedBox( - height: showAgency ? 12 : 0, - ), - ////SHOW CATEGORY AGENCY - SizedBox( - child: showAgency - ? SearchField( - focusNode: agencyCategoryFocusNode, - itemHeight: 70, - suggestions: state.agencyCategory - .map((Category category) => - SearchFieldListItem( - category.name!, - item: category, - child: ListTile( - title: Text( - category.name!), - subtitle: Text(category - .industryClass! - .name!), - ))) - .toList(), - emptyWidget: Container( - height: 100, - decoration: box1(), - child: const Center( - child: Text( - "No result found ...")), - ), - onSuggestionTap: (agencyCategory) { - setState(() { - selectedCategoty = - agencyCategory.item; - agencyCategoryFocusNode.unfocus(); - }); - }, - searchInputDecoration: - normalTextFieldStyle( - "Category *", "") - .copyWith( - suffixIcon: IconButton( - icon: const Icon( - Icons.arrow_drop_down), - onPressed: () { - agencyCategoryFocusNode.unfocus(); - }, - )), - validator: (value) { - if (value!.isEmpty) { - return "This field is required"; - } - return null; - }, - ) - : const SizedBox(), - ), - SizedBox( - height: showIsPrivateRadio ? 12 : 0, - ), - ////PRVIATE SECTOR - SizedBox( - child: showIsPrivateRadio - ? FormBuilderSwitch( - initialValue: false, - title: - Text(isPrivate ? "YES" : "NO"), - decoration: normalTextFieldStyle( - "Private Entity?", - 'Private Entity?'), - - ////onvhange private sector - onChanged: (value) { - setState(() { - isPrivate = value!; - agencyCategoryFocusNode - .unfocus(); - }); - }, - - name: 'isPrivate', - validator: FormBuilderValidators - .required(), - ) - : const SizedBox()), - const SizedBox( - height: 12, - ), - //// total hours - FormBuilderTextField( - validator: FormBuilderValidators.required( - errorText: "This Field is required"), - name: "total_hours", - keyboardType: TextInputType.number, - decoration: - normalTextFieldStyle("Total Hours*", "0"), - ), - const SizedBox( - height: 12, - ), - ////Currently Involved - StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - FormBuilderSwitch( - initialValue: currentlyInvolved, - activeColor: second, - onChanged: (value) { - setState(() { - currentlyInvolved = value!; - }); - }, - decoration: normalTextFieldStyle( - "Currently Involved?", - 'Graduated?'), - name: 'currently_involved', - title: Text( - currentlyInvolved ? "YES" : "NO"), - ), - const SizedBox( - height: 12, - ), - SizedBox( - width: screenWidth, - child: StatefulBuilder( - builder: (context, setState) { - return Row( - children: [ - //// FROM DATE - Flexible( - flex: 1, - child: DateTimePicker( - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - use24HourFormat: false, - icon: const Icon( - Icons.date_range), - controller: - fromDateController, - firstDate: DateTime(1990), - lastDate: DateTime(2100), - selectableDayPredicate: - (date) { - if (to != null && - to!.microsecondsSinceEpoch <= - date.microsecondsSinceEpoch) { - return false; - } - return true; - }, - onChanged: (value) { - setState(() { - from = DateTime.parse( - value); - }); - }, - initialDate: to == null - ? DateTime.now() - : to!.subtract( - const Duration( - days: 1)), - timeHintText: - "Date of Examination/Conferment", - decoration: - normalTextFieldStyle( - "From *", - "From *") - .copyWith( - prefixIcon: - const Icon( - Icons.date_range, - color: Colors.black87, - )), - initialValue: null, - )), - const SizedBox( - width: 12, - ), - //// TO DATE - Flexible( - flex: 1, - child: currentlyInvolved - ? TextFormField( - enabled: false, - initialValue: "PRESENT", - style: const TextStyle( - color: - Colors.black45), - decoration: - normalTextFieldStyle( - "", "") - .copyWith(), - ) - : DateTimePicker( - validator: - FormBuilderValidators - .required( - errorText: - "This field is required"), - controller: - toDateController, - selectableDayPredicate: - (date) { - if (from != null && - from!.microsecondsSinceEpoch >= - date.microsecondsSinceEpoch) { - return false; - } - return true; - }, - onChanged: (value) { - setState(() { - to = DateTime.parse( - value); - }); - }, - initialDate: from == - null - ? DateTime.now() - : from!.add( - const Duration( - days: 1)), - firstDate: - DateTime(1990), - lastDate: - DateTime(2100), - decoration: normalTextFieldStyle( - "To *", "To *") - .copyWith( - prefixIcon: - const Icon( - Icons - .date_range, - color: Colors - .black87, - ), - prefixText: - currentlyInvolved - ? "PRESENT" - : ""), - initialValue: null, - ), - ), - ], - ); - }), - ), - ], - ); - }), - ], - ); - }), + name: 'isPrivate', + validator: FormBuilderValidators + .required(), + ) + : const SizedBox()), const SizedBox( height: 12, ), - //// OVERSEAS + //// total hours + FormBuilderTextField( + validator: FormBuilderValidators.required( + errorText: "This Field is required"), + name: "total_hours", + keyboardType: TextInputType.number, + decoration: + normalTextFieldStyle("Total Hours*", "0"), + ), + const SizedBox( + height: 12, + ), + ////Currently Involved StatefulBuilder(builder: (context, setState) { return Column( children: [ FormBuilderSwitch( - initialValue: overseas, + initialValue: currentlyInvolved, activeColor: second, onChanged: (value) { setState(() { - overseas = value!; + currentlyInvolved = value!; }); }, decoration: normalTextFieldStyle( - "Overseas Address?", ''), - name: 'overseas', - title: Text(overseas ? "YES" : "NO"), + "Currently Involved?", + 'Graduated?'), + name: 'currently_involved', + title: Text( + currentlyInvolved ? "YES" : "NO"), + ), + const SizedBox( + height: 12, ), SizedBox( - height: overseas == true ? 12 : 0, - ), - SizedBox( - child: overseas == false - ? Column( - children: [ - const SizedBox( - height: 12, - ), - ////REGION DROPDOWN - FormBuilderDropdown( - autovalidateMode: AutovalidateMode - .onUserInteraction, + width: screenWidth, + child: StatefulBuilder( + builder: (context, setState) { + return Row( + children: [ + //// FROM DATE + Flexible( + flex: 1, + child: DateTimePicker( validator: FormBuilderValidators .required( errorText: "This field is required"), - onChanged: - (Region? region) async { - if (selectedRegion != region) { - setState(() { - provinceCall = true; - }); - selectedRegion = region; - getProvinces(); - } + use24HourFormat: false, + icon: const Icon( + Icons.date_range), + controller: + fromDateController, + firstDate: DateTime(1990), + lastDate: DateTime(2100), + // selectableDayPredicate: + // (date) { + // if (to != null && + // to!.microsecondsSinceEpoch >= + // date.microsecondsSinceEpoch) { + // return false; + // } + // return true; + // }, + onChanged: (value) { + setState(() { + from = DateTime.parse( + value); + }); }, + initialDate: to == null + ? DateTime.now() + : to!.subtract( + const Duration( + days: 1)), + timeHintText: + "Date of Examination/Conferment", + decoration: + normalTextFieldStyle( + "From *", + "From *") + .copyWith( + prefixIcon: + const Icon( + Icons.date_range, + color: Colors.black87, + )), initialValue: null, - decoration: normalTextFieldStyle( - "Region*", "Region"), - name: 'region', - items: state.regions.map< - DropdownMenuItem>( - (Region region) { - return DropdownMenuItem( - value: region, - child: Text( - region.description!)); - }).toList(), - ), - const SizedBox( - height: 12, - ), - //// PROVINCE DROPDOWN - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.transparent, - inAsyncCall: provinceCall, - child: DropdownButtonFormField< - Province?>( - autovalidateMode: - AutovalidateMode - .onUserInteraction, - validator: (value) => - value == null - ? 'required' - : null, - isExpanded: true, - value: selectedProvince, - onChanged: - (Province? province) { - if (selectedProvince != - province) { - setState(() { - cityCall = true; - }); - selectedProvince = - province; - getCities(); - } - }, - items: provinces == null - ? [] - : provinces!.map< - DropdownMenuItem< - Province>>( - (Province - province) { - return DropdownMenuItem( - value: province, - child: - FittedBox( - child: Text( - province - .description!), - )); - }).toList(), - decoration: - normalTextFieldStyle( - "Province*", - "Province")), - ), - ), - ////CITY MUNICIPALITY - SizedBox( - height: 60, - child: ModalProgressHUD( - color: Colors.white, - inAsyncCall: cityCall, - child: DropdownButtonFormField< - CityMunicipality>( - validator: FormBuilderValidators - .required( - errorText: - "This field is required"), - isExpanded: true, - onChanged: - (CityMunicipality? city) { - if (selectedMunicipality != - city) { - selectedMunicipality = - city; - } - }, + )), + const SizedBox( + width: 12, + ), + //// TO DATE + Flexible( + flex: 1, + child: currentlyInvolved + ? TextFormField( + enabled: false, + initialValue: "PRESENT", + style: const TextStyle( + color: + Colors.black45), decoration: normalTextFieldStyle( - "Municipality*", - "Municipality"), - value: selectedMunicipality, - items: citymuns == null - ? [] - : citymuns!.map< - DropdownMenuItem< - CityMunicipality>>( - (CityMunicipality c) { - return DropdownMenuItem( - value: c, - child: Text(c - .description!)); - }).toList(), + "", "") + .copyWith(), + ) + : DateTimePicker( + validator: + FormBuilderValidators + .required( + errorText: + "This field is required"), + controller: + toDateController, + // selectableDayPredicate: + // (date) { + // if (from != null && + // from!.microsecondsSinceEpoch > + // date.microsecondsSinceEpoch) { + // return false; + // } + // return true; + // }, + onChanged: (value) { + setState(() { + to = DateTime.parse( + value); + }); + }, + initialDate: from == + null + ? DateTime.now() + : from!.add( + const Duration( + days: 1)), + firstDate: + DateTime(1990), + lastDate: + DateTime(2100), + decoration: normalTextFieldStyle( + "To *", "To *") + .copyWith( + prefixIcon: + const Icon( + Icons + .date_range, + color: Colors + .black87, + ), + prefixText: + currentlyInvolved + ? "PRESENT" + : ""), + initialValue: null, ), - ), - ), - ], - ) - //// COUNTRY DROPDOWN - : SizedBox( - height: 60, - child: FormBuilderDropdown( - initialValue: null, - validator: - FormBuilderValidators.required( - errorText: - "This field is required"), - items: state.countries - .map>( - (Country country) { - return DropdownMenuItem( - value: country, - child: FittedBox( - child: - Text(country.name!))); - }).toList(), - name: 'country', - decoration: normalTextFieldStyle( - "Country*", "Country"), - onChanged: (Country? value) { - selectedCountry = value; - }, - ), ), + ], + ); + }), ), ], ); }), ], - ), + ); + }), + const SizedBox( + height: 12, ), - SizedBox( + //// OVERSEAS + StatefulBuilder(builder: (context, setState) { + return Column( + children: [ + FormBuilderSwitch( + initialValue: overseas, + activeColor: second, + onChanged: (value) { + setState(() { + overseas = value!; + }); + }, + decoration: normalTextFieldStyle( + "Overseas Address?", ''), + name: 'overseas', + title: Text(overseas ? "YES" : "NO"), + ), + SizedBox( + height: overseas == true ? 12 : 0, + ), + SizedBox( + child: overseas == false + ? Column( + children: [ + const SizedBox( + height: 12, + ), + ////REGION DROPDOWN + FormBuilderDropdown( + autovalidateMode: AutovalidateMode + .onUserInteraction, + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + onChanged: + (Region? region) async { + if (selectedRegion != region) { + setState(() { + provinceCall = true; + }); + selectedRegion = region; + getProvinces(); + } + }, + initialValue: null, + decoration: normalTextFieldStyle( + "Region*", "Region"), + name: 'region', + items: state.regions.map< + DropdownMenuItem>( + (Region region) { + return DropdownMenuItem( + value: region, + child: Text( + region.description!)); + }).toList(), + ), + const SizedBox( + height: 12, + ), + //// PROVINCE DROPDOWN + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.transparent, + inAsyncCall: provinceCall, + child: DropdownButtonFormField< + Province?>( + autovalidateMode: + AutovalidateMode + .onUserInteraction, + validator: (value) => + value == null + ? 'required' + : null, + isExpanded: true, + value: selectedProvince, + onChanged: + (Province? province) { + if (selectedProvince != + province) { + setState(() { + cityCall = true; + }); + selectedProvince = + province; + getCities(); + } + }, + items: provinces == null + ? [] + : provinces!.map< + DropdownMenuItem< + Province>>( + (Province + province) { + return DropdownMenuItem( + value: province, + child: + FittedBox( + child: Text( + province + .description!), + )); + }).toList(), + decoration: + normalTextFieldStyle( + "Province*", + "Province")), + ), + ), + ////CITY MUNICIPALITY + SizedBox( + height: 60, + child: ModalProgressHUD( + color: Colors.white, + inAsyncCall: cityCall, + child: DropdownButtonFormField< + CityMunicipality>( + validator: FormBuilderValidators + .required( + errorText: + "This field is required"), + isExpanded: true, + onChanged: + (CityMunicipality? city) { + if (selectedMunicipality != + city) { + selectedMunicipality = + city; + } + }, + decoration: + normalTextFieldStyle( + "Municipality*", + "Municipality"), + value: selectedMunicipality, + items: citymuns == null + ? [] + : citymuns!.map< + DropdownMenuItem< + CityMunicipality>>( + (CityMunicipality c) { + return DropdownMenuItem( + value: c, + child: Text(c + .description!)); + }).toList(), + ), + ), + ), + ], + ) + //// COUNTRY DROPDOWN + : SizedBox( + height: 60, + child: FormBuilderDropdown( + initialValue: null, + validator: + FormBuilderValidators.required( + errorText: + "This field is required"), + items: state.countries + .map>( + (Country country) { + return DropdownMenuItem( + value: country, + child: FittedBox( + child: + Text(country.name!))); + }).toList(), + name: 'country', + decoration: normalTextFieldStyle( + "Country*", "Country"), + onChanged: (Country? value) { + selectedCountry = value; + }, + ), + ), + ), + ], + ); + }), + const SizedBox(height: 16,), + SizedBox( width: double.infinity, height: 60, child: ElevatedButton( diff --git a/lib/screens/profile/components/voluntary_works/edit_modal.dart b/lib/screens/profile/components/voluntary_works/edit_modal.dart index 520a478..29b420c 100644 --- a/lib/screens/profile/components/voluntary_works/edit_modal.dart +++ b/lib/screens/profile/components/voluntary_works/edit_modal.dart @@ -156,6 +156,7 @@ class _EditVoluntaryWorkScreenState extends State { onSuggestionTap: (position) { setState(() { selectedPosition = position.item; + print(selectedPosition!.title); positionFocusNode.unfocus(); }); }, @@ -193,6 +194,8 @@ class _EditVoluntaryWorkScreenState extends State { return Column( children: [ SearchField( + + enabled: false, inputFormatters: [ UpperCaseTextFormatter() ], @@ -222,6 +225,8 @@ class _EditVoluntaryWorkScreenState extends State { searchInputDecoration: normalTextFieldStyle("Agency *", "") .copyWith( + filled: true, + fillColor: Colors.grey.shade300, suffixIcon: GestureDetector( child: const Icon( Icons.arrow_drop_down, @@ -267,7 +272,10 @@ class _EditVoluntaryWorkScreenState extends State { }); }, title: "Add Agency")), - + Padding( + padding: const EdgeInsets.all(8.0), + child: Text("You cannot change agency on update mode",style: Theme.of(context).textTheme.bodySmall,), + ), SizedBox( height: showAgency ? 12 : 0, ), @@ -424,15 +432,7 @@ class _EditVoluntaryWorkScreenState extends State { Icons.date_range, color: Colors.black87, )), - selectableDayPredicate: - (date) { - if (to != null && - to!.microsecondsSinceEpoch <= - date.microsecondsSinceEpoch) { - return false; - } - return true; - }, + onChanged: (value) { setState(() { from = DateTime.parse( @@ -475,15 +475,7 @@ class _EditVoluntaryWorkScreenState extends State { DateTime(1990), lastDate: DateTime(2100), - selectableDayPredicate: - (date) { - if (from != null && - from!.microsecondsSinceEpoch >= - date.microsecondsSinceEpoch) { - return false; - } - return true; - }, + onChanged: (value) { setState(() { to = DateTime.parse( @@ -522,6 +514,7 @@ class _EditVoluntaryWorkScreenState extends State { ], ); }), + const SizedBox( height: 12, ), diff --git a/lib/screens/profile/components/work_history/add_modal.dart b/lib/screens/profile/components/work_history/add_modal.dart index 75b0aab..5c353b4 100644 --- a/lib/screens/profile/components/work_history/add_modal.dart +++ b/lib/screens/profile/components/work_history/add_modal.dart @@ -197,6 +197,7 @@ class _AddWorkHistoryScreenState extends State { ////AGENCY StatefulBuilder(builder: (context, setState) { return Column( + mainAxisAlignment: MainAxisAlignment.start, children: [ SearchField( inputFormatters: [UpperCaseTextFormatter()], @@ -407,6 +408,7 @@ class _AddWorkHistoryScreenState extends State { SizedBox( child: showSalaryGradeAndSalaryStep ? Column( + mainAxisAlignment: MainAxisAlignment.start, children: [ Row( children: [ @@ -481,20 +483,7 @@ class _AddWorkHistoryScreenState extends State { ], ); }), - const SizedBox( - height: 12, - ), - //// NAME OF OFFICE UNIT - FormBuilderTextField( - onChanged: (value){ - sOffice = value; - }, - validator: FormBuilderValidators.required( - errorText: "This field is required"), - name: 'office', - decoration: normalTextFieldStyle( - "Name of Office/Unit", "Name of Office/Unit"), - ), + const SizedBox( height: 12, ), @@ -518,7 +507,10 @@ class _AddWorkHistoryScreenState extends State { const SizedBox( height: 12, ), - const Text("Immediate SuperVisor"), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text("Immediate SuperVisor",textAlign: TextAlign.start, style: Theme.of(context).textTheme.titleMedium,), + ), const SizedBox( height: 12, ), @@ -557,12 +549,26 @@ class _AddWorkHistoryScreenState extends State { decoration: normalTextFieldStyle("Last name", "Last Name"), ), - + const SizedBox( + height: 12, + ), + //// NAME OF OFFICE UNIT + FormBuilderTextField( + onChanged: (value){ + sOffice = value; + }, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: 'office', + decoration: normalTextFieldStyle( + "Name of Office/Unit", "Name of Office/Unit"), + ), const SizedBox( height: 12, ), StatefulBuilder(builder: (context, setState) { return Column( + mainAxisAlignment: MainAxisAlignment.start, children: [ ////CURRENTLY EMPLOYED FormBuilderSwitch( @@ -690,12 +696,18 @@ class _AddWorkHistoryScreenState extends State { ], ); }), - - const Text("Work Experience"), + const SizedBox( + height: 8, + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text("Work Experience",style: Theme.of(context).textTheme.titleMedium,), + ), const SizedBox( height: 8, ), FormBuilderTextField( + maxLines: 3, onChanged: (value){ accomplishment = value; }, @@ -709,6 +721,7 @@ class _AddWorkHistoryScreenState extends State { height: 12, ), FormBuilderTextField( + maxLines: 3, onChanged: (value){ duties = value; }, @@ -733,8 +746,7 @@ class _AddWorkHistoryScreenState extends State { style: mainBtnStyle(primary, Colors.transparent, second), onPressed: () { - print(salaryGrade); - print(salaryGradeStep); + if (_formKey.currentState!.validate()) { final progress = ProgressHUD.of(context); diff --git a/lib/screens/profile/components/work_history/edit_modal.dart b/lib/screens/profile/components/work_history/edit_modal.dart index 5c0f633..5e8eafd 100644 --- a/lib/screens/profile/components/work_history/edit_modal.dart +++ b/lib/screens/profile/components/work_history/edit_modal.dart @@ -48,6 +48,12 @@ class _EditWorkHistoryScreenState extends State { String? salary; String? salaryGrade; String? salaryGradeStep; + String? accomplishments; + String? duties; + String? sFname; + String? sMname; + String? sLname; + String? sOffice; //show agency category is a variable to show adding of agency category if you add agency manually bool showAgencyCategory = false; //showSalaryGadeAndSalaryStep is a variable that will show salary @@ -91,6 +97,12 @@ class _EditWorkHistoryScreenState extends State { currentlyEmployed = state.workHistory.toDate == null ? true : false; from = state.workHistory.fromDate; to = state.workHistory.toDate; + accomplishments = state.workHistory.accomplishment == null?null:state.workHistory.accomplishment!.first.accomplishment!; + duties = state.workHistory.actualDuties == null? null:state.workHistory.actualDuties!.first.description; + sFname = state.workHistory.supervisor?.firstname; + sMname = state.workHistory.supervisor?.middlename; + sLname = state.workHistory.supervisor?.lastname; + sOffice = state.workHistory.supervisor?.stationName; return FormBuilder( key: _formKey, child: SizedBox( @@ -146,10 +158,11 @@ class _EditWorkHistoryScreenState extends State { controller: addPositionController, onpressed: () { setState(() { - PositionTitle newAgencyPosition = PositionTitle( - id: null, - title: addPositionController.text - .toUpperCase()); + PositionTitle newAgencyPosition = + PositionTitle( + id: null, + title: addPositionController.text + .toUpperCase()); state.agencyPositions .insert(0, newAgencyPosition); selectedPosition = newAgencyPosition; @@ -209,8 +222,10 @@ class _EditWorkHistoryScreenState extends State { ////AGENCY StatefulBuilder(builder: (context, setState) { return Column( + mainAxisAlignment: MainAxisAlignment.start, children: [ SearchField( + enabled: false, inputFormatters: [UpperCaseTextFormatter()], controller: oldAgencyController, itemHeight: 100, @@ -288,6 +303,7 @@ class _EditWorkHistoryScreenState extends State { }); }, title: "Add Agency")), + Text("You cannot change agency on update mode",textAlign: TextAlign.start,style: Theme.of(context).textTheme.bodySmall,), SizedBox( height: showAgencyCategory ? 12 : 0, @@ -476,6 +492,7 @@ class _EditWorkHistoryScreenState extends State { ], ); }), + const SizedBox( height: 12, ), @@ -494,10 +511,71 @@ class _EditWorkHistoryScreenState extends State { normalTextFieldStyle("Monthly Salary *", "") .copyWith(prefix: const Text("₱ ")), ), - + Padding( + padding: const EdgeInsets.all(8.0), + child: Text("Immediate SuperVisor",style: Theme.of(context).textTheme.titleMedium,), + ), const SizedBox( height: 12, ), + ////IMMEDIATE SUPERVISOR + FormBuilderTextField( + initialValue: sFname, + onChanged: (value) { + sFname = value; + }, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: 'supervisor_firstname', + decoration: normalTextFieldStyle( + "First name", "First Name"), + ), + const SizedBox( + height: 12, + ), + FormBuilderTextField( + initialValue: sMname, + onChanged: (value) { + sMname = value; + }, + name: 'supervisor_middlename', + decoration: normalTextFieldStyle( + "Middle name", "Middle Name"), + ), + const SizedBox( + height: 12, + ), + FormBuilderTextField( + initialValue: sLname, + onChanged: (value) { + sLname = value; + }, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: 'supervisor_lastname', + decoration: + normalTextFieldStyle("Last name", "Last Name"), + ), + + const SizedBox( + height: 12, + ), + //// NAME OF OFFICE UNIT + FormBuilderTextField( + initialValue: + sOffice, + onChanged: (value) { + sOffice = value; + }, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: 'office', + decoration: normalTextFieldStyle( + "Name of Office/Unit", "Name of Office/Unit"), + ), + const SizedBox( + height: 12, + ), StatefulBuilder(builder: (context, setState) { return Column( children: [ @@ -640,12 +718,52 @@ class _EditWorkHistoryScreenState extends State { ); }), ), + const SizedBox( + height: 8, + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text("Work Experience",textAlign: TextAlign.start, style: Theme.of(context).textTheme.titleMedium,), + ), + const SizedBox( + height: 8, + ), + FormBuilderTextField( + initialValue: accomplishments, + maxLines: 3, + onChanged: (value){ + accomplishments = value; + }, + name: "accomplishment", + decoration: normalTextFieldStyle( + "List of Accomplishment and Contribution", + "", + ), + ), + const SizedBox( + height: 12, + ), + FormBuilderTextField( + initialValue: duties, + maxLines: 3, + onChanged: (value){ + duties = value; + }, + validator: FormBuilderValidators.required( + errorText: "This field is required"), + name: "summary", + decoration: normalTextFieldStyle( + "Summary of Actual Duties", + "", + ), + ), ], ); }), ], ), ), + const SizedBox(height: 25,), ////SUBMIT BUTTON SizedBox( width: double.infinity, @@ -654,50 +772,55 @@ class _EditWorkHistoryScreenState extends State { style: mainBtnStyle(primary, Colors.transparent, second), onPressed: () { - // if (_formKey.currentState!.saveAndValidate()) { - // final progress = ProgressHUD.of(context); - // progress!.showWithText("Loading..."); - // salary = _formKey.currentState!.value['salary']; - // selectedPosition ??= state.workHistory.position; - // salaryGrade = - // _formKey.currentState!.value['salary_grade']; - // salaryGradeStep = - // _formKey.currentState!.value['salary_step']; - // selectedAgency ??= state.workHistory.agency; + if (_formKey.currentState!.saveAndValidate()) { + final progress = ProgressHUD.of(context); + progress!.showWithText("Loading..."); + salary = _formKey.currentState!.value['salary']; + selectedPosition ??= state.workHistory.position; + salaryGrade = + _formKey.currentState!.value['salary_grade']; + salaryGradeStep = + _formKey.currentState!.value['salary_step']; + selectedAgency ??= state.workHistory.agency; - // selectedStatus ??= AppoinemtStatus( - // value: state.workHistory.statusAppointment!, - // label: state.workHistory.statusAppointment!); - // WorkHistory newWorkHistory = WorkHistory( - // id: state.workHistory.id, - // position: selectedPosition, - // agency: selectedAgency, - // fromDate: fromDateController.text.isEmpty - // ? null - // : DateTime.parse(fromDateController.text), - // toDate: toDateController.text.isEmpty || - // toDateController.text.toUpperCase() == - // "PRESENT" || - // toDateController.text.toLowerCase() == - // 'null' - // ? null - // : DateTime.parse(toDateController.text), - // monthlySalary: double.parse(salary!), - // appointmentStatus: selectedStatus!.value, - // salaryGrade: salaryGrade == null - // ? null - // : int.parse(salaryGrade!), - // sgStep: salaryGradeStep == null - // ? null - // : int.parse(salaryGradeStep!), - // ); - // context.read().add( - // UpdateWorkHistory( - // oldWorkHistory: state.workHistory, - // profileId: widget.profileId.toString(), - // token: widget.token, - // workHistory: newWorkHistory)); - // } + selectedStatus ??= AppoinemtStatus( + value: state.workHistory.statusAppointment!, + label: state.workHistory.statusAppointment!); + WorkHistory newWorkHistory = WorkHistory( + accomplishment: accomplishments == null?null: [Accomplishment(id: state.workHistory.accomplishment!.first.id, workExperienceId: state.workHistory.id, accomplishment: accomplishments)], + actualDuties: duties == null? null: [ActualDuty(id: state.workHistory.actualDuties!.first.id, workExperienceId: state.workHistory.id, description: duties!)], + agencydepid: state.workHistory.agency!.id, + supervisor: Supervisor(agencyId: state.workHistory.agencydepid,id: state.workHistory.supervisor!.id,firstname: sFname,middlename: sMname,lastname: sLname,stationName: sOffice), + id: state.workHistory.id, + position: selectedPosition, + agency: selectedAgency, + fromDate: fromDateController.text.isEmpty + ? null + : DateTime.parse(fromDateController.text), + toDate: toDateController.text.isEmpty || + toDateController.text.toUpperCase() == + "PRESENT" || + toDateController.text.toLowerCase() == + 'null' + ? null + : DateTime.parse(toDateController.text), + monthlysalary: double.parse(salary!), + statusAppointment: selectedStatus!.value, + salarygrade: salaryGrade == null + ? null + : int.parse(salaryGrade!), + sgstep: salaryGradeStep == null + ? null + : int.parse(salaryGradeStep!), + ); + context.read().add( + UpdateWorkHistory( + + isPrivate: state.workHistory.agency!.privateEntity!, + profileId: widget.profileId, + token: widget.token, + workHistory: newWorkHistory)); + } }, child: const Text(submit)), ), diff --git a/lib/screens/profile/components/work_history_screen.dart b/lib/screens/profile/components/work_history_screen.dart index 9255c4b..63822ad 100644 --- a/lib/screens/profile/components/work_history_screen.dart +++ b/lib/screens/profile/components/work_history_screen.dart @@ -322,20 +322,20 @@ class WorkHistoryScreen extends StatelessWidget { } if (value == 1) { ////edit eligibilty-= = = = = = = = =>> - final progress = - ProgressHUD.of( - context); - progress!.showWithText( - "Loading..."); - WorkHistory workHistory = - state.workExperiences[ - index]; - context - .read< - WorkHistoryBloc>() - .add(ShowEditWorkHistoryForm( - workHistory: - workHistory)); + // final progress = + // ProgressHUD.of( + // context); + // progress!.showWithText( + // "Loading..."); + // WorkHistory workHistory = + // state.workExperiences[ + // index]; + // context + // .read< + // WorkHistoryBloc>() + // .add(ShowEditWorkHistoryForm( + // workHistory: + // workHistory)); } ////Attachment if (value == 3) { diff --git a/lib/screens/profile/shared/view_attachment.dart b/lib/screens/profile/shared/view_attachment.dart deleted file mode 100644 index 54bd51d..0000000 --- a/lib/screens/profile/shared/view_attachment.dart +++ /dev/null @@ -1,62 +0,0 @@ -import 'package:cached_network_image/cached_network_image.dart'; -import 'package:flutter/material.dart'; -import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart'; -import 'package:unit2/theme-data.dart/colors.dart'; -import 'package:unit2/widgets/error_state.dart'; - -import '../../../utils/urls.dart'; - -class ImageAttachment extends StatefulWidget { - final String imgUrl; - const ImageAttachment({super.key, required this.imgUrl}); - - @override - State createState() => _ImageAttachmentState(); -} - -class _ImageAttachmentState extends State { - @override - Widget build(BuildContext context) { - bool isPDF = widget.imgUrl[widget.imgUrl.length - 1] == 'f' ? true : false; - - return Scaffold( - floatingActionButton: FloatingActionButton( - onPressed: () {}, - child: const Icon(Icons.file_download), - ), - appBar: AppBar( - title: const Text("Attachment"), - centerTitle: true, - actions: [ - IconButton(onPressed: () {}, icon: const Icon(Icons.share)), - ], - ), - body: isPDF - ? SfPdfViewer.network( - '${Url.instance.prefixHost()}://${Url.instance.host()}${widget.imgUrl}',onDocumentLoadFailed: (details) { - Center(child: Text(details.description),); - },) - : Center( - child: CachedNetworkImage( - progressIndicatorBuilder: (context, url, progress) { - return const SizedBox( - height: 100, - width: 100, - child: CircularProgressIndicator(color: primary,)); - }, - - imageBuilder: (context, imageProvider) => Container( - decoration: BoxDecoration( - image: DecorationImage( - image: imageProvider, fit: BoxFit.fill)), - ), - imageUrl: - '${Url.instance.prefixHost()}://${Url.instance.host()}${widget.imgUrl}', - width: double.infinity, - height: 220, - fit: BoxFit.cover, - ), - ), - ); - } -} diff --git a/lib/sevices/profile/education_services.dart b/lib/sevices/profile/education_services.dart index 9ed67ed..05a86f5 100644 --- a/lib/sevices/profile/education_services.dart +++ b/lib/sevices/profile/education_services.dart @@ -68,7 +68,7 @@ class EducationService { try { http.Response response = await Request.instance .postRequest(path: path, param: {}, body: body, headers: headers); - if (response.statusCode == 2011) { + if (response.statusCode == 201) { Map data = jsonDecode(response.body); statusResponse = data; } else { @@ -150,7 +150,7 @@ class EducationService { http.Response response = await Request.instance.deleteRequest( path: path, headers: headers, body: body, param: params); - if (response.statusCode == 2001) { + if (response.statusCode == 200) { Map data = jsonDecode(response.body); success = data['success']; } else { diff --git a/lib/sevices/profile/profile_other_info.dart b/lib/sevices/profile/profile_other_info.dart index 5561b1c..8945835 100644 --- a/lib/sevices/profile/profile_other_info.dart +++ b/lib/sevices/profile/profile_other_info.dart @@ -21,7 +21,7 @@ class ProfileOtherInfoServices{ try { http.Response response = await Request.instance .getRequest(path: path, headers: headers, param: {}); - if (response.statusCode == 20012) { + if (response.statusCode == 200) { Map data = jsonDecode(response.body); if (data['data'] != null) { data['data'].forEach((var e) { diff --git a/lib/sevices/profile/work_history_services.dart b/lib/sevices/profile/work_history_services.dart index e46b08d..d227099 100644 --- a/lib/sevices/profile/work_history_services.dart +++ b/lib/sevices/profile/work_history_services.dart @@ -40,7 +40,6 @@ class WorkHistoryService { return workExperiences; } - ////delete workhistory Future delete( {required int profileId, @@ -69,10 +68,10 @@ class WorkHistoryService { http.Response response = await Request.instance.deleteRequest( path: path, headers: headers, body: body, param: params); - if(response.statusCode == 200){ + if (response.statusCode == 200) { Map data = jsonDecode(response.body); success = data['success']; - }else{ + } else { success = false; } } catch (e) { @@ -81,7 +80,6 @@ class WorkHistoryService { return success!; } - ////edit work history // Future> update({required WorkHistory oldWorkHistory, required WorkHistory newWorkHistory, required String token, required String profileId})async{ // Map? statusResponse={}; @@ -108,8 +106,7 @@ class WorkHistoryService { // "oldPosId":oldWorkHistory.position!.id, // "_oldAgencyId":oldWorkHistory.agency!.id, // "oldFromDate":oldWorkHistory.fromDate?.toString(), - // }; - + // }; // try{ // http.Response response = await Request.instance.putRequest(path: path, headers: headers, body: body, param: {}); @@ -125,74 +122,129 @@ class WorkHistoryService { // } // } - - ////Add work history - Future>add({required WorkHistory workHistory, required String token, required int profileId , required bool isPrivate,required String? accomplishment, required String? actualDuties})async{ - String authtoken = "Token $token"; + Future> add( + {required WorkHistory workHistory, + required String token, + required int profileId, + required bool isPrivate, + required String? accomplishment, + required String? actualDuties}) async { + String authtoken = "Token $token"; String path = '${Url.instance.workhistory()}$profileId/'; - Map headers = { + Map headers = { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': authtoken }; - Map body = {}; - Map statusResponse = {}; - String fromDate = DateFormat('yyyy-MM-dd').format(workHistory.fromDate!); - String? toDate; - if(workHistory.toDate != null){ - toDate = DateFormat('yyyy-MM-dd').format(workHistory.toDate!); - } - if(workHistory.toDate == null){ - body = { -"a_category_id ": workHistory.agency?.category?.id == null? "":workHistory.agency!.category!.id.toString(), - "a_name" : workHistory.agency?.name == null? "":workHistory.agency!.name!, - " a_private_entity ": workHistory.agency?.privateEntity == null?"":workHistory.agency!.privateEntity.toString(), - "accomplishment" : accomplishment??"", - "actual_duties ": actualDuties!, - "agency_id" : workHistory.agency?.id == null?"": workHistory.agency!.id.toString() , - "from_date" : fromDate, - "monthly_salary" : workHistory.monthlysalary == null? "": workHistory.monthlysalary.toString(), - "position_id" : workHistory.position?.id == null? "": workHistory.position!.id.toString(), - "position_name" : workHistory.position?.title == null? "":workHistory.position!.title!, - "s_fname" : workHistory.supervisor?.firstname == null?"":workHistory.supervisor!.firstname!, - "s_lname" : workHistory.supervisor?.lastname == null? "":workHistory.supervisor!.lastname!, - "s_mname" : workHistory.supervisor?.middlename == null?"":workHistory.supervisor!.middlename!, - "s_office" : workHistory.supervisor?.stationName == null?"":workHistory.supervisor!.stationName!, - "salary_grade" : workHistory.salarygrade == null? "":workHistory.salarygrade.toString(), - "sg_step" : workHistory.sgstep == null?"":workHistory.sgstep.toString() , - 'status_appointment' : workHistory.statusAppointment??"", - }; - }else{ - body = { -"a_category_id ": workHistory.agency?.category?.id == null? "":workHistory.agency!.category!.id.toString(), - "a_name" : workHistory.agency?.name == null? "":workHistory.agency!.name!, - " a_private_entity ": workHistory.agency?.privateEntity == null?"":workHistory.agency!.privateEntity.toString(), - "accomplishment" : accomplishment??"", - "actual_duties ": actualDuties!, - "agency_id" : workHistory.agency?.id == null?"": workHistory.agency!.id.toString() , - "from_date" : workHistory.fromDate == null? "2018-10-04":"2018-06-04", - "monthly_salary" : workHistory.monthlysalary == null? "": workHistory.monthlysalary.toString(), - "position_id" : workHistory.position?.id == null? "": workHistory.position!.id.toString(), - "position_name" : workHistory.position?.title == null? "":workHistory.position!.title!, - "s_fname" : workHistory.supervisor?.firstname == null?"":workHistory.supervisor!.firstname!, - "s_lname" : workHistory.supervisor?.lastname == null? "":workHistory.supervisor!.lastname!, - "s_mname" : workHistory.supervisor?.middlename == null?"":workHistory.supervisor!.middlename!, - "s_office" : workHistory.supervisor?.stationName == null?"":workHistory.supervisor!.stationName!, - "salary_grade" : workHistory.salarygrade == null? "":workHistory.salarygrade.toString(), - "sg_step" : workHistory.sgstep == null?"":workHistory.sgstep.toString() , - 'status_appointment' : workHistory.statusAppointment??"", - "to_date" : toDate!, - }; + Map body = {}; + Map statusResponse = {}; + String fromDate = DateFormat('yyyy-MM-dd').format(workHistory.fromDate!); + String? toDate = workHistory.toDate == null + ? null + : DateFormat('yyyy-MM-dd').format(workHistory.toDate!); + + if (workHistory.toDate == null) { + body = { + "a_category_id ": workHistory.agency?.category?.id == null + ? "" + : workHistory.agency!.category!.id.toString(), + "a_name": + workHistory.agency?.name == null ? "" : workHistory.agency!.name!, + " a_private_entity ": workHistory.agency?.privateEntity == null + ? "" + : workHistory.agency!.privateEntity.toString(), + "accomplishment": accomplishment ?? "", + "actual_duties ": actualDuties!, + "agency_id": workHistory.agency?.id == null + ? "" + : workHistory.agency!.id.toString(), + "from_date": fromDate, + "monthly_salary": workHistory.monthlysalary == null + ? "" + : workHistory.monthlysalary.toString(), + "position_id": workHistory.position?.id == null + ? "" + : workHistory.position!.id.toString(), + "position_name": workHistory.position?.title == null + ? "" + : workHistory.position!.title!, + "s_fname": workHistory.supervisor?.firstname == null + ? "" + : workHistory.supervisor!.firstname!, + "s_lname": workHistory.supervisor?.lastname == null + ? "" + : workHistory.supervisor!.lastname!, + "s_mname": workHistory.supervisor?.middlename == null + ? "" + : workHistory.supervisor!.middlename!, + "s_office": workHistory.supervisor?.stationName == null + ? "" + : workHistory.supervisor!.stationName!, + "salary_grade": workHistory.salarygrade == null + ? "" + : workHistory.salarygrade.toString(), + "sg_step": + workHistory.sgstep == null ? "" : workHistory.sgstep.toString(), + 'status_appointment': workHistory.statusAppointment ?? "", + }; + } else { + body = { + "a_category_id ": workHistory.agency?.category?.id == null + ? "" + : workHistory.agency!.category!.id.toString(), + "a_name": + workHistory.agency?.name == null ? "" : workHistory.agency!.name!, + " a_private_entity ": workHistory.agency?.privateEntity == null + ? "" + : workHistory.agency!.privateEntity.toString(), + "accomplishment": accomplishment ?? "", + "actual_duties ": actualDuties!, + "agency_id": workHistory.agency?.id == null + ? "" + : workHistory.agency!.id.toString(), + "from_date": fromDate, + "monthly_salary": workHistory.monthlysalary == null + ? "" + : workHistory.monthlysalary.toString(), + "position_id": workHistory.position?.id == null + ? "" + : workHistory.position!.id.toString(), + "position_name": workHistory.position?.title == null + ? "" + : workHistory.position!.title!, + "s_fname": workHistory.supervisor?.firstname == null + ? "" + : workHistory.supervisor!.firstname!, + "s_lname": workHistory.supervisor?.lastname == null + ? "" + : workHistory.supervisor!.lastname!, + "s_mname": workHistory.supervisor?.middlename == null + ? "" + : workHistory.supervisor!.middlename!, + "s_office": workHistory.supervisor?.stationName == null + ? "" + : workHistory.supervisor!.stationName!, + "salary_grade": workHistory.salarygrade == null + ? "" + : workHistory.salarygrade.toString(), + "sg_step": + workHistory.sgstep == null ? "" : workHistory.sgstep.toString(), + 'status_appointment': workHistory.statusAppointment ?? "", + "to_date": toDate!, + }; } - - var request = http.MultipartRequest('POST',Uri.parse('${Url.instance.prefixHost()}://${Url.instance.host()}$path')); - request.fields.addAll(body); - request.headers.addAll(headers); + + var request = http.MultipartRequest( + 'POST', + Uri.parse( + '${Url.instance.prefixHost()}://${Url.instance.host()}$path')); + request.fields.addAll(body); + request.headers.addAll(headers); try { http.StreamedResponse response = await request.send(); - final steamResponse = await response.stream.bytesToString(); + final steamResponse = await response.stream.bytesToString(); Map data = jsonDecode(steamResponse); - if (response.statusCode == 201) { + if (response.statusCode == 201) { statusResponse = data; } else { String message = data['response']['details']; @@ -204,91 +256,151 @@ class WorkHistoryService { } catch (e) { throw e.toString(); } -return statusResponse; + return statusResponse; } -Future>update({required WorkHistory workHistory, required String token, required int profileId , required bool isPrivate,required String? accomplishment, required String? actualDuties})async{ - String authtoken = "Token $token"; + Future> update( + {required WorkHistory workHistory, + required String token, + required int profileId, + required bool isPrivate}) async { + String authtoken = "Token $token"; String path = '${Url.instance.workhistory()}$profileId/'; - Map headers = { + Map headers = { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': authtoken }; - Map body = {}; - Map statusResponse = {}; - String fromDate = DateFormat('yyyy-MM-dd').format(workHistory.fromDate!); - String? toDate; - if(workHistory.toDate != null){ - toDate = DateFormat('yyyy-MM-dd').format(workHistory.toDate!); - } - if(workHistory.toDate == null){ - body = { -"a_category_id ": workHistory.agency?.category?.id == null? "":workHistory.agency!.category!.id.toString(), - "a_name" : workHistory.agency?.name == null? "":workHistory.agency!.name!, - " a_private_entity ": workHistory.agency?.privateEntity == null?"":workHistory.agency!.privateEntity.toString(), - "accomplishment" : accomplishment??"", - "actual_duties ": actualDuties!, - "agency_id" : workHistory.agency?.id == null?"": workHistory.agency!.id.toString() , - "from_date" : fromDate, - "monthly_salary" : workHistory.monthlysalary == null? "": workHistory.monthlysalary.toString(), - "position_id" : workHistory.position?.id == null? "": workHistory.position!.id.toString(), - "position_name" : workHistory.position?.title == null? "":workHistory.position!.title!, - "s_fname" : workHistory.supervisor?.firstname == null?"":workHistory.supervisor!.firstname!, - "s_lname" : workHistory.supervisor?.lastname == null? "":workHistory.supervisor!.lastname!, - "s_mname" : workHistory.supervisor?.middlename == null?"":workHistory.supervisor!.middlename!, - "s_office" : workHistory.supervisor?.stationName == null?"":workHistory.supervisor!.stationName!, - "salary_grade" : workHistory.salarygrade == null? "":workHistory.salarygrade.toString(), - "sg_step" : workHistory.sgstep == null?"":workHistory.sgstep.toString() , - 'status_appointment' : workHistory.statusAppointment??"", - }; - }else{ - body = { -"a_category_id ": workHistory.agency?.category?.id == null? "":workHistory.agency!.category!.id.toString(), - "a_name" : workHistory.agency?.name == null? "":workHistory.agency!.name!, - " a_private_entity ": workHistory.agency?.privateEntity == null?"":workHistory.agency!.privateEntity.toString(), - "accomplishment" : accomplishment??"", - "actual_duties ": actualDuties!, - "agency_id" : workHistory.agency?.id == null?"": workHistory.agency!.id.toString() , - "from_date" : workHistory.fromDate == null? "2018-10-04":"2018-06-04", - "monthly_salary" : workHistory.monthlysalary == null? "": workHistory.monthlysalary.toString(), - "position_id" : workHistory.position?.id == null? "": workHistory.position!.id.toString(), - "position_name" : workHistory.position?.title == null? "":workHistory.position!.title!, - "s_fname" : workHistory.supervisor?.firstname == null?"":workHistory.supervisor!.firstname!, - "s_lname" : workHistory.supervisor?.lastname == null? "":workHistory.supervisor!.lastname!, - "s_mname" : workHistory.supervisor?.middlename == null?"":workHistory.supervisor!.middlename!, - "s_office" : workHistory.supervisor?.stationName == null?"":workHistory.supervisor!.stationName!, - "salary_grade" : workHistory.salarygrade == null? "":workHistory.salarygrade.toString(), - "sg_step" : workHistory.sgstep == null?"":workHistory.sgstep.toString() , - 'status_appointment' : workHistory.statusAppointment??"", - "to_date" : toDate!, - }; + + Map body = {}; + Map statusResponse = {}; + String fromDate = DateFormat('yyyy-MM-dd').format(workHistory.fromDate!); + String? toDate = workHistory.toDate == null + ? null + : DateFormat('yyyy-MM-dd').format(workHistory.toDate!); + if (workHistory.toDate == null) { + body = { + "a_category_id ": workHistory.agency?.category?.id == null + ? "" + : workHistory.agency!.category!.id.toString(), + "a_name": + workHistory.agency?.name == null ? "" : workHistory.agency!.name!, + " a_private_entity ": workHistory.agency?.privateEntity == null + ? "" + : workHistory.agency!.privateEntity.toString(), + "accomplishment": workHistory.accomplishment == null + ? "" + : workHistory.accomplishment!.first.accomplishment!, + "actual_duties ": workHistory.actualDuties == null + ? "" + : workHistory.actualDuties!.first.description, + "agency_id": workHistory.agency?.id == null + ? "" + : workHistory.agency!.id.toString(), + "from_date": fromDate, + "monthly_salary": workHistory.monthlysalary == null + ? "" + : workHistory.monthlysalary.toString(), + "position_id": workHistory.position?.id == null + ? "" + : workHistory.position!.id.toString(), + "position_name": workHistory.position?.title == null + ? "" + : workHistory.position!.title!, + "s_fname": workHistory.supervisor?.firstname == null + ? "" + : workHistory.supervisor!.firstname!, + "s_lname": workHistory.supervisor?.lastname == null + ? "" + : workHistory.supervisor!.lastname!, + "s_mname": workHistory.supervisor?.middlename == null + ? "" + : workHistory.supervisor!.middlename!, + "s_office": workHistory.supervisor?.stationName == null + ? "" + : workHistory.supervisor!.stationName!, + "salary_grade": workHistory.salarygrade == null + ? "" + : workHistory.salarygrade.toString(), + "sg_step": + workHistory.sgstep == null ? "" : workHistory.sgstep.toString(), + 'status_appointment': workHistory.statusAppointment ?? "", + }; + } else { + body = { + "a_category_id ": workHistory.agency?.category?.id == null + ? "" + : workHistory.agency!.category!.id.toString(), + "a_name": + workHistory.agency?.name == null ? "" : workHistory.agency!.name!, + " a_private_entity ": workHistory.agency?.privateEntity == null + ? "" + : workHistory.agency!.privateEntity.toString(), + "accomplishment": workHistory.accomplishment == null + ? "" + : workHistory.accomplishment!.first.accomplishment!, + "actual_duties ": workHistory.actualDuties == null + ? "" + : workHistory.actualDuties!.first.description, + "agency_id": workHistory.agency?.id == null + ? "" + : workHistory.agency!.id.toString(), + "from_date": fromDate, + "monthly_salary": workHistory.monthlysalary == null + ? "" + : workHistory.monthlysalary.toString(), + "position_id": workHistory.position?.id == null + ? "" + : workHistory.position!.id.toString(), + "position_name": workHistory.position?.title == null + ? "" + : workHistory.position!.title!, + "s_fname": workHistory.supervisor?.firstname == null + ? "" + : workHistory.supervisor!.firstname!, + "s_lname": workHistory.supervisor?.lastname == null + ? "" + : workHistory.supervisor!.lastname!, + "s_mname": workHistory.supervisor?.middlename == null + ? "" + : workHistory.supervisor!.middlename!, + "s_office": workHistory.supervisor?.stationName == null + ? "" + : workHistory.supervisor!.stationName!, + "salary_grade": workHistory.salarygrade == null + ? "" + : workHistory.salarygrade.toString(), + "sg_step": + workHistory.sgstep == null ? "" : workHistory.sgstep.toString(), + 'status_appointment': workHistory.statusAppointment ?? "", + "to_date": toDate!, + }; } - - var request = http.MultipartRequest('PUT',Uri.parse('${Url.instance.prefixHost()}://${Url.instance.host()}$path')); - request.fields.addAll(body); - request.headers.addAll(headers); - try { - http.StreamedResponse response = await request.send(); - final steamResponse = await response.stream.bytesToString(); - Map data = jsonDecode(steamResponse); - if (response.statusCode == 201) { - statusResponse = data; - } else { - String message = data['response']['details']; - statusResponse.addAll({'message': message}); - statusResponse.addAll( - {'success': false}, - ); - } - } catch (e) { - throw e.toString(); + + var request = http.MultipartRequest( + 'PUT', + Uri.parse( + '${Url.instance.prefixHost()}://${Url.instance.host()}$path')); + request.fields.addAll(body); + request.headers.addAll(headers); + // try { + http.StreamedResponse response = await request.send(); + final steamResponse = await response.stream.bytesToString(); + Map data = jsonDecode(steamResponse); + if (response.statusCode == 201) { + statusResponse = data; + } else { + String message = data['response']['details']; + statusResponse.addAll({'message': message}); + statusResponse.addAll( + {'success': false}, + ); } -return statusResponse; + // } catch (e) { + // throw e.toString(); + // } + return statusResponse; } - - - ////get agency position Future> getAgencyPosition() async { List agencyPositions = []; @@ -324,7 +436,7 @@ return statusResponse; AppoinemtStatus(value: "Coterminous", label: "Coterminous"), AppoinemtStatus(value: "Elected", label: "Elected"), AppoinemtStatus(value: "Job Order", label: "Job Order"), - AppoinemtStatus(value: "Permanent", label: "Permanent"), + AppoinemtStatus(value: "Permanent", label: "Permanent"), AppoinemtStatus(value: "Elected", label: "Elected"), ]; } diff --git a/lib/utils/url_launcher_file_downloader.dart b/lib/utils/url_launcher_file_downloader.dart new file mode 100644 index 0000000..ac3155f --- /dev/null +++ b/lib/utils/url_launcher_file_downloader.dart @@ -0,0 +1,16 @@ +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + + Future launchInBrowser(String url) async { + final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance; + if (!await launcher.launch( + url, + useSafariVC: false, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: {}, + )) { + throw Exception('Could not launch $url'); + } + } \ No newline at end of file diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 9a31980..e8df21a 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -5,6 +5,8 @@ PODS: - FlutterMacOS - audioplayers_darwin (0.0.1): - FlutterMacOS + - device_info_plus (0.0.1): + - FlutterMacOS - FlutterMacOS (1.0.0) - FMDB (2.7.5): - FMDB/standard (= 2.7.5) @@ -24,17 +26,24 @@ PODS: - FlutterMacOS - rive_common (0.0.1): - FlutterMacOS + - share_plus (0.0.1): + - FlutterMacOS - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS - sqflite (0.0.2): - FlutterMacOS - FMDB (>= 2.7.5) + - syncfusion_pdfviewer_macos (0.0.1): + - FlutterMacOS + - url_launcher_macos (0.0.1): + - FlutterMacOS DEPENDENCIES: - assets_audio_player (from `Flutter/ephemeral/.symlinks/plugins/assets_audio_player/macos`) - assets_audio_player_web (from `Flutter/ephemeral/.symlinks/plugins/assets_audio_player_web/macos`) - audioplayers_darwin (from `Flutter/ephemeral/.symlinks/plugins/audioplayers_darwin/macos`) + - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - location (from `Flutter/ephemeral/.symlinks/plugins/location/macos`) - modal_progress_hud_nsn (from `Flutter/ephemeral/.symlinks/plugins/modal_progress_hud_nsn/macos`) @@ -43,8 +52,11 @@ DEPENDENCIES: - platform_device_id (from `Flutter/ephemeral/.symlinks/plugins/platform_device_id/macos`) - platform_device_id_macos (from `Flutter/ephemeral/.symlinks/plugins/platform_device_id_macos/macos`) - rive_common (from `Flutter/ephemeral/.symlinks/plugins/rive_common/macos`) + - share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`) - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos`) - sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`) + - syncfusion_pdfviewer_macos (from `Flutter/ephemeral/.symlinks/plugins/syncfusion_pdfviewer_macos/macos`) + - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) SPEC REPOS: trunk: @@ -57,6 +69,8 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/assets_audio_player_web/macos audioplayers_darwin: :path: Flutter/ephemeral/.symlinks/plugins/audioplayers_darwin/macos + device_info_plus: + :path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos FlutterMacOS: :path: Flutter/ephemeral location: @@ -73,15 +87,22 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/platform_device_id_macos/macos rive_common: :path: Flutter/ephemeral/.symlinks/plugins/rive_common/macos + share_plus: + :path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos shared_preferences_foundation: :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos sqflite: :path: Flutter/ephemeral/.symlinks/plugins/sqflite/macos + syncfusion_pdfviewer_macos: + :path: Flutter/ephemeral/.symlinks/plugins/syncfusion_pdfviewer_macos/macos + url_launcher_macos: + :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos SPEC CHECKSUMS: assets_audio_player: be2578e6f11dd4d183412e97143673c3c4cb2e8a assets_audio_player_web: 917101123b6db8f73156835c0fa266c11340ff15 audioplayers_darwin: dcad41de4fbd0099cb3749f7ab3b0cb8f70b810c + device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a location: 7cdb0665bd6577d382b0a343acdadbcb7f964775 @@ -91,8 +112,11 @@ SPEC CHECKSUMS: platform_device_id: 3e414428f45df149bbbfb623e2c0ca27c545b763 platform_device_id_macos: f763bb55f088be804d61b96eb4710b8ab6598e94 rive_common: fab8476ce8352bf54152a913f393a8696d3dc98c + share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7 shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea + syncfusion_pdfviewer_macos: e9194851581cad04b28b53913d0636d39a4ed4b2 + url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95 PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7 From 09b4071ae2817842fe3cb905959a86b0747afacd Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Fri, 1 Sep 2023 13:41:51 +0800 Subject: [PATCH 85/86] fix bugs for v1 release --- .../profile/shared/multiple_attachment.dart | 1 - .../components/drawer-screen.dart | 1 - lib/utils/urls.dart | 3 +- pubspec.lock | 203 ++++++++---------- 4 files changed, 94 insertions(+), 114 deletions(-) diff --git a/lib/screens/profile/shared/multiple_attachment.dart b/lib/screens/profile/shared/multiple_attachment.dart index 149cdfb..03dbeb4 100644 --- a/lib/screens/profile/shared/multiple_attachment.dart +++ b/lib/screens/profile/shared/multiple_attachment.dart @@ -7,7 +7,6 @@ import 'package:unit2/bloc/profile/education/education_bloc.dart'; import 'package:unit2/bloc/profile/eligibility/eligibility_bloc.dart'; import 'package:unit2/bloc/profile/learningDevelopment/learning_development_bloc.dart'; import 'package:unit2/bloc/profile/workHistory/workHistory_bloc.dart'; -import 'package:unit2/screens/profile/shared/view_attachment.dart'; import 'package:unit2/utils/global_context.dart'; import '../../../model/profile/attachment.dart'; diff --git a/lib/screens/unit2/homepage.dart/components/drawer-screen.dart b/lib/screens/unit2/homepage.dart/components/drawer-screen.dart index c1ba365..4f0d609 100644 --- a/lib/screens/unit2/homepage.dart/components/drawer-screen.dart +++ b/lib/screens/unit2/homepage.dart/components/drawer-screen.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_zoom_drawer/config.dart'; import 'package:flutter_zoom_drawer/flutter_zoom_drawer.dart'; import 'package:unit2/theme-data.dart/colors.dart'; import '../../../../bloc/user/user_bloc.dart'; diff --git a/lib/utils/urls.dart b/lib/utils/urls.dart index 896d0d7..20385fe 100644 --- a/lib/utils/urls.dart +++ b/lib/utils/urls.dart @@ -15,7 +15,7 @@ class Url { String prefixHost() { return "https"; - // return "https"; + // return "http"; } String authentication() { @@ -26,6 +26,7 @@ class Url { return 'api/jobnet_app/profile/pds/'; } + String latestApk() { return "/api/system_app/apk_version/latest"; } diff --git a/pubspec.lock b/pubspec.lock index 21b386e..71599d5 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -53,18 +53,18 @@ packages: dependency: "direct main" description: name: assets_audio_player - sha256: dcea8cd9c11cd9c34586f2446bfcdf099362159c56f97517ba941ac151974ea9 + sha256: "9a87062cf39be0730ba8bb31ed4d148ca4e892e0ae607113f346d3c9a8da5df0" url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.1.1" assets_audio_player_web: dependency: transitive description: name: assets_audio_player_web - sha256: "4575ec40033d818ff022d48f7d46e24c01bc632bd1cd36a4f8b58b38e9aa4a81" + sha256: "24cf82e72c7e7f9292d67e1b52d7945a182d9695ce8f903f60e5c6b379cbcaac" url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.1.1" async: dependency: transitive description: @@ -245,10 +245,10 @@ packages: dependency: transitive description: name: built_value - sha256: "598a2a682e2a7a90f08ba39c0aaa9374c5112340f0a2e275f61b59389543d166" + sha256: ff627b645b28fb8bdb69e645f910c2458fd6b65f6585c3a53e0626024897dedf url: "https://pub.dev" source: hosted - version: "8.6.1" + version: "8.6.2" cached_network_image: dependency: "direct main" description: @@ -301,10 +301,10 @@ packages: dependency: transitive description: name: code_builder - sha256: "4ad01d6e56db961d29661561effde45e519939fdaeb46c351275b182eac70189" + sha256: "315a598c7fbe77f22de1c9da7cfd6fd21816312f16ffa124453b4fc679e540f1" url: "https://pub.dev" source: hosted - version: "4.5.0" + version: "4.6.0" collection: dependency: transitive description: @@ -341,10 +341,10 @@ packages: dependency: transitive description: name: cross_file - sha256: "0b0036e8cccbfbe0555fd83c1d31a6f30b77a96b598b35a5d36dd41f718695e9" + sha256: fd832b5384d0d6da4f6df60b854d33accaaeb63aa9e10e736a87381f08dee2cb url: "https://pub.dev" source: hosted - version: "0.3.3+4" + version: "0.3.3+5" crypto: dependency: transitive description: @@ -546,10 +546,10 @@ packages: dependency: transitive description: name: flutter_cache_manager - sha256: "32cd900555219333326a2d0653aaaf8671264c29befa65bbd9856d204a4c9fb3" + sha256: "8207f27539deb83732fdda03e259349046a39a4c767269285f449ade355d54ba" url: "https://pub.dev" source: hosted - version: "3.3.0" + version: "3.3.1" flutter_custom_clippers: dependency: "direct main" description: @@ -578,10 +578,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.3" flutter_localizations: dependency: transitive description: flutter @@ -591,10 +591,10 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: "950e77c2bbe1692bc0874fc7fb491b96a4dc340457f4ea1641443d0a6c1ea360" + sha256: f185ac890306b5779ecbd611f52502d8d4d63d27703ef73161ca0407e815f02c url: "https://pub.dev" source: hosted - version: "2.0.15" + version: "2.0.16" flutter_progress_hud: dependency: "direct main" description: @@ -603,16 +603,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.2" -<<<<<<< HEAD - flutter_speed_dial: - dependency: "direct main" - description: - name: flutter_speed_dial - sha256: "41d7ad0bc224248637b3a5e0b9083e912a75445bdb450cf82b8ed06d7af7c61d" - url: "https://pub.dev" - source: hosted - version: "6.2.0" -======= flutter_simple_treeview: dependency: "direct main" description: @@ -621,7 +611,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.2" ->>>>>>> develop + flutter_speed_dial: + dependency: "direct main" + description: + name: flutter_speed_dial + sha256: "41d7ad0bc224248637b3a5e0b9083e912a75445bdb450cf82b8ed06d7af7c61d" + url: "https://pub.dev" + source: hosted + version: "6.2.0" flutter_spinkit: dependency: "direct main" description: @@ -660,10 +657,10 @@ packages: dependency: "direct main" description: name: flutter_zoom_drawer - sha256: b439c87d63680465582ac301f2502dea6f95ddcba3a9787365dcadb52777c1e1 + sha256: be25be3536e030096c356b7b0fbeaee418b5e0994d87ff74875032b349cb4079 url: "https://pub.dev" source: hosted - version: "3.0.4+1" + version: "3.1.1" fluttericon: dependency: "direct main" description: @@ -692,10 +689,10 @@ packages: dependency: transitive description: name: freezed_annotation - sha256: aeac15850ef1b38ee368d4c53ba9a847e900bb2c53a4db3f6881cbb3cb684338 + sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.4.1" frontend_server_client: dependency: transitive description: @@ -884,10 +881,10 @@ packages: dependency: "direct main" description: name: mask_text_input_formatter - sha256: "19bb7809c3c2559277e95521b3ee421e1409eb2cc85efd2feb191696c92490f4" + sha256: "2056a9b8303f71003b9c06c6f71443504f3ca9f4b79b1aa40c9f0f62f9d312cf" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.5.0" matcher: dependency: transitive description: @@ -1028,98 +1025,90 @@ packages: dependency: "direct main" description: name: path_provider - sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2" + sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa url: "https://pub.dev" source: hosted - version: "2.0.15" + version: "2.1.1" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "2cec049d282c7f13c594b4a73976b0b4f2d7a1838a6dd5aaf7bd9719196bee86" + sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1" url: "https://pub.dev" source: hosted - version: "2.0.27" + version: "2.2.0" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "1995d88ec2948dac43edf8fe58eb434d35d22a2940ecee1a9fefcd62beee6eb3" + sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.3.1" path_provider_linux: dependency: transitive description: name: path_provider_linux - sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57 + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 url: "https://pub.dev" source: hosted - version: "2.1.11" + version: "2.2.1" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec" + sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" url: "https://pub.dev" source: hosted - version: "2.0.6" + version: "2.1.1" path_provider_windows: dependency: transitive description: name: path_provider_windows - sha256: "1cb68ba4cd3a795033de62ba1b7b4564dace301f952de6bfb3cd91b202b6ee96" + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" url: "https://pub.dev" source: hosted - version: "2.1.7" - pedantic: - dependency: transitive - description: - name: pedantic - sha256: "67fc27ed9639506c856c840ccce7594d0bdcd91bc8d53d6e52359449a1d50602" - url: "https://pub.dev" - source: hosted - version: "1.11.1" + version: "2.2.1" permission_handler: dependency: "direct main" description: name: permission_handler - sha256: "1b6b3e73f0bcbc856548bbdfb1c33084a401c4f143e220629a9055233d76c331" + sha256: "63e5216aae014a72fe9579ccd027323395ce7a98271d9defa9d57320d001af81" url: "https://pub.dev" source: hosted - version: "10.3.0" + version: "10.4.3" permission_handler_android: dependency: transitive description: name: permission_handler_android - sha256: "8f6a95ccbca13766882f95d32684d7c9bfe6c45650c32bedba948ef1c6a4ddf7" + sha256: d74e77a5ecd38649905db0a7d05ef16bed42ff263b9efb73ed794317c5764ec3 url: "https://pub.dev" source: hosted - version: "10.2.3" + version: "10.3.4" permission_handler_apple: dependency: transitive description: name: permission_handler_apple - sha256: "08dcb6ce628ac0b257e429944b4c652c2a4e6af725bdf12b498daa2c6b2b1edb" + sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5" url: "https://pub.dev" source: hosted - version: "9.1.0" + version: "9.1.4" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface - sha256: de20a5c3269229c1ae2e5a6b822f6cb59578b23e8255c93fbeebfc82116e6b11 + sha256: "7c6b1500385dd1d2ca61bb89e2488ca178e274a69144d26bbd65e33eae7c02a9" url: "https://pub.dev" source: hosted - version: "3.10.0" + version: "3.11.3" permission_handler_windows: dependency: transitive description: name: permission_handler_windows - sha256: f67cab14b4328574938ecea2db3475dad7af7ead6afab6338772c5f88963e38b + sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098 url: "https://pub.dev" source: hosted - version: "0.1.2" + version: "0.1.3" petitparser: dependency: transitive description: @@ -1132,10 +1121,10 @@ packages: dependency: transitive description: name: platform - sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.2" platform_device_id: dependency: "direct main" description: @@ -1188,10 +1177,10 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" + sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.6" pointycastle: dependency: transitive description: @@ -1208,14 +1197,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" - process: - dependency: transitive - description: - name: process - sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" - url: "https://pub.dev" - source: hosted - version: "4.2.4" protobuf: dependency: transitive description: @@ -1268,18 +1249,18 @@ packages: dependency: transitive description: name: rive - sha256: bb7a16bc6a88484fe3136890030b71776ffea368edd2a8368fe99d6430e4a802 + sha256: f3b8af0898c987d68019e91d92257edd902c28c816e49de033a7272e86bd5425 url: "https://pub.dev" source: hosted - version: "0.11.2" + version: "0.11.4" rive_common: dependency: transitive description: name: rive_common - sha256: "5a0dbf689527c51ee5430608181d81460c9c45ab6cc3bd52dd9bbb3d8c4455b6" + sha256: f6687f9d70e6fd3888a9b0e9c0b307966d2ce74cf00cfb01dce906c3bbada52f url: "https://pub.dev" source: hosted - version: "0.0.9" + version: "0.1.0" rxdart: dependency: transitive description: @@ -1340,58 +1321,58 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: "396f85b8afc6865182610c0a2fc470853d56499f75f7499e2a73a9f0539d23d0" + sha256: b7f41bad7e521d205998772545de63ff4e6c97714775902c199353f8bf1511ac url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.2.1" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "6478c6bbbecfe9aced34c483171e90d7c078f5883558b30ec3163cf18402c749" + sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.1" shared_preferences_foundation: dependency: transitive description: name: shared_preferences_foundation - sha256: e014107bb79d6d3297196f4f2d0db54b5d1f85b8ea8ff63b8e8b391a02700feb + sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7" url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.3.4" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux - sha256: "9d387433ca65717bbf1be88f4d5bb18f10508917a8fa2fb02e0fd0d7479a9afa" + sha256: c2eb5bf57a2fe9ad6988121609e47d3e07bb3bdca5b6f8444e4cf302428a128a url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.3.1" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface - sha256: fb5cf25c0235df2d0640ac1b1174f6466bd311f621574997ac59018a6664548d + sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.3.1" shared_preferences_web: dependency: transitive description: name: shared_preferences_web - sha256: "74083203a8eae241e0de4a0d597dbedab3b8fef5563f33cf3c12d7e93c655ca5" + sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.2.1" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows - sha256: "5e588e2efef56916a3b229c3bfe81e6a525665a454519ca51dbcc4236a274173" + sha256: f763a101313bd3be87edffe0560037500967de9c394a714cd598d945517f694f url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.3.1" shelf: dependency: transitive description: @@ -1465,10 +1446,10 @@ packages: dependency: transitive description: name: sqflite_common - sha256: e77abf6ff961d69dfef41daccbb66b51e9983cdd5cb35bf30733598057401555 + sha256: "8f7603f3f8f126740bc55c4ca2d1027aab4b74a1267a3e31ce51fe40e3b65b8f" url: "https://pub.dev" source: hosted - version: "2.4.5" + version: "2.4.5+1" stack_trace: dependency: transitive description: @@ -1633,58 +1614,58 @@ packages: dependency: "direct main" description: name: url_launcher_android - sha256: "3dd2388cc0c42912eee04434531a26a82512b9cb1827e0214430c9bcbddfe025" + sha256: b04af59516ab45762b2ca6da40fa830d72d0f6045cd97744450b73493fa76330 url: "https://pub.dev" source: hosted - version: "6.0.38" + version: "6.1.0" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "9af7ea73259886b92199f9e42c116072f05ff9bea2dcb339ab935dfc957392c2" + sha256: "7c65021d5dee51813d652357bc65b8dd4a6177082a9966bc8ba6ee477baa795f" url: "https://pub.dev" source: hosted - version: "6.1.4" + version: "6.1.5" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: "207f4ddda99b95b4d4868320a352d374b0b7e05eefad95a4a26f57da413443f5" + sha256: b651aad005e0cb06a01dbd84b428a301916dc75f0e7ea6165f80057fee2d8e8e url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.6" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - sha256: "1c4fdc0bfea61a70792ce97157e5cc17260f61abbe4f39354513f39ec6fd73b1" + sha256: b55486791f666e62e0e8ff825e58a023fd6b1f71c49926483f1128d3bbd8fe88 url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.0.7" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - sha256: bfdfa402f1f3298637d71ca8ecfe840b4696698213d5346e9d12d4ab647ee2ea + sha256: "95465b39f83bfe95fcb9d174829d6476216f2d548b79c38ab2506e0458787618" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.5" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: cc26720eefe98c1b71d85f9dc7ef0cada5132617046369d9dc296b3ecaa5cbb4 + sha256: ba140138558fcc3eead51a1c42e92a9fb074a1b1149ed3c73e66035b2ccd94f2 url: "https://pub.dev" source: hosted - version: "2.0.18" + version: "2.0.19" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: "7967065dd2b5fccc18c653b97958fdf839c5478c28e767c61ee879f4e7882422" + sha256: "95fef3129dc7cfaba2bc3d5ba2e16063bb561fc6d78e63eee16162bc70029069" url: "https://pub.dev" source: hosted - version: "3.0.7" + version: "3.0.8" uuid: dependency: transitive description: @@ -1729,10 +1710,10 @@ packages: dependency: transitive description: name: xdg_directories - sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1 + sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.0.3" xml: dependency: transitive description: From f109fa3b2aa51ecf5004786cffafc6c694e2f1ab Mon Sep 17 00:00:00 2001 From: PGAN-MIS Date: Fri, 1 Sep 2023 13:56:34 +0800 Subject: [PATCH 86/86] fix bugs for passo navigation screen --- .../unit2/homepage.dart/components/dashboard/dashboard.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart b/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart index 2071193..c814837 100644 --- a/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart +++ b/lib/screens/unit2/homepage.dart/components/dashboard/dashboard.dart @@ -646,7 +646,9 @@ class _DashBoardState extends State { title: e.object.name == 'Real Property' ? "Field Surveyor" : e.object.name!, - ontap: () {}) + ontap: () { + Navigator.pushNamed(context, '/passo-home'); + }) : Container( color: Colors.black, ));

4U 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 X#J1$b??@evwKWRu^?=~{UFQD-d`?7> literal 0 HcmV?d00001 diff --git a/unit2/assets/pngs/qr-scan.png b/unit2/assets/pngs/qr-scan.png new file mode 100644 index 0000000000000000000000000000000000000000..2e534a2680deacc92cb82774cbc7bf918d107c56 GIT binary patch literal 18267 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 oPV?}0Nz>-NDvt6r66Ro*PQBOi \ No newline at end of file diff --git a/unit2/assets/svgs/download.svg b/unit2/assets/svgs/download.svg new file mode 100644 index 0000000..feb7a4c --- /dev/null +++ b/unit2/assets/svgs/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/unit2/assets/svgs/emergency.png b/unit2/assets/svgs/emergency.png new file mode 100644 index 0000000000000000000000000000000000000000..5a1201577c01d70058cf91a2bd8fa4742cf50c66 GIT binary patch literal 163354 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�V%6lst{EdZE2SkF03j&5vUj-sgica&6I1TWkq zo^NfDLOYz51EDtsg=aUeJM2Yek9?8};9f9qM1+m(_>urilLh>6ClmlcxRiuY&@Q4n zeqF5yAp8MO3iF!S!-IYs<$aaHUjne;|D5@x=b^H#Fj3y5FeY6-6njo|fSyj&)tb+t zP^c&~;n-Hbwlly)`40hF+4DA<{J-1V()Rc<2(oL3i5g;pgN1Mp0GHn%!EXVIra-2i zKAY-JxrE49BS4RZK8AL)$Oscch!{f>&M$n3Zh+r9g&KSS^KU;-(VlfU7VN_g5{v~s z1^EQr17480v#DN`<=9BB&~%hZ@xxQbVL*`QIh7uq8rGx&(8fo#5)<0E&kNUIeYyV2 zfBUb!H{N)|iena*H~BmJCW#j*e-WG z{AwAzJZBj!s{)~vSd<34CCnF1Q*saQ& zEU)%HxOuPt{-IYk;5pm>gKMXj_R_cy^uuqZclqmX;QHeiGnyL$+=#>?fTl~JjQ(m{W3E*eHvSC=j4bzRE>=P4{rJIkKmpHC9l89FYjKrhGWK9<FD8~*pG7YDC;GQ<@#gOup&pnj-iM^HUJ0N#@fPXPmn($02e`bpyc5l>$g zd>u#wur>~RI?&WwWPMd2qUlCS2p2+wtY0Jp9EyaZF(a}B3fYqdWlQj@kZSb_3Y7@>1}V`w4Aj zuM0qT79{61JX--MH&>yuJNc?HMr`dK6W1)to!De7y9^1Jd}C)2uLN&UB|y{MVd}^X z(@3J7!r=a}xFu>x07n4a16<_t!<6Y+OKQuhqM1TKl0917$Jma!PWUIvwMl;bw(s%2 zFEN5)X~XN+wd+S8RZDe^ZQyft;QCn8DbM~*Xe$Biqi-PkAYNFQ!^a*o1M&~~ToWat z5)BTeXyb-{dh6{^>7$Q^@O=*1fargC{P>>d1PEkp5)$G3t5&aCbjs=a(gX5fnuI6$ z-GeXtk=W6M^;DPYx)9o!3t(stcWHc|vmoZ!$HX370x%O&^e{s4GEf80`7{jb1%Rk$ zV!O8$AjL=w+=ALjlX_)Xx4&!+6-iYuF0F{cDT;>SvW)Z%K z+$Ln5wNq%-C)Ba*9nmgPiws@~P7@c#5TQ2aA2A7-Lu9)<^2GkmrqIY1>3me`-K4C8}6ISOV23EqYoEe%xWT}2&P_Rc{C zFq)?!@Qf;zFtX*a2Ycz@31ZJ7m*}z}BAA12lmrc$J>>-n{t=qRP{#Ij*qkdiv*r)e zND_o+^mp^D6G?443cmvYi%daVY1>9x#lTw;k8&^B5=@wnj0pK=E~LoFChGcV0|k7@ zX5?}u7}s3O%>_@98=`^(0NNge3HgPAHHs7vr(NJx<=CraXC|_VIQ%sssJBq?#b12+ zBjv-7KNZ(kys*D0cxnvzH6~z{PIkS!DYS(AIW{aYD{8(eI;;YooM! z4Roo70bn`P4P{}t$-wToM(BR!hirSXC)G647`Y?xX6^o_nAo9vP>v2DQiTG+i{RP5 z8hU0Y;aOCV+G_9>+ROfTu&^|$_EH0$6*XR;XQtb5e-&Ws@w*;<;{^!QZiP39!)B9u zaRAVy?jEOcV=a))WE!F7d}+=s9O?FY+{4kRNN1ltcaC_I^?~cdclJ9=m*TM4Z3L{%@OJ)?p_|xaY64k4;0L(j za1i`;uYcT5v+LfcTQ5GDrav*`#)E z9Sd8!z&k^!PY|!`NUNWZwxXt&2)AuZv$yr8i<~U}J4R_6kQKymWWhx$$=A?E+Qv$F zM4})Jv%n_8Mhy5M_Niff#nOYIoIzo^nCLD95g9y#T+Q{*M2yWQz)ncd*VfOC%{$_h zmtK77=@VD4AB3kCVe6)k$?Ne^CWEn#_OkY<#ER&MV`Y#b7Ec(Iilgh16-nUzK#Cjy zj|;o9*A8J%C|_qFeE|P0!p4~9F(_h*Sl;8QT6fARr@V&yp9#E&&GMW3mZ-SqW&n}% zv0eo)&0J)lmT}0HWvXobs_|Y-6cI9CNgR|6d1v+-l=dH3@*1O5GHg5Pt274TItIn@sW>vWKtt7~h7#!m*dnAWaYLz_2lwA0=3 z^2f^0E>_9K3i6*&%FTmZnSuL=4vUpSNTPS3Ed`M9u(Nc z#WhF}`^I!Kh*5! z_sV22A~(^kCrA>37D!twn*0hSPcO)drunR=XeIy_F_#NSo`u>0?PXU&Yk}MDTU<6W5Ww!JG?H>b3uP#v0^FRe|GiP z_F{V5bzj+U`iBl4Fq>!_VPzARaZ~mz3VDnU%Z;}w8 zgoP8i^12C6LC_V$HfUw<3<6?@P8p#e%mD+5BE?diStn!cz!7SK|1@c(gn_H^kZJ&wdb=5&z zl#sy=6P9_}XTTvmKel&IWQ%bfEdh3MefW&RwD6h%NU%^QUoiWX1bZ0a7W{eUy)Ig} zp^v6RWN+buO8Uz2^C{xZ1nA)v^~_lE zfB~~DfVsW4f!!-MpNK=04EWseh>(eW{m_l_fFKN1RfT?KeNA=}7VsZ75tqM6M29HC zAWlPq0M}Gw�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 z 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 OA1N_8(JEns!2bseL#4L> literal 0 HcmV?d00001 diff --git a/unit2/assets/svgs/empty.svg b/unit2/assets/svgs/empty.svg new file mode 100644 index 0000000..5834f68 --- /dev/null +++ b/unit2/assets/svgs/empty.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/unit2/assets/svgs/error.svg b/unit2/assets/svgs/error.svg new file mode 100644 index 0000000..3fbbf17 --- /dev/null +++ b/unit2/assets/svgs/error.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/unit2/assets/svgs/female.svg b/unit2/assets/svgs/female.svg new file mode 100644 index 0000000..35fa415 --- /dev/null +++ b/unit2/assets/svgs/female.svg @@ -0,0 +1 @@ +female_avatar \ No newline at end of file diff --git a/unit2/assets/svgs/logo.svg b/unit2/assets/svgs/logo.svg new file mode 100644 index 0000000..81670ef --- /dev/null +++ b/unit2/assets/svgs/logo.svg @@ -0,0 +1,108 @@ + + + + + + + + diff --git a/unit2/assets/svgs/male.svg b/unit2/assets/svgs/male.svg new file mode 100644 index 0000000..a2b2d1e --- /dev/null +++ b/unit2/assets/svgs/male.svg @@ -0,0 +1 @@ +male_avatar \ No newline at end of file diff --git a/unit2/assets/svgs/settings.svg b/unit2/assets/svgs/settings.svg new file mode 100644 index 0000000..b6f9644 --- /dev/null +++ b/unit2/assets/svgs/settings.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/unit2/assets/svgs/sos.svg b/unit2/assets/svgs/sos.svg new file mode 100644 index 0000000..6133f7f --- /dev/null +++ b/unit2/assets/svgs/sos.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/unit2/assets/svgs/timeout.svg b/unit2/assets/svgs/timeout.svg new file mode 100644 index 0000000..7638790 --- /dev/null +++ b/unit2/assets/svgs/timeout.svg @@ -0,0 +1 @@ +server down \ No newline at end of file diff --git a/unit2/assets/svgs/welcome.svg b/unit2/assets/svgs/welcome.svg new file mode 100644 index 0000000..1518990 --- /dev/null +++ b/unit2/assets/svgs/welcome.svg @@ -0,0 +1 @@ +welcome_cats \ No newline at end of file diff --git a/unit2/assets/svgs/workspace.svg b/unit2/assets/svgs/workspace.svg new file mode 100644 index 0000000..4f56391 --- /dev/null +++ b/unit2/assets/svgs/workspace.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/unit2/ios/.gitignore b/unit2/ios/.gitignore new file mode 100644 index 0000000..7a7f987 --- /dev/null +++ b/unit2/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/unit2/ios/Flutter/AppFrameworkInfo.plist b/unit2/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..9625e10 --- /dev/null +++ b/unit2/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 + 11.0 + + diff --git a/unit2/ios/Flutter/Debug.xcconfig b/unit2/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..592ceee --- /dev/null +++ b/unit2/ios/Flutter/Debug.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/unit2/ios/Flutter/Release.xcconfig b/unit2/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..592ceee --- /dev/null +++ b/unit2/ios/Flutter/Release.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/unit2/ios/Runner.xcodeproj/project.pbxproj b/unit2/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..65a38e0 --- /dev/null +++ b/unit2/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,481 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 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 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 = ""; }; + 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 */ + 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 */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + 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 */ + 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 = { + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 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 */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 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; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + 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; + 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 */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase 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; + 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; + 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 = 11.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.unit2; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + 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; + 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 = 11.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; + 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; + 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 = 11.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.unit2; + 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.unit2; + 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 */ + 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/unit2/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/unit2/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/unit2/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/unit2/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/unit2/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/unit2/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/unit2/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/unit2/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/unit2/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/unit2/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/unit2/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..c87d15a --- /dev/null +++ b/unit2/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unit2/ios/Runner.xcworkspace/contents.xcworkspacedata b/unit2/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/unit2/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/unit2/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/unit2/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/unit2/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/unit2/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/unit2/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/unit2/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/unit2/ios/Runner/AppDelegate.swift b/unit2/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000..70693e4 --- /dev/null +++ b/unit2/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d36b1fa --- /dev/null +++ b/unit2/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/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..dc9ada4725e9b0ddb1deab583e5b5102493aa332 GIT binary patch literal 10932 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 literal 0 HcmV?d00001 diff --git a/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..f091b6b0bca859a3f474b03065bef75ba58a9e4c GIT binary patch literal 1588 zcmV-42Fv-0P)C1SqPt}wig>|5Crh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)y zRAZ>eDe#*r`yDAVgB_R*LB*MAc)8(b{g{9McCXW!lq7r(btRoB9!8B-#AI6JMb~YFBEvdsV)`mEQO^&#eRKx@b&x- z5lZm*!WfD8oCLzfHGz#u7sT0^VLMI1MqGxF^v+`4YYnVYgk*=kU?HsSz{v({E3lb9 z>+xILjBN)t6`=g~IBOelGQ(O990@BfXf(DRI5I$qN$0Gkz-FSc$3a+2fX$AedL4u{ z4V+5Ong(9LiGcIKW?_352sR;LtDPmPJXI{YtT=O8=76o9;*n%_m|xo!i>7$IrZ-{l z-x3`7M}qzHsPV@$v#>H-TpjDh2UE$9g6sysUREDy_R(a)>=eHw-WAyfIN z*qb!_hW>G)Tu8nSw9yn#3wFMiLcfc4pY0ek1}8(NqkBR@t4{~oC>ryc-h_ByH(Cg5 z>ao-}771+xE3um9lWAY1FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zm zZQj(aA_HeBY&OC^jj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5Kh zX*|AU4QE#~SgPzOXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&g3j|zDgC+}2Q_v%YfDax z!?umcN^n}KYQ|a$Lr+51Nf9dkkYFSjZZjkma$0KOj+;aQ&721~t7QUKx61J3(P4P1 zstI~7-wOACnWP4=8oGOwz%vNDqD8w&Q`qcNGGrbbf&0s9L0De{4{mRS?o0MU+nR_! zrvshUau0G^DeMhM_v{5BuLjb#Hh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG z;P%vktHcXwTj~+IE(~px)3*MY77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC z(zYC$g|@+I+kY;dg_dE>scBf&bP1Nc@Hz<3R)V`=AGkc;8CXqdi=B4l2k|g;2%#m& z*jfX^%b!A8#bI!j9-0Fi0bOXl(-c^AB9|nQaE`*)Hw+o&jS9@7&Gov#HbD~#d{twV zXd^Tr^mWLfFh$@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)y zI9C9*oUga6=hxw6QasLPnee@3^Rr*M{CdaL5=R41nLs(AHk_=Y+A9$2&H(B7!_pURs&8aNw7?`&Z&xY_Ye z)~D5Bog^td-^QbUtkTirdyK^mTHAOuptDflut!#^lnKqU md>ggs(5nOWAqO?umG&QVYK#ibz}*4>0000U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~cTNYR~@Y9KepMPrrB8cABapAFa=`H+UGhkXUZV1GnwR1*lPyZ;*K(i~2gp|@bzp8}og7e*#% zEnr|^CWdVV!-4*Y_7rFvlww2Ze+>j*!Z!pQ?2l->4q#nqRu9`ELo6RMS5=br47g_X zRw}P9a7RRYQ%2Vsd0Me{_(EggTnuN6j=-?uFS6j^u69elMypu?t>op*wBx<=Wx8?( ztpe^(fwM6jJX7M-l*k3kEpWOl_Vk3@(_w4oc}4YF4|Rt=2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX?F&rExl^W|G8Z8;`5z-k}OGNZ0#WVb$WCpQu-_YsiqKP?BB# vzVHS-CTUF4Ozn5G+mq_~Qqto~ahA+K`|lyv3(-e}00000NkvXXu0mjfd`9t{ literal 0 HcmV?d00001 diff --git a/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d0ef06e7edb86cdfe0d15b4b0d98334a86163658 GIT binary patch literal 1716 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 literal 0 HcmV?d00001 diff --git a/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c8f9ed8f5cee1c98386d13b17e89f719e83555b2 GIT binary patch literal 1895 zcmV-t2blPYP)FQtfgmafE#=YDCq`qUBt#QpG%*H6QHY765~R=q zZ6iudfM}q!Pz#~9JgOi8QJ|DSu?1-*(kSi1K4#~5?#|rh?sS)(-JQqX*}ciXJ56_H zdw=^s_srbAdqxlvGyrgGet#6T7_|j;95sL%MtM;q86vOxKM$f#puR)Bjv9Zvz9-di zXOTSsZkM83)E9PYBXC<$6(|>lNLVBb&&6y{NByFCp%6+^ALR@NCTse_wqvNmSWI-m z!$%KlHFH2omF!>#%1l3LTZg(s7eof$7*xB)ZQ0h?ejh?Ta9fDv59+u#MokW+1t8Zb zgHv%K(u9G^Lv`lh#f3<6!JVTL3(dCpxHbnbA;kKqQyd1~^Xe0VIaYBSWm6nsr;dFj z4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3QU4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKubPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`Q zykI#*+rQ=z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRe zt3L_uNyQ*cE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=ky zx=~RKa4{iTm{_>_vSCm?$Ej=i6@=m%@VvAITnigVg{&@!7CDgs908761meDK5azA} z4?=NOH|PdvabgJ&fW2{Mo$Q0CcD8Qc84%{JPYt5EiG{MdLIAeX%T=D7NIP4%Hw}p9 zg)==!2Lbp#j{u_}hMiao9=!VSyx0gHbeCS`;q&vzeq|fs`y&^X-lso(Ls@-706qmA z7u*T5PMo_w3{se1t2`zWeO^hOvTsohG_;>J0wVqVe+n)AbQCx)yh9;w+J6?NF5Lmo zecS@ieAKL8%bVd@+-KT{yI|S}O>pYckUFs;ry9Ow$CD@ztz5K-*D$^{i(_1llhSh^ zEkL$}tsQt5>QA^;QgjgIfBDmcOgi5YDyu?t6vSnbp=1+@6D& z5MJ}B8q;bRlVoxasyhcUF1+)o`&3r0colr}QJ3hcSdLu;9;td>kf@Tcn<@9sIx&=m z;AD;SCh95=&p;$r{Xz3iWCO^MX83AGJ(yH&eTXgv|0=34#-&WAmw{)U7OU9!Wz^!7 zZ%jZFi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i h0WYBP*#0Ks^FNSabJA*5${_#%002ovPDHLkV1oKhTl@e3 literal 0 HcmV?d00001 diff --git a/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..75b2d164a5a98e212cca15ea7bf2ab5de5108680 GIT binary patch literal 3831 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 literal 0 HcmV?d00001 diff --git a/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/unit2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..c4df70d39da7941ef3f6dcb7f06a192d8dcb308d GIT binary patch literal 1888 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 iW8Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/unit2/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/unit2/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/unit2/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/unit2/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/unit2/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/unit2/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/unit2/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/unit2/ios/Runner/Base.lproj/LaunchScreen.storyboard b/unit2/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f2e259c --- /dev/null +++ b/unit2/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unit2/ios/Runner/Base.lproj/Main.storyboard b/unit2/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/unit2/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unit2/ios/Runner/Info.plist b/unit2/ios/Runner/Info.plist new file mode 100644 index 0000000..48a1080 --- /dev/null +++ b/unit2/ios/Runner/Info.plist @@ -0,0 +1,51 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Unit2 + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + unit2 + 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 + + UIViewControllerBasedStatusBarAppearance + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + + diff --git a/unit2/ios/Runner/Runner-Bridging-Header.h b/unit2/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/unit2/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/unit2/lib/main.dart b/unit2/lib/main.dart new file mode 100644 index 0000000..e016029 --- /dev/null +++ b/unit2/lib/main.dart @@ -0,0 +1,115 @@ +import 'package:flutter/material.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + // This is the theme of your application. + // + // Try running your application with "flutter run". You'll see the + // application has a blue toolbar. Then, without quitting the app, try + // changing the primarySwatch below to Colors.green and then invoke + // "hot reload" (press "r" in the console where you ran "flutter run", + // or simply save your changes to "hot reload" in a Flutter IDE). + // Notice that the counter didn't reset back to zero; the application + // is not restarted. + primarySwatch: Colors.blue, + ), + home: const MyHomePage(title: 'Flutter Demo Home Page'), + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({super.key, required this.title}); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + int _counter = 0; + + void _incrementCounter() { + setState(() { + // This call to setState tells the Flutter framework that something has + // changed in this State, which causes it to rerun the build method below + // so that the display can reflect the updated values. If we changed + // _counter without calling setState(), then the build method would not be + // called again, and so nothing would appear to happen. + _counter++; + }); + } + + @override + Widget build(BuildContext context) { + // This method is rerun every time setState is called, for instance as done + // by the _incrementCounter method above. + // + // The Flutter framework has been optimized to make rerunning build methods + // fast, so that you can just rebuild anything that needs updating rather + // than having to individually change instances of widgets. + return Scaffold( + appBar: AppBar( + // Here we take the value from the MyHomePage object that was created by + // the App.build method, and use it to set our appbar title. + title: Text(widget.title), + ), + body: Center( + // Center is a layout widget. It takes a single child and positions it + // in the middle of the parent. + child: Column( + // Column is also a layout widget. It takes a list of children and + // arranges them vertically. By default, it sizes itself to fit its + // children horizontally, and tries to be as tall as its parent. + // + // Invoke "debug painting" (press "p" in the console, choose the + // "Toggle Debug Paint" action from the Flutter Inspector in Android + // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) + // to see the wireframe for each widget. + // + // Column has various properties to control how it sizes itself and + // how it positions its children. Here we use mainAxisAlignment to + // center the children vertically; the main axis here is the vertical + // axis because Columns are vertical (the cross axis would be + // horizontal). + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text( + 'You have pushed the button this many times:', + ), + Text( + '$_counter', + style: Theme.of(context).textTheme.headline4, + ), + ], + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: _incrementCounter, + tooltip: 'Increment', + child: const Icon(Icons.add), + ), // This trailing comma makes auto-formatting nicer for build methods. + ); + } +} diff --git a/unit2/linux/.gitignore b/unit2/linux/.gitignore new file mode 100644 index 0000000..d3896c9 --- /dev/null +++ b/unit2/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/unit2/linux/CMakeLists.txt b/unit2/linux/CMakeLists.txt new file mode 100644 index 0000000..f9949bc --- /dev/null +++ b/unit2/linux/CMakeLists.txt @@ -0,0 +1,138 @@ +# 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 "unit2") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "com.example.unit2") + +# 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) + +# 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/unit2/linux/flutter/CMakeLists.txt b/unit2/linux/flutter/CMakeLists.txt new file mode 100644 index 0000000..d5bd016 --- /dev/null +++ b/unit2/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/unit2/linux/flutter/generated_plugin_registrant.cc b/unit2/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000..e71a16d --- /dev/null +++ b/unit2/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,11 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + + +void fl_register_plugins(FlPluginRegistry* registry) { +} diff --git a/unit2/linux/flutter/generated_plugin_registrant.h b/unit2/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000..e0f0a47 --- /dev/null +++ b/unit2/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/unit2/linux/flutter/generated_plugins.cmake b/unit2/linux/flutter/generated_plugins.cmake new file mode 100644 index 0000000..2e1de87 --- /dev/null +++ b/unit2/linux/flutter/generated_plugins.cmake @@ -0,0 +1,23 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +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/unit2/linux/main.cc b/unit2/linux/main.cc new file mode 100644 index 0000000..e7c5c54 --- /dev/null +++ b/unit2/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/unit2/linux/my_application.cc b/unit2/linux/my_application.cc new file mode 100644 index 0000000..1cb3700 --- /dev/null +++ b/unit2/linux/my_application.cc @@ -0,0 +1,104 @@ +#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, "unit2"); + 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, "unit2"); + } + + 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 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_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/unit2/linux/my_application.h b/unit2/linux/my_application.h new file mode 100644 index 0000000..72271d5 --- /dev/null +++ b/unit2/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/unit2/macos/.gitignore b/unit2/macos/.gitignore new file mode 100644 index 0000000..746adbb --- /dev/null +++ b/unit2/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/unit2/macos/Flutter/Flutter-Debug.xcconfig b/unit2/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 0000000..c2efd0b --- /dev/null +++ b/unit2/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/unit2/macos/Flutter/Flutter-Release.xcconfig b/unit2/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 0000000..c2efd0b --- /dev/null +++ b/unit2/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/unit2/macos/Flutter/GeneratedPluginRegistrant.swift b/unit2/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 0000000..cccf817 --- /dev/null +++ b/unit2/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,10 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { +} diff --git a/unit2/macos/Runner.xcodeproj/project.pbxproj b/unit2/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..83583de --- /dev/null +++ b/unit2/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,572 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + 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 */ + 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 */ + 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 */ + 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 /* unit2.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "unit2.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 */ + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 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 */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* unit2.app */, + ); + 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 */ + 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 /* unit2.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 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 */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 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; + 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 */ + 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 */ + 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 */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + 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; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + 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.11; + 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; + 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; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + 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.11; + 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; + 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; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + 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.11; + 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 */ + 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/unit2/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/unit2/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/unit2/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/unit2/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/unit2/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..a007c96 --- /dev/null +++ b/unit2/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unit2/macos/Runner.xcworkspace/contents.xcworkspacedata b/unit2/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/unit2/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/unit2/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/unit2/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/unit2/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/unit2/macos/Runner/AppDelegate.swift b/unit2/macos/Runner/AppDelegate.swift new file mode 100644 index 0000000..d53ef64 --- /dev/null +++ b/unit2/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..a2ec33f --- /dev/null +++ b/unit2/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/unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..82b6f9d9a33e198f5747104729e1fcef999772a5 GIT binary patch literal 102994 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ⅅ8MglG&ER{i5lN315cO?EdHNLrg? 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 z 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 Pe7HEdAEg|=c=LY&YVNkY literal 0 HcmV?d00001 diff --git a/unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..13b35eba55c6dabc3aac36f33d859266c18fa0d0 GIT binary patch literal 5680 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 literal 0 HcmV?d00001 diff --git a/unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 0000000000000000000000000000000000000000..0a3f5fa40fb3d1e0710331a48de5d256da3f275d GIT binary patch literal 520 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 literal 0 HcmV?d00001 diff --git a/unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/unit2/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 0000000000000000000000000000000000000000..2f1632cfddf3d9dade342351e627a0a75609fb46 GIT binary patch literal 2218 zcmV;b2vzrqP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91K%fHv1ONa40RR91KmY&$07g+lumAuE6iGxuRCodHTWf3-RTMruyW6Fu zQYeUM04eX6D5c0FCjKKPrco1(K`<0SL=crI{PC3-^hZU0kQie$gh-5!7z6SH6Q0J% zqot*`H1q{R5fHFYS}dje@;kG=v$L0(yY0?wY2%*c?A&{2?!D*x?m71{of2gv!$5|C z3>qG_BW}7K_yUcT3A5C6QD<+{aq?x;MAUyAiJn#Jv8_zZtQ{P zTRzbL3U9!qVuZzS$xKU10KiW~Bgdcv1-!uAhQxf3a7q+dU6lj?yoO4Lq4TUN4}h{N z*fIM=SS8|C2$(T>w$`t@3Tka!(r!7W`x z-isCVgQD^mG-MJ;XtJuK3V{Vy72GQ83KRWsHU?e*wrhKk=ApIYeDqLi;JI1e zuvv}5^Dc=k7F7?nm3nIw$NVmU-+R>> zyqOR$-2SDpJ}Pt;^RkJytDVXNTsu|mI1`~G7yw`EJR?VkGfNdqK9^^8P`JdtTV&tX4CNcV4 z&N06nZa??Fw1AgQOUSE2AmPE@WO(Fvo`%m`cDgiv(fAeRA%3AGXUbsGw{7Q`cY;1BI#ac3iN$$Hw z0LT0;xc%=q)me?Y*$xI@GRAw?+}>=9D+KTk??-HJ4=A>`V&vKFS75@MKdSF1JTq{S zc1!^8?YA|t+uKigaq!sT;Z!&0F2=k7F0PIU;F$leJLaw2UI6FL^w}OG&!;+b%ya1c z1n+6-inU<0VM-Y_s5iTElq)ThyF?StVcebpGI znw#+zLx2@ah{$_2jn+@}(zJZ{+}_N9BM;z)0yr|gF-4=Iyu@hI*Lk=-A8f#bAzc9f z`Kd6K--x@t04swJVC3JK1cHY-Hq+=|PN-VO;?^_C#;coU6TDP7Bt`;{JTG;!+jj(` zw5cLQ-(Cz-Tlb`A^w7|R56Ce;Wmr0)$KWOUZ6ai0PhzPeHwdl0H(etP zUV`va_i0s-4#DkNM8lUlqI7>YQLf)(lz9Q3Uw`)nc(z3{m5ZE77Ul$V%m)E}3&8L0 z-XaU|eB~Is08eORPk;=<>!1w)Kf}FOVS2l&9~A+@R#koFJ$Czd%Y(ENTV&A~U(IPI z;UY+gf+&6ioZ=roly<0Yst8ck>(M=S?B-ys3mLdM&)ex!hbt+ol|T6CTS+Sc0jv(& z7ijdvFwBq;0a{%3GGwkDKTeG`b+lyj0jjS1OMkYnepCdoosNY`*zmBIo*981BU%%U z@~$z0V`OVtIbEx5pa|Tct|Lg#ZQf5OYMUMRD>Wdxm5SAqV2}3!ceE-M2 z@O~lQ0OiKQp}o9I;?uxCgYVV?FH|?Riri*U$Zi_`V2eiA>l zdSm6;SEm6#T+SpcE8Ro_f2AwxzI z44hfe^WE3!h@W3RDyA_H440cpmYkv*)6m1XazTqw%=E5Xv7^@^^T7Q2wxr+Z2kVYr + + + + + + + + + + + + + + + + + + + + + +